Python与Java AES加解密一致性问题:Java解密报IllegalBlockSizeException
这个问题我之前在做跨语言AES加解密的时候也碰到过,简直踩了好几个坑!javax.crypto.IllegalBlockSizeException这个异常的核心原因很明确:AES作为块加密算法,每个块是16字节,解密时输入的字节数组长度必须是16的倍数(除非用了无填充的流模式)。结合你说的Python解密正常、两边加密结果看似一致的情况,大概率是这几个原因导致的,咱们一个个来捋:
1. Hex编码/解码的不一致(最常见的元凶)
你贴的Java代码片段里的toHexString方法大概率有问题!比如当byte值小于0x10的时候,有没有补前导零?举个例子:如果byte是0x05,要是直接转成Hex字符串是"5"而不是"05",那整个Hex字符串的长度就会比实际需要的短,解码成字节数组时长度自然不是16的倍数,直接触发异常。
Python里的Hex编码通常会把每个byte转成两位十六进制字符,不管值大小,但很多手写的Java Hex方法容易忽略补零的细节。
解决方法:把你的Java Hex转换方法换成标准实现,确保每个byte都转成两位十六进制字符:
// 正确的字节转Hex字符串 private static String toHexString(byte[] data) { StringBuilder buf = new StringBuilder(); for (byte b : data) { // 先把byte转成无符号int,再格式化补零 buf.append(String.format("%02x", b & 0xFF)); } return buf.toString(); } // 对应的Hex字符串转字节数组 private static byte[] hexStringToByteArray(String hex) { int len = hex.length(); // 先检查长度是否为偶数,避免后续出错 if (len % 2 != 0) { throw new IllegalArgumentException("Hex字符串长度必须为偶数"); } byte[] data = new byte[len / 2]; for (int i = 0; i < len; i += 2) { data[i / 2] = (byte) ((Character.digit(hex.charAt(i), 16) << 4) + Character.digit(hex.charAt(i+1), 16)); } return data; }
另外还要注意Hex字符串的大小写,Python可能输出大写,Java解析时如果区分大小写也会出问题,最好统一成小写或大写(上面的代码用的是小写,Python那边也输出小写Hex就好)。
2. 填充模式不匹配
虽然AES的PKCS5Padding和PKCS7Padding在16字节块大小下是等价的,但如果Python端用了NoPadding,而Java端用了默认的PKCS5Padding,就会出问题。因为NoPadding要求明文长度必须是16的倍数,Python可能手动做了填充,但Java解密时会尝试用PKCS5Padding去解,导致长度不匹配。
排查方法:检查Python代码里的AES初始化逻辑,比如用pycryptodome库的话,是不是指定了padding=PKCS7Padding;Java代码里是不是用了Cipher.getInstance("AES/CBC/PKCS5Padding")(或者对应模式)。如果Python用了NoPadding,Java也要同步用NoPadding,并且确保明文、密文长度都是16的倍数。
3. 加密模式或IV的不一致
如果Python用的是CBC模式,Java也必须用CBC模式,并且IV必须完全一致(长度16字节,编码方式相同)。要是Python用了ECB模式(不推荐,安全性差),Java也要对应用ECB模式。模式不一致的话,哪怕加密结果看似一致,解密时也会触发长度异常或者乱码。
排查方法:确认两边的加密模式,比如Python里是不是AES.MODE_CBC,Java是不是"AES/CBC/PKCS5Padding";同时检查IV的生成和传递,比如两边是不是用相同的编码(比如UTF-8)把IV字符串转成字节数组,长度必须是16字节。
4. 密文传递过程中被截断或修改
比如在传递加密后的Hex字符串时,有没有被截断?比如网络传输、存储时丢失了部分字符,导致Hex字符串长度是奇数,解码后字节数组长度就不是整数,更不可能是16的倍数。
排查方法:在Python加密后打印Hex字符串的长度,在Java接收后也打印这个长度,看看是否完全一致。正常情况下,Hex字符串长度应该是偶数,且是32的倍数(因为16字节的AES块转Hex是32字符),多块的话以此类推。
快速验证步骤
- 先打印Java解密前的字节数组长度,确认是不是16的倍数;
- 用上面的标准Hex方法替换你现有的实现,再测试;
- 核对两边的AES模式、填充方式、密钥、IV是否完全一致;
- 检查密文在传递过程中有没有被修改。
内容的提问来源于stack exchange,提问作者L..

