CryptoJS AES解密逻辑迁移至Java后端遇填充错误求助
AES解密逻辑从CryptoJS迁移到Java后端的问题排查
需要将前端可正常运行的CryptoJS AES解密逻辑迁移至Java后端,前端代码、自行编写的Java代码、运行错误如下,期望解密得到JSON对象{"name": "Burak", "surName": "Bayraktaroglu"},请求排查问题。
前端可运行的CryptoJS代码
const token = "U2FsdGVkX1+6YueVRKp6h0dZfk/a8AC9vyFfAjxD4nb7mXsKrM7rI7xZ0OgrF1sShHYNLMJglz4+67n/I7P+fg==" const key = "p80a0811-47db-2c39-bcdd-4t3g5h2d5d1a" // Decrypt const iv = CryptoJS.enc.Utf8.parse(key); const decryptedToken = CryptoJS.AES.decrypt(token, key, { keySize: 16, iv, mode: CryptoJS.mode.CBC, padding: CryptoJS.pad.Pkcs7, }); const stringToken = decryptedToken.toString(CryptoJS.enc.Utf8); const dataToUse = JSON.parse(stringToken) console.log("decryptedToken => ", stringToken); console.log("data => ",dataToUse);
自行编写的Java解密代码
public String decrypt() { String finalToken = null; Cipher cipher; try { String token = "U2FsdGVkX1+6YueVRKp6h0dZfk/a8AC9vyFfAjxD4nb7mXsKrM7rI7xZ0OgrF1sShHYNLMJglz4+67n/I7P+fg=="; String key = "p80a0811-47db-2c39-bcdd-4t3g5h2d5d1a"; Key skeySpec = new SecretKeySpec(Arrays.copyOf(key.getBytes(), 16), "AES"); byte[] iv = Arrays.copyOf(key.getBytes(StandardCharsets.UTF_8), 16); IvParameterSpec ivParameterSpec = new IvParameterSpec(iv); byte[] bytesToDecrypt = Base64.decodeBase64(token.getBytes()); cipher = Cipher.getInstance("AES/CBC/PKCS7Padding"); cipher.init(Cipher.DECRYPT_MODE, skeySpec, ivParameterSpec); byte[] decrypted = cipher.doFinal(bytesToDecrypt); finalToken = new String(decrypted); log.info("Decrypt token succesfully {}", finalToken); } catch (NoSuchAlgorithmException | InvalidAlgorithmParameterException | IllegalBlockSizeException | NoSuchPaddingException | BadPaddingException | InvalidKeyException e) { e.printStackTrace(); log.error("Error decrypting"); } return finalToken; }
运行错误信息
javax.crypto.BadPaddingException: pad block corrupted
若不截断密钥和IV至16字节则会出现长度错误,不确定密钥和IV的设置是否正确。
问题原因及修复方案
问题核心是CryptoJS与Java原生AES实现的密钥处理、密文格式逻辑不一致:
- CryptoJS传入字符串密钥时,会自动通过MD5哈希派生128位密钥,而非直接截断字符串;
- 给定的token是CryptoJS特有的格式化密文(开头
U2FsdGVkX1是加盐标识),包含盐值,不能直接Base64解码后解密。
修复后的Java代码
import org.apache.commons.codec.binary.Base64; import javax.crypto.Cipher; import javax.crypto.spec.IvParameterSpec; import javax.crypto.spec.SecretKeySpec; import java.nio.charset.StandardCharsets; import java.security.MessageDigest; import java.util.Arrays; public class AESDecryptor { public String decrypt() { String finalToken = null; try { String token = "U2FsdGVkX1+6YueVRKp6h0dZfk/a8AC9vyFfAjxD4nb7mXsKrM7rI7xZ0OgrF1sShHYNLMJglz4+67n/I7P+fg=="; String key = "p80a0811-47db-2c39-bcdd-4t3g5h2d5d1a"; // 解析CryptoJS格式化密文:提取盐值和实际密文 byte[] decodedToken = Base64.decodeBase64(token.getBytes(StandardCharsets.UTF_8)); byte[] salt = Arrays.copyOfRange(decodedToken, 8, 16); // 跳过前8字节的"Salted__"标识,取后续8字节盐值 byte[] ciphertext = Arrays.copyOfRange(decodedToken, 16, decodedToken.length); // 按照CryptoJS规则派生密钥和IV:两次MD5哈希 MessageDigest md5 = MessageDigest.getInstance("MD5"); byte[] keyPlusSalt = concat(key.getBytes(StandardCharsets.UTF_8), salt); byte[] hash1 = md5.digest(keyPlusSalt); byte[] hash2 = md5.digest(concat(hash1, keyPlusSalt)); byte[] finalKey = Arrays.copyOf(hash1, 16); // 128位AES密钥 byte[] finalIv = Arrays.copyOf(hash2, 16); // 128位IV // 初始化Cipher执行解密 Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding"); // Java中PKCS5Padding等价于PKCS7Padding SecretKeySpec keySpec = new SecretKeySpec(finalKey, "AES"); IvParameterSpec ivSpec = new IvParameterSpec(finalIv); cipher.init(Cipher.DECRYPT_MODE, keySpec, ivSpec); byte[] decryptedBytes = cipher.doFinal(ciphertext); finalToken = new String(decryptedBytes, StandardCharsets.UTF_8); System.out.println("Decrypted token: " + finalToken); } catch (Exception e) { e.printStackTrace(); } return finalToken; } // 辅助方法:拼接两个字节数组 private byte[] concat(byte[] a, byte[] b) { byte[] result = new byte[a.length + b.length]; System.arraycopy(a, 0, result, 0, a.length); System.arraycopy(b, 0, result, a.length, b.length); return result; } }
关键说明
- CryptoJS格式化密文结构:
Salted__(8字节标识) + 8字节盐值 + 实际密文,需先拆分提取; - 密钥派生规则:对
密钥字符串+盐值做两次MD5哈希,第一次哈希结果作为AES密钥,第二次作为IV; - Java中
PKCS7Padding与PKCS5Padding等价(AES块大小为16字节,PKCS5Padding支持填充至该大小),直接使用AES/CBC/PKCS5Padding即可。
内容的提问来源于stack exchange,提问作者Eliana Chávez Valencia
相关产品推荐
相关产品推荐

