Java AES解密检测错误的密钥

时间:2012-07-20 10:15:54

标签: java android encryption aes padding

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

6 个答案:

答案 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模式下,这确实不需要。无论如何,这样你就可以避免对充气城堡的额外依赖。