AES/CBC/PKCS5Padding加密JSON字符串解密时抛出IllegalBlockSizeException
问题分析与解决方案
这个异常的核心原因是你在加密过程中非法截断了Base64编码后的密文,又在解密时强行补全==,这种操作彻底破坏了Base64编码的完整性。只有当原始Base64字符串末尾恰好有两个=时才能正常工作,而当加密的内容(比如JSON字符串)导致Base64编码后没有后缀=时,就会导致解密时Base64解码出的字节长度不符合AES/CBC的块大小要求(必须是16的倍数),从而抛出IllegalBlockSizeException。
具体错误点
看你的encrypt方法:
String code = Base64.getEncoder().encodeToString(encrypted); return salt + code.substring(0, code.length() - 2) + iv;
你无条件截断了Base64字符串的最后两个字符,然后在decrypt方法中强行补回==:
String base = message.substring(32, message.length() - 32) + "==";
但Base64的后缀=是用来补位的,只有当原始字节数不是3的倍数时才会出现,数量可能是1个、2个,甚至没有。比如:
- 当加密后的字节数是16(如"Hello World"加密后):Base64编码后长度为22,末尾有两个
=,截断后补回==刚好正确。 - 当加密后的字节数是24(比如JSON字符串加密后):Base64编码后长度为32,没有后缀
=,截断后补回==会导致Base64解码出的字节数比实际密文少,自然无法满足AES的块大小要求。
修正方案
完全去掉对Base64字符串的截断和补全操作,直接拼接salt、完整的Base64密文、iv即可——因为salt和iv的长度是固定的(都是32位十六进制字符串),解密时可以准确拆分:
修正后的encrypt方法
public static String encrypt(String password, String message) throws Exception { String salt = random(16); String iv = random(16); SecretKey key = generateKey(salt, password); byte[] encrypted = doFinal(Cipher.ENCRYPT_MODE, key, iv, message.getBytes("UTF-8")); String code = Base64.getEncoder().encodeToString(encrypted); // 直接拼接salt、完整Base64密文、iv return salt + code + iv; }
修正后的decrypt方法
public static String decrypt(String password, String message) throws Exception { String salt = message.substring(0, 32); String iv = message.substring(message.length() - 32); // 提取中间完整的Base64密文 String base = message.substring(32, message.length() - 32); SecretKey key = generateKey(salt, password); byte[] decrypted = doFinal(Cipher.DECRYPT_MODE, key, iv, Base64.getDecoder().decode(base)); return new String(decrypted, "UTF-8"); }
额外优化建议
- 你的
HEX数组中有大小写不一致的问题:{'0', '1', ..., 'A', 'b', 'c', 'D', 'e', 'F'},建议统一为大写或小写,虽然Character.digit方法不区分大小写,但统一风格更规范。 random方法中每次创建新的SecureRandom实例,建议复用一个全局实例以提高效率。
验证
修正后,无论是普通字符串还是JSON格式字符串,加密和解密都能正常运行,不会再抛出块大小异常。
内容的提问来源于stack exchange,提问作者Felix Gaebler
相关产品推荐
相关产品推荐

