使用Java加密和解密:无法获得相同的输出

我正在尝试学习和测试java 1.6加密/解密API。 我想知道我做错了什么以及我在知识方面缺少什么。

在下面的代码中,我创建了两个密码:一个用于加密,另一个用于解密。 当我使用这些密码时,我使用不同的SecretKey初始化它们,但我仍然能够获得相同的值。 为什么是这样?

String algorithm = "DES"; SecretKeyFactory keyFactory = SecretKeyFactory.getInstance(algorithm); byte[] encBytes = "12345678".getBytes("UTF8"); byte[] decBytes = "56781234".getBytes("UTF8"); DESKeySpec keySpecEncrypt = new DESKeySpec(encBytes); DESKeySpec keySpecDecrypt = new DESKeySpec(decBytes); SecretKey keyEncrypt = keyFactory.generateSecret(keySpecEncrypt); SecretKey keyDecrypt = keyFactory.generateSecret(keySpecDecrypt); Cipher cipherEncrypt = Cipher.getInstance(algorithm); Cipher cipherDecrypt = Cipher.getInstance(algorithm); String input = "john doe"; cipherEncrypt.init(Cipher.ENCRYPT_MODE, keyEncrypt); byte[] inputBytes = cipherEncrypt.doFinal(input.getBytes()); System.out.println("inputBytes: " + new String(inputBytes)); cipherDecrypt.init(Cipher.DECRYPT_MODE, keyDecrypt); byte[] outputBytes = cipherDecrypt.doFinal(inputBytes); System.out.println("outputBytes: " + new String(outputBytes)); 

这是JDK doc的描述:

 DESKeySpec
 public DESKeySpec(byte [] key)
           抛出InvalidKeyException
使用密钥中的前8个字节作为DES密钥的密钥材料创建DESKeySpec对象。 
构成DES密钥的字节是密钥[0]和密钥[7]之间的字节。 

DESKeySpec仅使用byte []的前8个字节作为键。 因此,在您的示例中使用的实际键是相同的。

欢迎加密! 如上所述,DES是对称的,并且需要与解密相同的加密密钥。 该密钥需要是您正在使用的密码的正确位数。 对于那个56位的DES。 在你走得太远之前,这里有一些你可能想要考虑的事情:

  1. 您应该使用更强大的加密标准,如AES 。 现在可以破坏DES加密。
  2. 如果您想使用字符串作为键,那么您应该使用强大的哈希函数,如SHA-256对该键字符串。 然后根据加密密钥需要从该散列输出中获取尽可能多的位,128位足以支持AES。 你的钥匙串应该像你一样长。
  3. 最好使用块密码模式,每次都不会为同一输入生成相同的输出。 有关信息,请参阅分组密码操作模式,并查看ECB模式为何不良的可视化。

以下是使用PKCS#5填充在CBC模式下使用128位AES加密的工作示例:

 import java.security.MessageDigest; import java.security.SecureRandom; import javax.crypto.Cipher; import javax.crypto.spec.IvParameterSpec; import javax.crypto.spec.SecretKeySpec; public class EncryptDecrypt { public static void main(String[] args) throws Exception { // here are your inputs String keyString = "averylongtext!@$@#$#@$#*&(*&}{23432432432dsfsdf"; String input = "john doe"; // setup AES cipher in CBC mode with PKCS #5 padding Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding"); // setup an IV (initialization vector) that should be // randomly generated for each input that's encrypted byte[] iv = new byte[cipher.getBlockSize()]; new SecureRandom().nextBytes(iv); IvParameterSpec ivSpec = new IvParameterSpec(iv); // hash keyString with SHA-256 and crop the output to 128-bit for key MessageDigest digest = MessageDigest.getInstance("SHA-256"); digest.update(keyString.getBytes()); byte[] key = new byte[16]; System.arraycopy(digest.digest(), 0, key, 0, key.length); SecretKeySpec keySpec = new SecretKeySpec(key, "AES"); // encrypt cipher.init(Cipher.ENCRYPT_MODE, keySpec, ivSpec); byte[] encrypted = cipher.doFinal(input.getBytes("UTF-8")); System.out.println("encrypted: " + new String(encrypted)); // include the IV with the encrypted bytes for transport, you'll // need the same IV when decrypting (it's safe to send unencrypted) // decrypt cipher.init(Cipher.DECRYPT_MODE, keySpec, ivSpec); byte[] decrypted = cipher.doFinal(encrypted); System.out.println("decrypted: " + new String(decrypted, "UTF-8")); } } 

这是使用56位DES加密的一个工作示例。

 import javax.crypto.Cipher; import javax.crypto.SecretKey; import javax.crypto.spec.SecretKeySpec; public class CipherHelper { // Algorithm used private final static String ALGORITHM = "DES"; /** * Encrypt data * @param secretKey - a secret key used for encryption * @param data - data to encrypt * @return Encrypted data * @throws Exception */ public static String cipher(String secretKey, String data) throws Exception { // Key has to be of length 8 if (secretKey == null || secretKey.length() != 8) throw new Exception("Invalid key length - 8 bytes key needed!"); SecretKey key = new SecretKeySpec(secretKey.getBytes(), ALGORITHM); Cipher cipher = Cipher.getInstance(ALGORITHM); cipher.init(Cipher.ENCRYPT_MODE, key); return toHex(cipher.doFinal(data.getBytes())); } /** * Decrypt data * @param secretKey - a secret key used for decryption * @param data - data to decrypt * @return Decrypted data * @throws Exception */ public static String decipher(String secretKey, String data) throws Exception { // Key has to be of length 8 if (secretKey == null || secretKey.length() != 8) throw new Exception("Invalid key length - 8 bytes key needed!"); SecretKey key = new SecretKeySpec(secretKey.getBytes(), ALGORITHM); Cipher cipher = Cipher.getInstance(ALGORITHM); cipher.init(Cipher.DECRYPT_MODE, key); return new String(cipher.doFinal(toByte(data))); } // Helper methods private static byte[] toByte(String hexString) { int len = hexString.length()/2; byte[] result = new byte[len]; for (int i = 0; i < len; i++) result[i] = Integer.valueOf(hexString.substring(2*i, 2*i+2), 16).byteValue(); return result; } public static String toHex(byte[] stringBytes) { StringBuffer result = new StringBuffer(2*stringBytes.length); for (int i = 0; i < stringBytes.length; i++) { result.append(HEX.charAt((stringBytes[i]>>4)&0x0f)).append(HEX.charAt(stringBytes[i]&0x0f)); } return result.toString(); } private final static String HEX = "0123456789ABCDEF"; // Helper methods - end /** * Quick test * @param args */ public static void main(String[] args) { try { String secretKey = "01234567"; String data="test"; String encryptedData = cipher(secretKey, data); System.out.println("encryptedData: " + encryptedData); String decryptedData = decipher(secretKey, encryptedData); System.out.println("decryptedData: " + decryptedData); } catch (Exception e) { e.printStackTrace(); } } }