【问题标题】:BadPaddingException: pad block corruptedBadPaddingException:垫块损坏
【发布时间】:2010-10-26 12:12:45
【问题描述】:

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

javax.crypto.BadPaddingException:垫块损坏
在 org.bouncycastle.jce.provider.JCEBlockCipher.engineDoFinal(未知来源)
在 javax.crypto.Cipher.doFinal(DashoA13*..)
在 AESFileDecrypter.decrypt(AESFileDecrypter.java:57)

当网络服务器调用doFinal(inpbytes) 方法获取第一个字节[] 时。我猜这是密钥或IV的问题。我的文件系统上有加密文件用于测试。下面的代码有什么明显错误的地方吗?

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

【问题讨论】:

    标签: java exception encryption


    【解决方案1】:

    我以前遇到过这个问题。

    当我写一些代码来做这样的加密和解密时:

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

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

    【讨论】:

      【解决方案2】:

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

      【讨论】:

        【解决方案3】:

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

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

        如果做不到这一点,那么作为调试的第一步,我建议这两者中的哪一个对你来说更容易:

        • 在没有填充的 ECB 模式下解密,看看你得到了什么。看看这带回来的第一个数据块。如果您可以将其与您的 IV 字节进行异或运算并获得预期的解密数据,那么您就知道您的密钥是正常的。
        • 在 base 64 编码之前从 C# 中转储出实际的密钥字节,在解码和检查之后从 Java 中转储它们是否相同。

        我记得,C# 有无符号字节(而 Java 有符号),所以在某些地方,字节符号可能会出现微妙的问题。

        【讨论】:

        • 有人告诉我的另一条评论是,一个主要问题是在每个块上调用“doFinal()”(它应该只执行一次)。有人建议我要么将 doFinal 更改为更新,然后只在最后执行 doFinal。或者使用密码流
        • 啊,是的,对不起,我显然没有仔细阅读您的代码。如果你对每个块都调用 doFinal(),那就错了。
        • 谢谢 Niel...我添加了更新的代码。我现在试图从 sendBytes 方法(位于 Web 服务器中)调用更新方法。在我循环输入流之后,调用 doFinal() 方法。在 doFinal() 方法上仍然得到相同的异常,因此它成功地循环通过文件流,对每个字节 [] 执行 update()。有什么建议吗?
        • 为了获得线索,我会尝试在最后一个块上调用 update() 而不是 doFinal()。我认为这应该解密并给你填充 - 例如如果有 5 个字节的填充,最后 5 个字节应该是 05,05,05,05,05。也许 C# 将其以其他格式 - 调用 update() 希望能告诉你。
        • P.S.在最后一次通过时,您只调用 doFinal() - 您不会对同一数据调用 update() 和 doFinal()。
        【解决方案4】:

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

        【讨论】:

        • 感谢您的回复。 AES 用于 256 位,而 C# 中的密码使用 4k 数据块。 thurn 每次调用 doFinal() 时我都使用相同的大小
        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2016-11-28
        • 1970-01-01
        • 2015-08-04
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多