在java中使用三重des(3des)​​解密,得到“javax.crypto.IllegalBlockSizeException:解密时最后一个块不完整”的错误

时间:2013-07-17 03:31:23

标签: java android encryption 3des

我使用3des-encryption-decryption-in-java之类的代码,但是当我使用解密时,它会出现这样的错误

javax.crypto.IllegalBlockSizeException: last block incomplete in decryption
07-17 11:27:27.580: WARN/System.err(22432): at com.android.org.bouncycastle.jcajce.provider.symmetric.util.BaseBlockCipher.engineDoFinal(BaseBlockCipher.java:705)
07-17 11:27:27.580: WARN/System.err(22432): at javax.crypto.Cipher.doFinal(Cipher.java:1111)

但是如果我将final Cipher decipher = Cipher.getInstance("DESede/CBC/PKCS5Padding");更改为final Cipher decipher = Cipher.getInstance("DESede/CFB/NoPadding");,则该方法可以运行但结果错误(模式与服务器不同)。 所以我想知道它的原因。

解密方法:

public static String decrypt(byte[] message) throws Exception {
    final MessageDigest md = MessageDigest.getInstance("SHA-1");
    final byte[] digestOfPassword = md.digest(token.getBytes("utf-8"));
    final byte[] keyBytes = Arrays.copyOf(digestOfPassword, 24);
    for (int j = 0, k = 16; j < 8;) {
        keyBytes[k++] = keyBytes[j++];
    }

    final SecretKey key = new SecretKeySpec(keyBytes, "DESede");
    final IvParameterSpec iv = new IvParameterSpec(new byte[8]);
    final Cipher decipher = Cipher.getInstance("DESede/CBC/PKCS5Padding");
    // final Cipher decipher = Cipher.getInstance("DESede/CFB/NoPadding");
    decipher.init(Cipher.DECRYPT_MODE, key, iv);
    final byte[] plainText = decipher.doFinal(message);
    return new String(plainText, "UTF-8");
}

加密方法:

public static byte[] encrypt(String message) throws Exception {
        final MessageDigest md = MessageDigest.getInstance("SHA-1");
        final byte[] digestOfPassword = md.digest(token
                .getBytes("utf-8"));
        final byte[] keyBytes = Arrays.copyOf(digestOfPassword, 24);
        for (int j = 0, k = 16; j < 8; ) {
            keyBytes[k++] = keyBytes[j++];
        }

        final SecretKey key = new SecretKeySpec(keyBytes, "DESede");
        final IvParameterSpec iv = new IvParameterSpec(new byte[8]);
        final Cipher cipher = Cipher.getInstance("DESede/CBC/PKCS5Padding");
        cipher.init(Cipher.ENCRYPT_MODE, key, iv, new SecureRandom(new byte[5]));
        cipher.init(Cipher.ENCRYPT_MODE, key, iv);

        final byte[] plainTextBytes = message.getBytes("utf-8");
        final byte[] cipherText = cipher.doFinal(plainTextBytes);
        return cipherText;
    }

1 个答案:

答案 0 :(得分:1)

有很多种可能性。最常见的是如果你将密钥编码为字符串,特别是没有指定字符编码。如果要执行此操作,请使用Base-64,它用于编码任何binary数据,而不是字符编码。还要确保源平台和目标平台编码应该相同。当您在这里使用UTF-8然后在另一个en上使用UTF-8必须使用

现在看看你告诉代码运行{{1而不是final Cipher decipher = Cipher.getInstance("DESede/CFB/NoPadding");
在解密时,您必须知道在加密时选择的填充大小模式。正如您所说的那样,当您使用final Cipher decipher = Cipher.getInstance("DESede/CBC/PKCS5Padding");模式时抛出异常,但当你将其更改为CBC时,它就能运行。在这种情况下,你需要确保在加密时使用哪种模式。

作为附注: CBC,OFB和CFB是相同的,但OFB / CFB更好,因为您只需要加密而不是解密,这可以节省代码空间。
CBC(密码块链接)用于数据通过AES功能的地方,并应用反馈来修改预加密数据,以便重复的普通数据不会产生相同的加密数据。数据只能在与基础加密函数的块大小匹配的块中处理(在AES的情况下为128位块),并且必须在加密和解密引擎之间提供此块级别的同步,否则数据将难以理解的 CFB(密码反馈模式)也是一种常见模式,它提供了使基础分组密码像流密码一样工作的可能性。即。因此,正在处理的数据可以是较短值的流(例如字节或甚至单个位),而不是仅处理更大的块。在CFB模式下,数据本身不通过AES引擎,而是进行异或具有AES引擎从先前消息历史记录生成的值。这意味着可以最小化通过CFB功能的延迟,因为应用于数据的唯一处理是XOR功能。数据宽度可以设置为任何大小,直到基础密码块大小,但请注意,随着数据宽度与块大小之比的宽度变小,吞吐量会降低。(侧注结束:D)
如果使用密码反馈(CFB)或输出反馈(OFB)或计数器(CTR)模式进行加密,则密文将与明文的大小相同,因此不需要填充。但是,使用这些模式时要小心,因为初始化矢量(IV)必须是唯一的 同样,使用像RC4或PC1这样的流密码进行加密不需要填充。

现在如果我们进行更严格的调查,你应该注意块的大小和填充大小(已经在上面提到过)。现在你需要确定的第一件事就是加密定义的填充大小算法。正如我所提到的,在CFB情况下不需要填充,所以首先尝试它而不给出填充。
如果问题仍然存在,则检查它是CFB还是pkcs5。尝试你的代码将decryptpion填充大小设置为pkcs7。如果是pkcs7,那么我猜它也适用于CBC。我建议你阅读Using Padding in Encryption

作为附加信息

  • PKCS#5填充在RFC 2898(PKCS#5:基于密码的加密规范版本2.0)中定义。

  • PKCS5填充是一种填充方案,用于扩展任意数据以匹配块密码的块大小,允许接收端可靠地删除填充。

  • PKCS#7(CMS,RFC 3369)定义了一个填充方案,但它是块密码的PKCS#5填充的扩展,块密码超过8个字节。