Java AES加密和JS 解密遇到的问题
问题描述
后端使用的是Java hutool
的AES加密,前端使用的是crypto-js
的AES解密,前端解密后出现错误提示。
Error: Malformed UTF-8 data
at Object.stringify (d:\Program\xmyzt\ChemicalSafety-pc\node_modules\crypto-js\core.js:513:24)
at WordArray.init.toString (d:\Program\xmyzt\ChemicalSafety-pc\node_modules\crypto-js\core.js:268:38)
at crypto.decryptAES (d:\Program\xmyzt\ChemicalSafety-pc\src\util\crypto.js:55:19)
at Object.<anonymous> (d:\Program\xmyzt\ChemicalSafety-pc\src\util\crypto.js:88:20)
at Module._compile (node:internal/modules/cjs/loader:1233:14)
at Module._extensions..js (node:internal/modules/cjs/loader:1287:10)
at Module.load (node:internal/modules/cjs/loader:1091:32)
at Module._load (node:internal/modules/cjs/loader:938:12)
at Function.executeUserEntryPoint [as runMain] (node:internal/modules/run_main:83:12)
at node:internal/main/run_main_module:23:47
问题原因
后端将内容加密成了16进制,前端解密时需要将16进制的内容转换成字符串, 且密码用了base64
解密了。
Java代码
package org.springblade.modules.system.utils;
import cn.hutool.core.codec.Base64;
import cn.hutool.crypto.SecureUtil;
import cn.hutool.crypto.symmetric.AES;
import javax.crypto.KeyGenerator;
import javax.crypto.SecretKey;
import java.security.NoSuchAlgorithmException;
public class AesUtil {
/**
* 密钥
*/
private static final String BASE64_SECRET = "sGfsTYs6WVzmpRQJBNJuKw==";
/**
* aes用来加密解密的byte[]
*/
private final static byte[] SECRET_BYTES = Base64.decode(BASE64_SECRET);
/**
* 根据这个秘钥得到一个aes对象
*/
private final static AES aes = SecureUtil.aes(SECRET_BYTES);
/**
* 加密
* @param str
* @return
*/
public static String encrypt(String str) {
String encrypt = aes.encryptHex(str);
return encrypt;
}
/**
* 解密
* @param str
* @return
*/
public static String decrypt(String str) {
String decrypt = aes.decryptStr(str);
return decrypt;
}
public static void main(String[] args) {
String djm = "19174085971";
String encryptData = encrypt(djm);
System.out.println("加密后:" + encryptData);
String decryptData = decrypt(encryptData);
System.out.println("解密后:" + decryptData);
}
}
JS代码
// aes 解密方法
function decryptAES(data, AES_KEY) {
// base64解密
const key = CryptoJS.enc.Base64.parse(AES_KEY);
// 16进制 > WordArray对象
const secretText = CryptoJS.enc.Hex.parse(data);
// AES解密
const result = CryptoJS.AES.decrypt({ ciphertext: secretText }, key, {
mode: CryptoJS.mode.ECB,
padding: CryptoJS.pad.Pkcs7
});
return result.toString(CryptoJS.enc.Utf8);
}
注意
加密数据传入CryptoJS.AES.decrypt
时,需使用 CryptoJS.AES.decrypt({ ciphertext: secretText }, key, {... })
写法。
const result = CryptoJS.AES.decrypt({ ciphertext: secretText }, key, {
mode: CryptoJS.mode.ECB,
padding: CryptoJS.pad.Pkcs7
});
如果使用 CryptoJS.AES.decrypt(secretText, key, {... })
写法,则会没有数据,巨坑这个问题,导致排查了很久。
const result = CryptoJS.AES.decrypt(secretText, key, {
mode: CryptoJS.mode.ECB,
padding: CryptoJS.pad.Pkcs7
});
两者的区别:
CryptoJS.AES.decrypt({ ciphertext: secretText }, key, {... })
:
- 这种写法中,第一个参数是一个对象,其中包含了 ciphertext 属性,这个属性的值是需要解密的密文数据(secretText)。
- 这种写法明确指定了密文数据,并且可以在对象中添加其他属性,比如 iv(初始化向量)等。
- 这种写法通常用于需要更精细控制解密过程的场景。
CryptoJS.AES.decrypt(secretText, key, {... })
:
- 这种写法中,第一个参数直接是需要解密的密文数据(secretText)。
- 这种写法没有明确指定密文数据的属性,而是直接将密文数据作为第一个参数传递。
- 这种写法通常用于简单的解密场景,不需要额外的控制参数。
在你的代码中,如果你需要更精细地控制解密过程,比如指定初始化向量或者其他参数,你应该使用第一种写法。如果你只需要简单地解密数据,第二种写法就足够了。