为何Java AES-ECB-NoPadding不支持非16字节字符串而JavaScript可以?
AES-ECB跨语言加密兼容问题解答
问题1:异常原因分析
CryptoJS的ZeroPadding会自动对不足AES块大小(16字节)的明文补0,直到长度满足16字节的整数倍,因此7字节明文能正常完成加密。而Java的NoPadding要求明文长度必须严格等于块大小的整数倍,7字节不符合该强制要求,所以抛出IllegalBlockSizeException。
问题2:Java代码修改方案
要和JavaScript的加密结果互解,需让Java端实现与CryptoJS完全一致的ZeroPadding逻辑,具体有两种可行方式:
方式1:基于BouncyCastle库实现ZeroPadding
引入BouncyCastle加密库后,直接使用标准的AES/ECB/ZeroPadding模式,示例代码如下:
import org.bouncycastle.jce.provider.BouncyCastleProvider; import javax.crypto.Cipher; import javax.crypto.spec.SecretKeySpec; import java.security.Security; public class AESZeroPaddingExample { static { Security.addProvider(new BouncyCastleProvider()); } public static byte[] encrypt(byte[] plainText, byte[] key) throws Exception { SecretKeySpec secretKey = new SecretKeySpec(key, "AES"); Cipher cipher = Cipher.getInstance("AES/ECB/ZeroPadding", "BC"); cipher.init(Cipher.ENCRYPT_MODE, secretKey); return cipher.doFinal(plainText); } public static byte[] decrypt(byte[] cipherText, byte[] key) throws Exception { SecretKeySpec secretKey = new SecretKeySpec(key, "AES"); Cipher cipher = Cipher.getInstance("AES/ECB/ZeroPadding", "BC"); cipher.init(Cipher.DECRYPT_MODE, secretKey); return cipher.doFinal(cipherText); } }
方式2:手动实现ZeroPadding逻辑
若不想引入第三方库,可手动给明文补0至16字节整数倍,解密时再去除末尾的补0:
import javax.crypto.Cipher; import javax.crypto.spec.SecretKeySpec; public class AESManualZeroPadding { private static final int BLOCK_SIZE = 16; public static byte[] encrypt(byte[] plainText, byte[] key) throws Exception { // 计算需要补0的长度 int paddingLength = BLOCK_SIZE - (plainText.length % BLOCK_SIZE); byte[] paddedText = new byte[plainText.length + paddingLength]; System.arraycopy(plainText, 0, paddedText, 0, plainText.length); // 填充0 for (int i = plainText.length; i < paddedText.length; i++) { paddedText[i] = 0; } SecretKeySpec secretKey = new SecretKeySpec(key, "AES"); Cipher cipher = Cipher.getInstance("AES/ECB/NoPadding"); cipher.init(Cipher.ENCRYPT_MODE, secretKey); return cipher.doFinal(paddedText); } public static byte[] decrypt(byte[] cipherText, byte[] key) throws Exception { SecretKeySpec secretKey = new SecretKeySpec(key, "AES"); Cipher cipher = Cipher.getInstance("AES/ECB/NoPadding"); cipher.init(Cipher.DECRYPT_MODE, secretKey); byte[] decrypted = cipher.doFinal(cipherText); // 去除末尾的补0 int lastNonZeroIndex = decrypted.length - 1; while (lastNonZeroIndex >= 0 && decrypted[lastNonZeroIndex] == 0) { lastNonZeroIndex--; } byte[] result = new byte[lastNonZeroIndex + 1]; System.arraycopy(decrypted, 0, result, 0, result.length); return result; } }
附:相关代码及报错信息
报错的Java示例代码
import javax.crypto.Cipher; import javax.crypto.spec.SecretKeySpec; public class AESNoPaddingError { public static void main(String[] args) throws Exception { byte[] key = new byte[16]; // 16字节密钥 byte[] plainText = new byte[7]; // 7字节明文 SecretKeySpec secretKey = new SecretKeySpec(key, "AES"); Cipher cipher = Cipher.getInstance("AES/ECB/NoPadding"); cipher.init(Cipher.ENCRYPT_MODE, secretKey); cipher.doFinal(plainText); // 此处抛出异常 } }
无报错的JavaScript示例代码
// 引入CryptoJS库 const CryptoJS = require('crypto-js'); const key = CryptoJS.enc.Hex.parse('00000000000000000000000000000000'); // 16字节密钥 const plainText = CryptoJS.enc.Hex.parse('00000000000000'); // 7字节明文 const cipherText = CryptoJS.AES.encrypt(plainText, key, { mode: CryptoJS.mode.ECB, padding: CryptoJS.pad.ZeroPadding }); console.log(cipherText.toString());
报错信息
javax.crypto.IllegalBlockSizeException: Input length not multiple of 16 bytes
内容的提问来源于stack exchange,提问作者DavidDunham
相关产品推荐
相关产品推荐

