JavaScript AES加密后Java解密报BadPaddingException问题排查
AES/CBC/PKCS7Padding前后端加解密报错:BadPaddingException
问题场景
前端用JavaScript(CryptoJS)实现AES/CBC/PKCS7Padding加密,后端Java解密时持续抛出javax.crypto.BadPaddingException: pad block corrupted错误。代码片段如下:
JavaScript加密代码
export const encryptPaymentData = (data) => { var encrypt = new JSEncrypt() encrypt.setPublicKey(publicKey) // convert the JSON object to a string const jsonDataString = JSON.stringify(data) // Create iv and secret_key let iv = crypto.randomBytes(16) let secret_key = crypto.randomBytes(32) // iv = Buffer.from(iv).toString("base64") // secret_key = Buffer.from(secret_key).toString("base64") iv = CryptoJS.enc.Base64.stringify(CryptoJS.lib.WordArray.create(iv)) secret_key = CryptoJS.enc.Base64.stringify( CryptoJS.lib.WordArray.create(secret_key) ) // Encrypt the plaintext using AES/CBC/PKCS5Padding console.log("iv", iv) console.log("Secret", secret_key) const ciphertext = CryptoJS.AES.encrypt(jsonDataString, secret_key, { iv: iv, padding: CryptoJS.pad.Pkcs7, mode: CryptoJS.mode.CBC, }) console.log("cipherText", ciphertext) // Print the ciphertext console.log(ciphertext.toString()) }
Java解密代码
private static final String ALGORITHM = "AES/CBC/PKCS7Padding"; public String decryptAES(String ciphertext, String secretKey, String iv) throws Exception { IvParameterSpec ivSpec = new IvParameterSpec(Base64.getDecoder().decode(iv)); SecretKeySpec keySpec = new SecretKeySpec(Base64.getDecoder().decode(secretKey), "AES"); Cipher cipher = Cipher.getInstance(ALGORITHM, "BC"); cipher.init(Cipher.DECRYPT_MODE, keySpec, ivSpec); byte[] decrypted = cipher.doFinal(Base64.getDecoder().decode(ciphertext)); return new String(decrypted); }
问题原因
- 密钥解析错误:CryptoJS的
AES.encrypt方法接收密钥参数时,若传入字符串,会默认按UTF-8编码解析成字节。但你的secret_key是Base64编码后的字符串,直接传入会导致CryptoJS使用的密钥字节和Java端Base64解码后的原始密钥字节完全不匹配。 - IV格式不匹配:同样,你传给CryptoJS的
iv是Base64字符串,CryptoJS会把它当作UTF-8字符串解析成WordArray,这和Java端直接Base64解码得到的原始IV字节不一致。 - 冗余的JSEncrypt代码:当前代码里引入了JSEncrypt但没实际使用公钥加密密钥/IV,不过这不是导致解密报错的直接原因,但属于无用代码。
修正方案
修改JavaScript加密代码,确保密钥和IV以原始字节的WordArray形式传入CryptoJS,而非Base64字符串:
export const encryptPaymentData = (data) => { // 转换数据为JSON字符串 const jsonDataString = JSON.stringify(data) // 生成原始字节的IV和密钥(16字节IV对应AES CBC要求,32字节密钥对应AES-256) const ivWordArray = CryptoJS.lib.WordArray.random(16) const secretKeyWordArray = CryptoJS.lib.WordArray.random(32) // 转换为Base64字符串,用于传输给后端 const ivBase64 = CryptoJS.enc.Base64.stringify(ivWordArray) const secretKeyBase64 = CryptoJS.enc.Base64.stringify(secretKeyWordArray) // 加密:直接传入WordArray类型的密钥和IV const ciphertext = CryptoJS.AES.encrypt(jsonDataString, secretKeyWordArray, { iv: ivWordArray, padding: CryptoJS.pad.Pkcs7, mode: CryptoJS.mode.CBC, }) // 输出用于后端解密的参数:密文(默认Base64)、密钥Base64、IV Base64 console.log("iv", ivBase64) console.log("Secret", secretKeyBase64) console.log("cipherText", ciphertext.toString()) // 返回这三个参数给后端 return { ciphertext: ciphertext.toString(), secretKey: secretKeyBase64, iv: ivBase64 } }
验证说明
修正后:
- 前端生成的
secretKeyBase64和ivBase64,后端直接Base64解码就能得到和前端一致的原始密钥/IV字节。 - CryptoJS的
AES.encrypt输出的ciphertext.toString()是Base64编码的密文,后端直接Base64解码后即可解密。 - Java端代码无需修改,保持现有逻辑即可正常解密。
内容的提问来源于stack exchange,提问作者Akin_Glen
相关产品推荐
相关产品推荐

