Java BadPaddingException:填充块损坏

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/868776/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-11 20:27:02  来源:igfitidea点击:

BadPaddingException: pad block corrupted

javaexceptionencryption

提问by

I am trying to decrypt a file in Java which was encrypted in C# using Rijndael/CBC/PKCS7. I keep getting the following exception:

我正在尝试使用 Rijndael/CBC/PKCS7 解密在 C# 中加密的 Java 文件。我不断收到以下异常:

javax.crypto.BadPaddingException: pad block corrupted
at org.bouncycastle.jce.provider.JCEBlockCipher.engineDoFinal(Unknown Source)
at javax.crypto.Cipher.doFinal(DashoA13*..)
at AESFileDecrypter.decrypt(AESFileDecrypter.java:57)

javax.crypto.BadPaddingException:填充块
在 org.bouncycastle.jce.provider.JCEBlockCipher.engineDoFinal(Unknown Source)
at javax.crypto.Cipher.doFinal(DashoA13*..)
at AESFileDecrypter.decrypt(AESFileDecrypter.java:57)损坏

when the doFinal(inpbytes)method is called by the web server for the first byte[]. I am guessing this is a problem with the key or IV. I have the encrypted files on my file system for testing. Is there anything that anyone can see glaringly wrong with my code below?

doFinal(inpbytes)Web 服务器为第一个字节 [] 调用该方法时。我猜这是密钥或 IV 的问题。我的文件系统上有加密文件用于测试。任何人都可以看到我下面的代码有什么明显的错误吗?

***keyStr is base64 encoded

***keyStr 是 base64 编码的

public AESFileDecrypter(String keyStr){
    try {
            Security.addProvider(new BouncyCastleProvider());   
            convertIvParameter();
            key = new sun.misc.BASE64Decoder().decodeBuffer(keyStr);

            //use the passed in Base64 decoded key to create a key object
            decryptKey = new SecretKeySpec(key, "AES");

            //specify the encryption algorithm
            decryptCipher = Cipher.getInstance("AES/CBC/PKCS7Padding", "BC");

            //make a parameter object for the initialization vector(IV)             
            IvParameterSpec ivs = new IvParameterSpec(_defaultIv);

            //initialize the decrypter to the correct mode, key used and IV
            decryptCipher.init(Cipher.DECRYPT_MODE, decryptKey, ivs);    
        } 
     catch (Exception e) {
             e.printStackTrace();
     } 
}

public void convertIvParameter() {

   int[] iv = new int[] {11, 190, 165, 33, 68, 88, 11, 200, 245, 35, 68, 23, 60, 24, 223, 67};

   _defaultIv = new byte[16];

   for(int x = 0; x < _defaultIv.length; x++) {
      _defaultIv[x] = (byte)iv[x];
   }
}

public void decryptUpdate(byte[] inpBytes) throws Exception {
   //decrypt the byte passed in from the web server
   decryptCipher.update(inpBytes);  
}

public byte[] decryptFinal() throws Exception {
   //decrypt the byte passed in from the web server
   return decryptCipher.doFinal();
}

//sends bytes to the client for diaply
private void sendBytes(FileInputStream fis, OutputStream os)throws Exception {
    //set the buffer size to send 4k segments of data
aesFileDecrypter = new AESFileDecrypter(<Insert Key string here>);

    byte[] buffer = new byte[4096];
    int bytes = 0, totalBytes = fis.available();

    //while there is still data to be sent keep looping and write the data
    //to the output stream as the buffer is filled
    try {
       while ((bytes = fis.read(buffer)) != -1) {   
          aesFileDecrypter.decryptUpdate(buffer);
          //os.write(buffer, 0, bytes);
       }

       os.write(aesFileDecrypter.decryptFinal(), 0, totalBytes);
   }
   catch(Exception e) {
      e.printStackTrace();
   }
}

回答by Daniel H.

As far as I know AES is based on Rijndael, but the specification is not exactly the same. I would suggest to check the key and block size you are using to cipher in C# and the sizes being use in Java. (.Net differences between Rijndael and AES).

据我所知,AES 是基于 Rijndael 的,但规范并不完全相同。我建议检查您在 C# 中用于加密的密钥和块大小以及在 Java 中使用的大小。(Rijndael 和 AES 之间的 .Net 差异)。

