我正在编写使用AES加密/解密文件的android应用程序。我希望能够检测是否指定了不正确的密码,因此不会导出匹配密钥以进行解密。 我使用256位密钥的AES / CBC / PKCS7Padding。 如果我做cipher.doFinal()我可以尝试/捕获BadPaddingException它告诉我有些东西是错的,可能键是不正确的。但是如果我使用CipherInputStream来读取加密文件,我就得不到关于填充正确性的反馈。因此,如果我故意指定不正确的密码,它会解密文件,然后报告一切正常,但解密文件是完全垃圾。 所以我的问题是如何在使用CipherInputStream时检测不良填充?
答案 0 :(得分:2)
尝试使用GCM模式(Java 7或Bouncy Castle提供程序)。填充的技巧是有时它在消息被改变后是正确的(一次大约256次)。 GCM模式将添加完整性保护,因此任何更改都将导致从BadPaddingException派生的异常。
但有一件事:你在使用GCM进行加密时应该预先设置(随机)随机数(实际上也是CBC模式的规则,但在CBC中使用非随机IV的含义不那么严重)。
请注意,您需要执行最终计算才能获得badpaddingexception,因此不要忘记关闭或结束基础流。这可能是你目前的问题。
[编辑]:这不是答案,但输入可用于生成更好的CipherInputStream
,请在此问题上查看我的other answer。
答案 1 :(得分:2)
为您的数据预先添加一些已知标头。首先解密它,如果它与您的预期不符,请停止并返回错误。
答案 2 :(得分:1)
我认为由于某种原因捕获了不良填充,这来自CipherInputStream
来源:
private int getMoreData() throws IOException {
if (done) return -1;
int readin = input.read(ibuffer);
if (readin == -1) {
done = true;
try {
obuffer = cipher.doFinal();
}
catch (IllegalBlockSizeException e) {obuffer = null;}
catch (BadPaddingException e) {obuffer = null;}
if (obuffer == null)
return -1;
else {
ostart = 0;
ofinish = obuffer.length;
return ofinish;
}
}
try {
obuffer = cipher.update(ibuffer, 0, readin);
} catch (IllegalStateException e) {obuffer = null;};
ostart = 0;
if (obuffer == null)
ofinish = 0;
else ofinish = obuffer.length;
return ofinish;
}
答案 3 :(得分:1)
这是CipherInputStream中getMoreData()方法的修改版本,它可能对遇到我的问题的人有用:
private int getMoreData() throws IOException {
if (done) return -1;
int readin = input.read(ibuffer);
if (readin == -1) {
done = true;
try {
obuffer = cipher.doFinal();
}
catch (IllegalBlockSizeException e) {
throw new IOException(e);
}
catch (BadPaddingException e) {
throw new IOException(e);
}
if (obuffer == null)
return -1;
else {
ostart = 0;
ofinish = obuffer.length;
return ofinish;
}
}
try {
obuffer = cipher.update(ibuffer, 0, readin);
} catch (IllegalStateException e) {obuffer = null;};
ostart = 0;
if (obuffer == null)
ofinish = 0;
else ofinish = obuffer.length;
return ofinish;
}
答案 4 :(得分:1)
我有同样的问题,如何知道用于加密的密钥是否与用于解密的密钥相同,因为在我的情况下我可以解密字符串但它返回了一些垃圾,我需要知道加密的字符串(它是随机的)以获得正确的值。
所以我所做的是;
解密加密的字符串。
使用正确的密钥再次加密字符串
解密以前的加密字符串。
如果原始解密密钥等于新解密密钥,则匹配。
Cipher c = Cipher.getInstance(algorithm);
c.init(Cipher.DECRYPT_MODE, keyspec, ivspec);
byte[] decValue = c.doFinal(encryptedData.getBytes());
decryptedValue = new String(decValue,"UTF-8");
//now i have the string decrypted in decryptedValue
byte[] encryptAgain = encrypt(decryptedValue);
String encryptAgaindecripted = new String(c.doFinal(encryptAgain),"UTF-8");
//if keys match then it uses the same key and string is valid
if (decryptedValue.equals(encryptAgaindecripted)){
//return valid
}
希望这有助于某人。
答案 5 :(得分:0)
我也碰到了这个。我有一个测试,在Java支持的AES / CBC / PKCS5Padding的一千次运行中可靠地失败了几次。
要修复,你可以按照上面的建议去做,并使用充气城堡。
然而,我做了一个不同的修复,只是在加密之前在纯文本中添加了一个md5内容哈希,我在解密时验证。只需将内容附加到md5哈希并解密,就可以获取md5哈希的前22个字符,并验证字符串的其余部分是否具有相同的哈希值,如果它没有返回明文(没有md5)则抛出异常hash)如果匹配的话。无论加密算法如何,这都可以。可能在GCM模式下,这确实不需要。无论如何,这样你就可以避免对充气城堡的额外依赖。