Java 7 - > Java 8:AES导致异常:"BadPaddingException:给定最终块未正确填充"与BufferedReader和ZipStreams一起使用

mor*_*s05 9 java encryption aes java-8

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

Cipher cipher = Cipher.getInstance("AES");
SecretKeySpec key = new SecretKeySpec(cipherKey, "AES");
Run Code Online (Sandbox Code Playgroud)

这适用于java 7(1.7_45),但不再适用于Java 8(1.8_25).我们传递cipher给a CipherInputStream并使用流来读/写数据.实际的异常发生在close.

编辑:

快速浏览一下JDK代码就会发现BadPaddingException被重新抛出,并且在7中它被忽略了:

JDK 7:CipherInputStream.close:

 try {
  this.cipher.doFinal();
} catch (BadPaddingException var2) {
  ;
} catch (IllegalBlockSizeException var3) {
  ;
}
Run Code Online (Sandbox Code Playgroud)

JDK 8:CipherInputStream.close:

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

问题是如何首先避免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;
  }
}
Run Code Online (Sandbox Code Playgroud)

首先,程序写入511个字节,这导致512字节长的"文件内容".我们只读了一个字节.CipherInputStream读取512字节块中的数据,因此每个字节都被读取,我们可以关闭流.

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

现在这个问题与ZipStreams一起特别成问题:加密内容在前一步中使用ZipOutputStream压缩并使用ZipInputStream读取.看起来,ZipInputStream不会消耗与之前编写的字节数相同的字节数.

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

编辑3 ZipStream详细说明:

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

BufferedReader/Writer -> InputStreamReader/Writer -> ZipInputStream/Output -> CipherInputStream/Output -> Underlying File Stream
Run Code Online (Sandbox Code Playgroud)

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

art*_*tol 6

看起来你已经点击了https://bugs.openjdk.java.net/browse/JDK-8061619.行为改变有充分的理由(参见http://blog.philippheckel.com/2014/03/01/cipherinputstream-for-aead-modes-is-broken-in-jdk7-gcm/)

您应该在密码实例中明确指定填充(例如AES/GCM/NoPadding,但检查您的应用程序的适用性.错误的填充是SSL的一些攻击的背后)


Bar*_*lly 5

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

我建议在委托方法时包装CipherInputStreamInputStream忽略的Facade实现中.如果您的用例需要对流内容进行身份验证,或者如果可能进行某种定时oracle攻击,请不要这样做.BadPaddingExceptionclose()