回答by Neil Coffey

Firstly, just to be clear, from comments below, you shouldn't call doFinal() on every block, because doFinal() expects any padding at the end, which obviouslly won't be there in intermediate blocks. Either (a) call update() on intermediate data, then doFinal() at the end, or (b) just arrange to have all your data in one buffer or byte array, and call doFinal() once on the whole job lot.

首先,为了清楚起见,从下面的评论中,您不应该在每个块上调用 doFinal(),因为 doFinal() 期望在末尾有任何填充,这显然不会出现在中间块中。(a) 对中间数据调用 update(),然后在最后调用 doFinal(),或者 (b) 将所有数据放在一个缓冲区或字节数组中,然后对整个工作批次调用一次 doFinal()。

It's not clear from the code you posted that that's actually what you're doing, but it should be mentioned just in case.

从您发布的代码中并不清楚这实际上是您在做什么,但应提及以防万一。

Failing that, then as a first step to debugging, I'd suggest whichever of these two is easier for you:

如果失败,那么作为调试的第一步,我建议您使用这两个中的任何一个对您来说更容易:

  • Decrypting in ECB mode with no padding and seeing what you get. Look at the first block of data this brings back. If you can XOR this with your IV bytes and get the expected decrypted data, you know your key is OK.
  • Dumping out the actual key bytes from C# before base 64 encoding and Java after decoding and checking they are the same.
  • 在没有填充的 ECB 模式下解密,看看你得到了什么。看看这带来的第一个数据块。如果您可以将它与您的 IV 字节进行异或并获得预期的解密数据,您就知道您的密钥没问题。
  • 在解码和检查它们是否相同之后,在 base 64 编码和 Java 之前从 C# 中转储实际的关键字节。

As I recall, C# has unsigned bytes (whereas Java signed) so there are a few places where there's room for things subtly going wrong with byte signedness.

我记得,C# 有无符号字节(而 Java 有符号字节),所以在一些地方,字节符号性有一些微妙的错误。

回答by Neil Coffey

The doFinal() was the undoing of the code above, and I ended up just using cipher streams instead of the update/doFinal approach. This way I could use the FileInputStream and my cipher as parameters for the CipherInputStream, and then pass the output to the web browser through an OutputStream. Breaking the update and doFinal out into their own method calls made the task much more difficult and both methods were deleted from the decrypter class (leaving a single while loop that read in chunks of data and output it to the browser). The Bouncy Castle Provider was also not needed in this case and PKCS5Padding was enough, which was given by the SunJCE.

doFinal() 是上面代码的撤销,我最终只使用密码流而不是 update/doFinal 方法。这样我就可以使用 FileInputStream 和我的密码作为 CipherInputStream 的参数,然后通过 OutputStream 将输出传递给 Web 浏览器。将 update 和 doFinal 分解为它们自己的方法调用使任务变得更加困难,并且这两种方法都从解密器类中删除(留下一个 while 循环,读取数据块并将其输出到浏览器)。在这种情况下也不需要 Bouncy Castle Provider,而 PKCS5Padding 就足够了,这是由 SunJCE 提供的。

回答by sodarfish

I have encountered this problem before.

我以前遇到过这个问题。

When I wrote some code to do encryption and decryption like this:

当我编写一些代码来进行加密和解密时:

    Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
    cipher.init(Cipher.ENCRYPT_MODE, new SecretKeySpec(sec, "AES"),new IvParameterSpec(new byte[cipher.getBlockSize()]));
    byte[] encode = cipher.doFinal(data);

    cipher.init(Cipher.DECRYPT_MODE, new SecretKeySpec(sec, "AES"), new IvParameterSpec(new byte[cipher.getBlockSize()]));
    byte[] decode = cipher.doFinal(encode);

I forgot the first IvParameterSpec(new byte[cipher.getBlockSize()])when encrypting data, then I got an exception "pad block corrupted", so maybe you should check you encryption code.

IvParameterSpec(new byte[cipher.getBlockSize()])在加密数据时忘记了第一个,然后我得到了一个异常“填充块损坏”,所以也许你应该检查你的加密代码。