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

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字节块中的数据,因此读取了每个字节,我们就可以关闭流。

接下来,我们创建一个512字节的内容。这将填充为528。如果我们现在仅读取一个字节,则剩下一些字节;如果现在关闭流,则它在给定异常的情况下崩溃。

现在,与ZipStreams结合使用时,此问题尤其成问题:在上一步中,将加密的内容压缩为ZipOutputStream,然后使用ZipInputStream读取。似乎ZipInputStream消耗的字节数与之前写入的字节数不同。

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

编辑3 ZipStream的详细说明:

在我们的应用程序中,我们读取了一堆加密的文本文件。因此,流的构造如下所示:
BufferedReader/Writer -> InputStreamReader/Writer -> ZipInputStream/Output -> CipherInputStream/Output -> Underlying File Stream

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

最佳答案

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

我建议将CipherInputStream包装在InputStream的外观实现中,该实现在委派BadPaddingException方法时会忽略close()。当然,如果您的用例需要对流的内容进行身份验证,或者如果可能发生某种定时的oracle攻击,则不要执行此操作。

07-24 18:31