Java 7 - > Java 8:AES导致异常:“BadPaddingException:给定最终块未正确填充”与BufferedReader& ZipStreams [英] Java 7 -> Java 8: AES Causes exception: "BadPaddingException: Given final block not properly padded" in conjunction with BufferedReader & ZipStreams

查看:160
本文介绍了Java 7 - > Java 8:AES导致异常:“BadPaddingException:给定最终块未正确填充”与BufferedReader& ZipStreams的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

 密码密码= Cipher.getInstance(AES); 
SecretKeySpec key = new SecretKeySpec(cipherKey,AES);

这个工作在java 7(1.7_45),但不再适用于Java 8(1.8_25)。我们将密码传递给 CipherInputStream ,并使用流读/写数据。实际例外发生在关闭



编辑:



快速查看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(清除文本长度:+ clearText.length); bb = bte =
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];


私有静态密码getCipher(int encryptMode)
throws NoSuchAlgorithmException,NoSuchPaddingException,InvalidKeyException {
密码加密= Cipher.getInstance(AES / ECB / PKCS5Padding );
SecretKeySpec key = new SecretKeySpec(SECRET_KEY,AES);
cipher.init(encryptMode,key);
返回密码;
}
}

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



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



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



似乎唯一的解决方法是在关闭时捕获BadPaddingException( )(?)从API的观点来看,对我来说似乎很奇怪,我无法关闭流,无论我读过的字节数如何。



编辑3 ZipStream阐述:



在我们的应用程序中,我们读了一堆加密的文本文件。因此,流的构造如下所示:

  BufferedReader / Writer  - > InputStreamReader / Writer  - > ZipInputStream / Output  - > CipherInputStream / Output  - >底层文件流

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

解决方案

Java 1.8 CipherInputStream 抛出一个 BadPaddingException if你不会完全消耗流,这可能是使用 ZipInputStream 的情况,因为以流式传输方式使用zip不需要在末尾读取zip索引的文件。



我建议将 CipherInputStream 包含在 InputStream 在委托 close()方法时忽略 BadPaddingException 。如果身份验证当然,您的用例需要流的内容,或者是否可能进行某种定时oracle攻击。


We instantiate the cipher with the following statement:

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

This works in java 7 (1.7_45) but no longer works in Java 8 (1.8_25). We pass the cipher to a CipherInputStream and use the streams to read/write data. The actual exception occurs during close.

EDIT:

A quick look at the JDK code shows that the BadPaddingException is rethrown and in 7 it was ignored:

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);
    }
  }

The question is how to avoid a BadPaddingException in the first place?

EDIT 2:

After doing some research and experimentation we came to the following test program:

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;
  }
}

First, the program writes 511 bytes which results in a 512 byte long "file content". We read exactly one byte. The CipherInputStream reads the data in a 512 byte chunk, so every byte is read and we can close the stream.

Next, we create a content of 512 bytes. This gets padded to 528. If we now read only one byte, we have some bytes left and if we close the stream now it crashes with the given exception.

Now this problem is especially problematic in conjunction with ZipStreams: The encrypted content is in a previous step zipped with a ZipOutputStream and read with a ZipInputStream. It seems, that the ZipInputStream does not consume the same amount of bytes as it has previously written.

It seems that the only solution is to catch the BadPaddingException in the close().(?) From an API view point it seems weird to me, that I cannot close a stream without exception regardless of the number of bytes I've read.

EDIT 3 ZipStream elaboration:

In our application we read a bunch of encrypted text files. So the consturction for the streams looks like this:

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

We read the content of the file with a "traditional" while (reader.readLine != null) loop until we reach EOF. After that we try to close the file. But sometimes this results in an exception in Java 8 - see above (-:. It seems, that the ZipOutputStream writes more bytes then the ZipInputStreams consumes, but I don't looked at the code yet.

解决方案

Java 1.8 CipherInputStream throws a BadPaddingException if you don't completely consume the stream. This may be the case when using ZipInputStream, since consuming a zip in streaming fashion doesn't need to read the zip index at the end of the file.

I recommend wrapping the CipherInputStream in a facade implementation of InputStream that ignores BadPaddingException when delegating the close() method. Don't do this if authentication of the contents of the stream is required for your use case, of course, or if some kind of timing oracle attack is possible.

这篇关于Java 7 - > Java 8:AES导致异常:“BadPaddingException:给定最终块未正确填充”与BufferedReader& ZipStreams的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