【发布时间】:2012-10-09 00:34:46
【问题描述】:
我已经花了 3 天的时间完成一项学校作业,今天终于完成了,没有错误并且工作正常!除了,我在 Java 1.7 上测试它,而学校服务器(教授将在其中编译它)运行 1.6。所以,我在 1.6 上测试了我的代码,想要覆盖我的所有基础,我在解密时得到一个BadPaddingException。
[编辑] 警告:此代码不遵循常见的安全实践,不应在生产代码中使用。
最初,我有这个,它在 1.7 上运行良好(对不起,很多代码......所有相关......):
public static String aes128(String key, String data, final int direction) {
SecureRandom rand = new SecureRandom(key.getBytes());
byte[] randBytes = new byte[16];
rand.nextBytes(randBytes);
SecretKey encKey = new SecretKeySpec(randBytes, "AES");
Cipher cipher = null;
try {
cipher = Cipher.getInstance("AES");
cipher.init((direction == ENCRYPT ? Cipher.ENCRYPT_MODE : Cipher.DECRYPT_MODE), encKey);
} catch (InvalidKeyException e) {
return null;
} catch (NoSuchPaddingException e) {
return null;
} catch (NoSuchAlgorithmException e) {
return null;
}
try {
if (direction == ENCRYPT) {
byte[] encVal = cipher.doFinal(data.getBytes());
String encryptedValue = Base64.encode(encVal);
return encryptedValue;
} else {
byte[] dataBytes = Base64.decode(data);
byte[] encVal = cipher.doFinal(dataBytes);
return new String(encVal);
}
} catch (NullPointerException e) {
return null;
} catch (BadPaddingException e) {
return null;
} catch (IllegalBlockSizeException e) {
return null;
}
}
但是,我的 BadPaddingException catch 块在解密时执行:
javax.crypto.BadPaddingException: Given final block not properly padded
at com.sun.crypto.provider.SunJCE_f.b(DashoA13*..)
at com.sun.crypto.provider.SunJCE_f.b(DashoA13*..)
at com.sun.crypto.provider.AESCipher.engineDoFinal(DashoA13*..)
at javax.crypto.Cipher.doFinal(DashoA13*..)
at CipherUtils.aes128(CipherUtils.java:112)
at CipherUtils.decryptFile(CipherUtils.java:44)
at decryptFile.main(decryptFile.java:21)
这是我试图解决的问题(基本上,我自己添加了所有的填充/取消填充,并使用了NoPadding):
public static String aes128(String key, String data, final int direction) {
// PADCHAR = (char)0x10 as String
while (key.length() % 16 > 0)
key = key + PADCHAR; // Added this loop
SecureRandom rand = new SecureRandom(key.getBytes());
byte[] randBytes = new byte[16];
rand.nextBytes(randBytes);
SecretKey encKey = new SecretKeySpec(randBytes, "AES");
AlgorithmParameterSpec paramSpec = new IvParameterSpec(key.getBytes()); // Created this
Cipher cipher = null;
try {
cipher = Cipher.getInstance("AES/CBC/NoPadding"); // Added CBC/NoPadding
cipher.init((direction == ENCRYPT ? Cipher.ENCRYPT_MODE : Cipher.DECRYPT_MODE), encKey, paramSpec); // Added paramSpec
} catch (InvalidKeyException e) {
return null;
} catch (NoSuchPaddingException e) {
return null;
} catch (NoSuchAlgorithmException e) {
return null;
} catch (InvalidAlgorithmParameterException e) {
return null; // Added this catch{}
}
try {
if (direction == ENCRYPT) {
while (data.length() % 16 > 0)
data = data + PADCHAR; // Added this loop
byte[] encVal = cipher.doFinal(data.getBytes());
String encryptedValue = Base64.encode(encVal);
return encryptedValue;
} else {
byte[] dataBytes = Base64.decode(data);
byte[] encVal = cipher.doFinal(dataBytes);
return new String(encVal);
}
} catch (NullPointerException e) {
return null;
} catch (BadPaddingException e) {
return null;
} catch (IllegalBlockSizeException e) {
return null;
}
}
当我使用这个时,我只是进进出出:
Out: u¢;èÉ÷JRLòB±J°N°[9cRÐ{ªv=]I¯¿©:
´RLA©êí;R([¶Ü9¸ßv&%®µ^#û|Bá (80)
Unpadded: u¢;èÉ÷JRLòB±J°N°[9cRÐ{ªv=]I¯¿©:
´RLA©êí;R([¶Ü9¸ßv&%®µ^#û|Bá (79)
另外值得注意的是,1.6 和 1.7 产生不同的加密字符串。
例如,在 1.7 上,使用密钥 hi 加密 xy(包括 SHA-1 哈希)会产生:
XLUVZBIJv1n/FV2MzaBK3FLPQRCQF2FY+ghyajdqCGsggAN4aac8bfwscrLaQT7BMHJgfnjJLn+/rwGv0UEW+dbRIMQkNAwkGeSjda3aEpk=
在 1.6 上,同样的事情会产生:
nqeahRnA0IuRn7HXUD1JnkhWB5uq/Ng+srUBYE3ycGHDC1QB6Xo7cPU6aEJxH7NKqe3kRN3rT/Ctl/OrhqVkyDDThbkY8LLP39ocC3oP/JE=
没想到作业要花这么长时间,所以我的时间已经不多了,确实需要今晚完成。但是,如果到那时还没有答案,我会就此给我的老师留个便条。这似乎是 1.7 中修复的一些问题......虽然希望可以通过我的代码中的正确添加/修复来解决。
非常感谢大家的时间!
【问题讨论】:
-
@Eric 我不相信这行得通。我没有过多地使用 Java 加密,但乍一看,您似乎每次都使用不同的密钥(感谢
SecureRandom),因此您不可能得到相同的结果。这也意味着你不可能让它正确解密,即使你解决了填充问题。 -
作业描述对我来说意义不大。你能发布一个链接吗?拉伸密钥很好(就像@par 所描述的那样),但它必须是确定性的(即:它不能是随机的)所以你每次都得到相同的密钥。密码的随机部分是IV,它用于ECB 以外的操作模式。使用它是为了在加密相同的明文时不会得到相同的密文。这也必须与加密数据一起存储,以便用于解密。
-
哦,顺便说一句,如果您需要使用已知种子的可重复随机数字,请使用 java.util.Random,而不是 SecureRandom。这将解决您的关键问题。
-
@TimBender 因为如果您在 ECB 以外的任何操作模式下使用分组密码(不要使用 ECB!),您需要一个初始化向量。该 IV,无论它是如何生成的,都必须存储在某个地方以进行解密。
标签: java cryptography aes