【问题标题】:AES File decrypting “given final block not properly padded”AES 文件解密“给定的最终块未正确填充”
【发布时间】:2015-10-07 02:00:51
【问题描述】:

我想使用 AES 加密然后解密文件。我已经阅读了很多关于错误"Given final block not properly padded" 的主题。但我没有找到适合我的解决方案。

很抱歉指定我的代码的语言,我不知道写语言 java

这是我的代码:

变量

// IV, secret, salt in the same time
private byte[] salt = { 'h', 'u', 'n', 'g', 'd', 'h', '9', '4' };
public byte[] iv;
public SecretKey secret;

createSecretKey

public void createSecretKey(String password){
    SecretKeyFactory factory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA1");
    KeySpec spec = new PBEKeySpec(password.toCharArray(), salt, 65536, 256);
    SecretKey tmp = factory.generateSecret(spec);
    secret = new SecretKeySpec(tmp.getEncoded(), "AES");
}

方法加密

public void encrypt(String inputFile){
    FileInputStream fis = new FileInputStream(inputFile);
    // Save file: inputFile.enc
    FileOutputStream fos = new FileOutputStream(inputFile + ".enc");

    Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
    cipher.init(Cipher.ENCRYPT_MODE, secret);

    AlgorithmParameters params = cipher.getParameters();
    // Gen Initialization Vector
    iv = (byte[]) ((IvParameterSpec) params
            .getParameterSpec(IvParameterSpec.class)).getIV();
    // read from file (plaint text)  -----> save with .enc
    int readByte;
    byte[] buffer = new byte[1024];
    while ((readByte = fis.read(buffer)) != -1) {
        fos.write(cipher.doFinal(buffer), 0, readByte);
    }
    fis.close();
    fos.flush();
    fos.close();
}

方法解密

public void decrypt(String inputFile){
    FileInputStream fis = new FileInputStream(inputFile);
    // Save file: filename.dec
    FileOutputStream fos = new FileOutputStream(inputFile.substring(0,
            inputFile.length() - 4) + ".dec");

    Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
    cipher.init(Cipher.DECRYPT_MODE, secret, new IvParameterSpec(iv));
    // Read from file encrypted  ---> .dec 
    int readByte;
    byte[] buffer = new byte[1024];
    while ((readByte = fis.read(buffer)) != -1) {
        fos.write(cipher.doFinal(buffer), 0, readByte);
    }
    fos.flush();
    fos.close();
    fis.close();
}

更新

解决方案:buffer 的编辑大小是 16 的倍数。使用 CipherInput/Output 读取/写入文件。

谢谢Artjom B.

【问题讨论】:

  • cipher.doFinal 是否总是返回 readByte 字节?我打赌没有。
  • issues.apache.org/jira/browse/PDFBOX-2469 显示了必须对 PDFBox 进行哪些更改。这可能适用于您,也可能不适用于您。一部分是关于根本没有加密的段,其他是关于调用什么以及如何进行异常处理。真正的痛苦。

标签: java encryption aes badpaddingexception


【解决方案1】:

AES 是一种分组密码,因此仅适用于 16 字节的分组。诸如 CBC 之类的操作模式使您能够将多个块链接在一起。诸如 PKCS#5 填充之类的填充使您能够通过将明文填充到块大小的下一个倍数来加密任意长度的明文。

问题是您要分别加密每 1024 个字节。由于 1024 分割了块大小,因此填充在加密之前添加了一个 full 块。因此,密文块的长度为 1040 字节。然后在解密期间,您只会读取 1024 缺少填充。 Java 尝试对其进行解密,然后尝试删除填充。如果填充格式不正确(因为它不存在),则会引发异常。

轻松修复

只需将用于解密的缓冲区增加到 1040 字节。

正确修复

不要在单独的块中对其进行加密,而是使用Cipher#update(byte[], int, int) 而不是Cipher.doFinal 来更新您读取的每个缓冲区的密文,或者使用CipherInputStream


其他安全注意事项:

您缺少一个随机 IV。没有它,攻击者可能仅通过观察密文就可以看到您在相同的密钥下加密了相同的明文。

您缺少密文身份验证。没有它,您将无法可靠地检测密文中的(恶意)更改,并可能使您的系统遭受攻击,例如填充 oracle 攻击。要么使用像 GCM 这样的认证模式,要么通过 HMAC 运行您创建的密文以创建认证标签并将其写入末尾。然后您可以在解密期间/之前验证标签。

【讨论】:

  • 是的,我用 1040 字节编辑并使用 CipherInputStream。这是工作。
  • 随机IV和密文认证很重要!
  • 据我所知,IV 是随机的,因为在这种情况下 JCE 会自动生成一个新的 IV。如果您打印出 IV,您可以看到...
  • @fhissen 这完全取决于 JCE 提供者。并非所有提供商都这样做。
  • @Artjom B. 很有趣,也很好。默认的 Sun/Oracle 提供程序似乎是这样做的。但是,最好的做法当然是显式生成 IV - 毫无疑问!
【解决方案2】:

您错误地假设加密数据的长度等于纯数据的长度,但加密的 AES 数据始终是 AES 块大小(16 字节)的倍数,并且可以有一个额外的完整填充块.

处理流加密的最有效方法是使用 JCE 的 CipherOutputStream 和 CipherInputStream (http://docs.oracle.com/javase/7/docs/api/javax/crypto/CipherInputStream.html)。这些类为您完成所有工作。

此外,请确保始终将新生成的 IV 保存在加密方法中,以便能够将其用于解密。

【讨论】:

    猜你喜欢
    • 2015-05-22
    • 2015-03-31
    • 1970-01-01
    • 1970-01-01
    • 2017-03-17
    • 2014-07-26
    • 1970-01-01
    • 2014-01-22
    • 1970-01-01
    相关资源
    最近更新 更多