3

我正在编写对文件进行 AES 加密/解密的 android 应用程序。我希望能够检测是否指定了不正确的密码,因此不匹配的密钥被派生用于解密。我正在使用带有 256 位密钥的 AES/CBC/PKCS7Padding。如果我执行 cipher.doFinal() 我可以尝试/捕获 BadPaddingException,它会告诉我有问题并且可能密钥不正确。但是如果我使用 CipherInputStream 来读取加密文件,我不会得到关于填充正确性的反馈。因此,如果我故意指定错误的密码,它会解密文件,然后报告一切正常,但解密的文件完全是垃圾。所以我的问题是如何在使用 CipherInputStream 时检测到错误的填充?

4

6 回答 6

3

将一些已知的标头添加到您的数据中。首先解密它,如果它不符合您的预期,停止并返回错误。

于 2012-07-21T04:22:32.980 回答
2

尝试改用 GCM 模式(Java 7 或 Bouncy Castle 提供程序)。填充的技巧是,有时在消息被更改后它是正确的(大约 256 次一次)。GCM 模式将添加完整性保护,因此任何更改都将导致从 BadPaddingException 派生的异常。

不过有一件事:在使用 GCM 加密时,您应该预先添加一个(随机)随机数(实际上也是 CBC 模式的规则,但在 CBC 中使用非随机 IV 的影响不那么严重)。

请注意,您需要执行最终计算以获得 badpaddingexception,因此不要忘记关闭或结束底层流。这可能是您当前的问题。

[编辑]:这不是答案,但输入可用于生成更好的CipherInputStream,请参阅我关于此问题的其他答案。

于 2012-07-20T10:53:47.830 回答
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;
}
于 2012-07-21T11:31:06.667 回答
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;
}
于 2012-07-23T08:28:51.573 回答
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
 }

希望这可以帮助某人。

于 2014-09-11T17:44:25.233 回答
0

我也遇到了这个。我有一个测试,在使用 Java 支持的 AES/CBC/PKCS5Padding 的一千次运行中可靠地失败了几次。

要修复,您可以按照上面的建议进行操作并使用充气城堡。

但是,我做了一个不同的修复,只是在加密之前向纯文本添加了一个 md5 内容哈希,我在解密时验证了这一点。只需将内容附加到 md5 散列并在解密时获取 md5 散列的前 22 个字符并验证字符串的其余部分是否具有相同的散列,如果没有则抛出异常或返回明文(没有 md5 散列)如果匹配。无论加密算法如何,这都将起作用。可能在 GCM 模式下,这确实是不需要的。无论如何,这样你可以避免对充气城堡的额外依赖。

于 2014-09-09T11:29:02.917 回答