Java 7 -> Java 8 : AES Causes exception: "BadPaddingException: Given final block not properly padded" in conjunction with BufferedReader & ZipStreams

标签 java encryption aes java-8

我们使用以下语句实例化密码:

Cipher cipher = Cipher.getInstance("AES");
SecretKeySpec key = new SecretKeySpec(cipherKey, "AES");

这在 Java 7 (1.7_45) 中有效,但在 Java 8 (1.8_25) 中不再有效。我们将 cipher 传递给 CipherInputStream 并使用流来读取/写入数据。实际异常发生在 close 期间。

编辑:

快速查看 JDK 代码会发现 BadPaddingException 被重新抛出,在 7 中它被忽略了:

JDK 7:CipherInputStream.close:

 try {
  this.cipher.doFinal();
} catch (BadPaddingException var2) {
  ;
} catch (IllegalBlockSizeException var3) {
  ;
}

JDK 8:CipherInputStream.close:

try {
    if(!this.done) {
      this.cipher.doFinal();
    }
  } catch (IllegalBlockSizeException | BadPaddingException var2) {
    if(this.read) {
      throw new IOException(var2);
    }
  }

问题是如何首先避免 BadPaddingException?

编辑 2:

经过一些研究和实验,我们得出了以下测试程序:

public final class CipherSpike2 {

  private static final byte[] SECRET_KEY = new byte[]{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16};

  public static void main(String[] args)
  throws NoSuchPaddingException, NoSuchAlgorithmException, InvalidKeyException, IOException {
    encryptDecrypt(511);
    encryptDecrypt(512);
  }

  private static void encryptDecrypt(int i)
  throws NoSuchAlgorithmException, NoSuchPaddingException, InvalidKeyException, IOException {

    byte[] clearText = generateClearText(i);
    System.out.println("Clear text length: " + clearText.length);

    ByteArrayOutputStream bos = new ByteArrayOutputStream();
    CipherOutputStream cos = new CipherOutputStream(bos, getCipher(Cipher.ENCRYPT_MODE));
    cos.write(clearText);
    cos.close();

    final byte[] content = bos.toByteArray();
    System.out.println("written bytes: " + content.length);

    CipherInputStream
    inputStream =
    new CipherInputStream(new ByteArrayInputStream(content), getCipher(Cipher.DECRYPT_MODE));

    inputStream.read();
    inputStream.close();
 }

 private static byte[] generateClearText(int size) {
    return new byte[size];
  }

  private static Cipher getCipher(int encryptMode)
  throws NoSuchAlgorithmException, NoSuchPaddingException, InvalidKeyException {
    Cipher cipher = Cipher.getInstance("AES/ECB/PKCS5Padding");
    SecretKeySpec key = new SecretKeySpec(SECRET_KEY, "AES");
    cipher.init(encryptMode, key);
    return cipher;
  }
}

首先,程序写入 511 个字节,结果是一个 512 字节长的“文件内容”。 我们只读了一个字节。 CipherInputStream 读取 512 字节 block 中的数据,因此每个字节都被读取,我们可以关闭流。

接下来,我们创建一个 512 字节的内容。这被填充为 528。如果我们现在只读取一个字节,我们还剩下一些字节,如果我们现在关闭流,它会崩溃并出现给定的异常。

现在这个问题在与 ZipStreams 结合时尤其成问题:加密内容在前面的步骤中用 ZipOutputStream 压缩并用 ZipInputStream 读取。看起来,ZipInputStream 消耗的字节数与之前写入的字节数不同。

似乎唯一的解决方案是在 close() 中捕获 BadPaddingException。(?)从 API 的角度来看,这对我来说似乎很奇怪,无论我的字节数是多少,我都无法无一异常(exception)地关闭流'已阅读。

编辑 3 ZipStream 阐述:

在我们的应用程序中,我们读取了一堆加密的文本文件。所以流的构造看起来像这样:

BufferedReader/Writer -> InputStreamReader/Writer -> ZipInputStream/Output -> CipherInputStream/Output -> Underlying File Stream

我们使用“传统的”while (reader.readLine != null) 循环读取文件内容,直到到达 EOF。之后我们尝试关闭文件。但有时这会导致 Java 8 出现异常 - 见上文(-:。似乎 ZipOutputStream 写入的字节数比 ZipInputStreams 消耗的字节数更多,但我还没有查看代码。

最佳答案

如果您没有完全使用流,Java 1.8 CipherInputStream 将抛出一个 BadPaddingException。使用 ZipInputStream 时可能就是这种情况,因为以流方式使用 zip 不需要读取文件末尾的 zip 索引。

我建议将 CipherInputStream 包装在 InputStream 的外观实现中,在委托(delegate) close() 时忽略 BadPaddingException > 方法。当然,如果您的用例需要对流的内容进行身份验证,或者可能发生某种定时预言机攻击,则不要这样做。

关于Java 7 -> Java 8 : AES Causes exception: "BadPaddingException: Given final block not properly padded" in conjunction with BufferedReader & ZipStreams,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/27124931/

相关文章:

java - 如何检查 EL 中的 boolean 条件?

Java AES 解密导致 BadPaddingException

javascript - 为什么java无法解密CryptoJS加密数据?

java - bouncycaSTLe 加密数据从 midlet 发送到 servlet

c# - 在 C# 中加密并在 Flex 中解密

aes - crypto-js 从十六进制解密

C# AES 加密行为异常

java - 如何才能完全禁用Spring Boot数据库相关配置

java - 第 i 个元素的值更改会影响 ArrayList 第 0 个元素的值

java - Spring 工具套件 : Error Importing Spring Getting Started Content