【问题标题】:BlackBerry Decryption - BadPaddingException黑莓解密 - BadPaddingException
【发布时间】:2013-01-17 16:41:10
【问题描述】:

我已成功以 AES 格式加密 BlackBerry 中的数据。为了验证我的结果,我正在尝试使用以下方法在 BlackBerry 中实现解密:

 private static byte[] decrypt( byte[] keyData, byte[] ciphertext )throws CryptoException, IOException
{
   // First, create the AESKey again.
   AESKey key = new AESKey( keyData );

   // Now, create the decryptor engine.
   AESDecryptorEngine engine = new AESDecryptorEngine( key );
   // Since we cannot guarantee that the data will be of an equal block length
   // we want to use a padding engine (PKCS5 in this case).
   PKCS5UnformatterEngine uengine = new PKCS5UnformatterEngine( engine );

   // Create the BlockDecryptor to hide the decryption details away.
   ByteArrayInputStream input = new ByteArrayInputStream( ciphertext );
   BlockDecryptor decryptor = new BlockDecryptor( uengine, input );

   // Now, read in the data. Remember that the last 20 bytes represent
   // the SHA1 hash of the decrypted data.
   byte[] temp = new byte[ 100 ];
   DataBuffer buffer = new DataBuffer();

   for( ;; ) {
       int bytesRead = decryptor.read( temp );
       buffer.write( temp, 0, bytesRead );

       if( bytesRead < 100 ) {
           // We ran out of data.
           break;
       }
   }

   byte[] plaintextAndHash = buffer.getArray();
   int plaintextLength = plaintextAndHash.length - SHA1Digest.DIGEST_LENGTH;
   byte[] plaintext = new byte[ plaintextLength ];
   byte[] hash = new byte[ SHA1Digest.DIGEST_LENGTH ];

   System.arraycopy( plaintextAndHash, 0, plaintext, 0, plaintextLength );
   System.arraycopy( plaintextAndHash, plaintextLength, hash, 0,
       SHA1Digest.DIGEST_LENGTH );

   // Now, hash the plaintext and compare against the hash
   // that we found in the decrypted data.
   SHA1Digest digest = new SHA1Digest();
   digest.update( plaintext );
   byte[] hash2 = digest.getDigest();

   if( !Arrays.equals( hash, hash2 )) {
       throw new RuntimeException();
   }

   return plaintext;
}

我在以下行抛出异常“BadPaddingException”

int bytesRead = decryptor.read( temp );

有人可以帮忙吗。

【问题讨论】:

  • 你能详细说明一下吗?我应该如何实现这个?这是导致此异常的原因吗?这是你的建议:byte[] raw = IOUtilities.streamToBytes(input) 然后使用这个数组来获取十六进制值?
  • 不,不是。忘了它。我将发布一个更详细的答案。

标签: blackberry java-me padding encryption


【解决方案1】:

我认为问题可能出在这个块中:

    for( ;; ) {
       int bytesRead = decryptor.read( temp );
       buffer.write( temp, 0, bytesRead );

       if( bytesRead < 100 ) {
           // We ran out of data.
           break;
       }
   }

read 返回-1 时,您也在将其写入缓冲区。而且退出条件也是错误的。将其与 CryptoDemo 示例项目中的块进行比较:

    for( ;; ) {
        int bytesRead = decryptor.read( temp );

        if( bytesRead <= 0 )
        {
            // We have run out of information to read, bail out of loop
            break;
        }

        db.write(temp, 0, bytesRead);
     }

还有几点你应该小心,即使它们没有导致错误:

    AESDecryptorEngine engine = new AESDecryptorEngine( key );

如果您阅读the docs for this constructor,它会说:

"在给定 AES 密钥的情况下创建 AESEncryptorEngine 类的实例 默认块长度为 16 个字节。”

但是在上一行中,当您创建密钥时,您正在这样做:

    AESKey key = new AESKey( keyData );

according to the docs,它“从现有数据中创建可能的最长密钥。”,但仅 “使用数组的前 128 位”。因此,无论您的 keyData 的长度是多少,您都将始终使用 128 位密钥长度,这是 3 种可用尺寸(128、192、256)中最短的。

相反,您可以显式选择算法 block 密钥长度。例如,要使用 AES-256:

AESKey key = new AESKey(keyData, 0, 256); //key length in BITS
AESDecryptorEngine engine = new AESDecryptorEngine(key, 32); //key lenth IN BYTES

最后,即使你得到这个工作,你应该知道直接从密码(可能是任意大小)派生密钥是不安全的。您可以使用 PKCS5KDF2PseudoRandomSource 从密钥材料(密码)派生出更强的密钥,而不是仅使用 PKCS5 进行填充。

【讨论】:

  • 关于我的最后评论,请看this answer。您可能希望将 PBKDF2 与盐一起使用,而不是 PKCS5。
  • 已编辑答案以包含有关如何从 BlackBerry 中的密码获取密钥的链接。
  • 感谢您的详细信息;确实有帮助。但是,我仍然在 for 循环中遇到错误,但现在抛出的异常是 CryptoIOException。其次通过 AESDecryptorEngine(key, 256) 也会引发 IllegalArgumentException。我解决了这个问题,但是如何解决 CryptoIOException 以及为什么会抛出这个问题?
  • IllegalArgumentException 被抛出,因为我发布的代码中存在错误。 AESDecryptorEngine 构造函数的密钥长度应该以字节为单位传递,这与 AESKey 构造函数需要以位为单位的长度不同,因此仅在引擎中将 256 替换为 32。我还将编辑我的答案。关于加密异常,您可以在此处发布异常消息作为评论吗?
  • 来自您的代码:“请记住,最后 20 个字节代表解密数据的 SHA1 哈希”。您可能按原样复制了 for 循环示例。您只需将前 N-100 个字节传递给引擎,将最后 100 个字节传递给校验和块,其中 N 是 ciphertext 参数的长度。
【解决方案2】:

您的加密数据应正确填充到块大小(16 字节)。 尝试在没有填充的情况下解密数据,看看尾部字节是否对应于PKCS#5填充(例如,如果需要5字节填充,则应附加0x05 0x05 0x05 0x05 0x05字节)。

【讨论】:

  • 我尝试删除填充,但它没有改变。问题是方法在我检查字节之前就退出了。
  • @Sarah,你必须将 BlockDecryptor 提供给 engine 而不是 uengine 才能做到这一点,Sarah。但这很可能会运行并产生随机的二进制输出。如果它看起来像什么东西,那么另一边没有正确填充。
【解决方案3】:

问题是任何具有正确块大小的数据都会解密。问题在于它可能会解密为随机的垃圾。看起来随机的垃圾通常与 PKCS#7 填充方案不兼容,因此例外。

我说问题是因为如果关键数据无效,如果使用了错误的填充或块模式,或者只是如果输入数据在此过程中出现乱码,则可能会引发此异常。最好的调试方法是确保 100% 的算法匹配,并且 binary 输入参数(包括 API 的默认参数)在两边都精确匹配。

【讨论】:

  • 我已经调试过了,我的密钥数据以及要解密的数据(加密值)都是绝对正确的。我对此感到非常迷茫。你能帮我写一个更好的解密方法吗?这是我的第一次尝试..
  • 抱歉,这将严重超出 StackOverflow 的范围。此外,我不是 BlackBerry / ME 框架方面的专家,所以我必须查很多资料。
猜你喜欢
  • 2015-11-03
  • 2012-03-21
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-09-15
  • 2014-09-19
相关资源
最近更新 更多