如何在Java中实现CryptoJS.AES.encrypt并解决解密报错问题
问题解答
你的代码存在3个核心错误:
- 盐的生成逻辑错误。你当前从明文字节的8~16位截取作为盐,不符合crypto-js的默认逻辑:crypto-js默认会随机生成8字节的盐值用于密钥派生,盐不能来自明文内容。
- 加密输出格式不符合crypto-js的规范。crypto-js默认使用OpenSSLFormatter格式化加密结果,结构为:固定前缀
Salted__(占8字节) + 8字节随机盐 + 实际密文,将整个二进制串做Base64编码后才是最终返回值。你当前只返回了密文的Base64,缺少前缀和盐,crypto-js解密时无法正确派生密钥,解密结果为乱码,转UTF-8就会抛出Malformed utf-8 data错误。 - 代码存在变量名笔误,你定义的明文字符串变量是
toEncrypt,但截取盐的时候用了不存在的stringToEncrypt,实际运行会直接报错。
修复后的实现代码
public String encrypt(Map<String,Object> param){ try { String toEncrypt = objectMapper.writeValueAsString(param); MessageDigest md5 = MessageDigest.getInstance("MD5"); // 随机生成8字节盐 SecureRandom random = new SecureRandom(); byte[] saltData = new byte[8]; random.nextBytes(saltData); final byte[][] keyAndIV = generateKeyAndIV(32, 16, 1, saltData, "apasswordblabla".getBytes(StandardCharsets.UTF_8), md5); SecretKeySpec skeySpec = new SecretKeySpec(keyAndIV[0], "AES"); IvParameterSpec iv = new IvParameterSpec(keyAndIV[1]); Cipher cipher = Cipher.getInstance("AES/CBC/PKCS7Padding", BouncyCastleProvider.PROVIDER_NAME); cipher.init(Cipher.ENCRYPT_MODE, skeySpec, iv); byte[] encryptedData = cipher.doFinal(toEncrypt.getBytes(StandardCharsets.UTF_8)); // 拼接OpenSSL兼容格式的输出 byte[] result = new byte[8 + saltData.length + encryptedData.length]; System.arraycopy("Salted__".getBytes(StandardCharsets.UTF_8), 0, result, 0, 8); System.arraycopy(saltData, 0, result, 8, 8); System.arraycopy(encryptedData, 0, result, 16, encryptedData.length); return Base64.getEncoder().encodeToString(result); } catch (Exception e) { e.printStackTrace(); return null; } }
现成工具方案
你不需要手动实现这套兼容逻辑,可以直接使用hutool-crypto模块的AES工具类,配置为OpenSSL密钥派生模式即可直接和crypto-js的默认加密解密逻辑互通。
内容的提问来源于stack exchange,提问作者rizesky
相关产品推荐
相关产品推荐

