如何在Spring Boot中解密CryptoJS生成的加密数据?
Spring Boot适配CryptoJS AES解密方案修正
你的解密逻辑和客户端CryptoJS的实现存在两处核心不匹配,导致解密失败及IV长度错误,以下是问题分析和修正后的完整实现:
问题根源
- 密钥推导逻辑错误:客户端使用CryptoJS的
PBKDF2算法生成密钥,而你用自定义MD5循环推导,两者完全不兼容。 - Hex转字节方法可能失效:如果
hexStringToByteArray实现错误,会导致32位的IV十六进制字符串无法正确转为16字节的IV数组,触发长度报错。
修正后的解密代码
以下是完全匹配客户端逻辑的Spring Boot解密实现,使用javax.crypto实现标准PBKDF2密钥推导,并修复Hex转字节逻辑:
import com.google.gson.Gson; import com.google.gson.JsonObject; import javax.crypto.Cipher; import javax.crypto.SecretKey; import javax.crypto.SecretKeyFactory; import javax.crypto.spec.IvParameterSpec; import javax.crypto.spec.PBEKeySpec; import java.security.spec.KeySpec; import java.util.Base64; public class CryptoJsDecryptor { // 严格匹配客户端CryptoJS的PBKDF2参数 private static final int PBKDF2_ITERATIONS = 1000; private static final int AES_KEY_SIZE = 256; private static final String PBKDF2_ALGORITHM = "PBKDF2WithHmacSHA1"; private static final String AES_TRANSFORMATION = "AES/CBC/PKCS5Padding"; public static String decrypt(String passphrase, String jsonString) throws Exception { JsonObject jsonObject = new Gson().fromJson(jsonString, JsonObject.class); String ctBase64 = jsonObject.get("ct").getAsString(); String ivHex = jsonObject.get("iv").getAsString(); String saltHex = jsonObject.get("s").getAsString(); // 将Hex字符串转为字节数组 byte[] salt = hexStringToByteArray(saltHex); byte[] iv = hexStringToByteArray(ivHex); byte[] cipherText = Base64.getDecoder().decode(ctBase64); // 使用PBKDF2生成密钥,完全对齐客户端逻辑 SecretKeyFactory factory = SecretKeyFactory.getInstance(PBKDF2_ALGORITHM); KeySpec spec = new PBEKeySpec(passphrase.toCharArray(), salt, PBKDF2_ITERATIONS, AES_KEY_SIZE); SecretKey secretKey = factory.generateSecret(spec); // 初始化AES解密器 Cipher cipher = Cipher.getInstance(AES_TRANSFORMATION); IvParameterSpec ivSpec = new IvParameterSpec(iv); cipher.init(Cipher.DECRYPT_MODE, secretKey, ivSpec); // 解密并返回明文 byte[] decryptedBytes = cipher.doFinal(cipherText); return new String(decryptedBytes, "UTF-8"); } // 正确的十六进制字符串转字节数组实现 private static byte[] hexStringToByteArray(String s) { int len = s.length(); byte[] data = new byte[len / 2]; for (int i = 0; i < len; i += 2) { data[i / 2] = (byte) ((Character.digit(s.charAt(i), 16) << 4) + Character.digit(s.charAt(i+1), 16)); } return data; } // 测试示例 public static void main(String[] args) throws Exception { String publicKey = "S!ZcdNb4XXcw_jXbHQqiFsHp6"; String encData = "{\"ct\":\"d97K/kt03Inh9wYp90iyNwefnaTwMAeQXTjOwJR+gU0=\",\"iv\":\"4101e6c0938e312952ea24f19306cfb2\",\"s\":\"615cc2b4480e260a\"}"; String plainText = decrypt(publicKey, encData); System.out.println("解密结果: " + plainText); } }
关键细节说明
- PBKDF2参数对齐:客户端CryptoJS的
PBKDF2默认使用SHA-1哈希、1000次迭代、256位密钥,服务端必须严格匹配这些参数才能生成一致的密钥。 - Hex转字节正确性:修正后的
hexStringToByteArray方法能将32位的IV十六进制字符串准确转为16字节的IV数组,彻底解决"Wrong IV length"错误。 - 填充模式兼容:Java的
PKCS5Padding对AES的16字节块加密完全兼容CryptoJS的PKCS7填充,无需额外修改。
内容的提问来源于stack exchange,提问作者Febry Fairuz
相关产品推荐
相关产品推荐

