最终成果是AES-128-GCM,先上代码
NODEJS
import crypto from 'crypto' //crypto是nodejs内置模块
const iv = "0123456789ABCDEF";
//加密方法
function encodeAes(word, aesKey) {
if (!word) {
return ''
}
if (typeof word != 'string') {
word = JSON.stringify(word)
}
const md5 = crypto.createHash('md5');
const result = md5.update(aesKey).digest();
const cipher = crypto.createCipheriv('aes-128-gcm', result, iv);
const encrypted = cipher.update(word, 'utf8');
const finalstr = cipher.final();
const tag = cipher.getAuthTag();
const res = Buffer.concat([encrypted, finalstr, tag]);
return res.toString('base64');
}
//解密方法
function decodeAes(word, aesKey) {
if (!word) {
return ''
}
const md5 = crypto.createHash('md5');
const result = md5.update(aesKey).digest();
const decipher = crypto.createDecipheriv('aes-128-gcm', result, iv);
const b = Buffer.from(word, 'base64')
decipher.setAuthTag(b.subarray(b.length - 16));
const str = decipher.update(Buffer.from(b.subarray(0, b.length - 16), 'hex'));
const fin = decipher.final();
const decryptedStr = new TextDecoder('utf8').decode(Buffer.concat([str, fin]))
try {
return JSON.parse(decryptedStr);
} catch (e) {
return decryptedStr
}
}
export default {
encodeParams(origin, aesKey) {
if (!origin) {
origin = {};
}
encodeAes(origin, aesKey);
},
decodeParams(parameters, aesKey) {
if (!parameters) {
return "";
}
return decodeAes(parameters, aesKey);
}
}
JAVA
import java.security.MessageDigest;
import java.security.Security;
import java.util.Base64;
import javax.crypto.Cipher;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
/**
* @author Lisa
*/
public class GCMUtil {
private static final String IV = "0123456789ABCDEF";
private static final String ALGORITHMSTR = "AES/GCM/NoPadding";
private static final String DEFAULT_CODING = "utf-8";
/**
* 如果报错java.security.NoSuchProviderException: no such provider: BC,那么需要加上这一段,同时需要bcprov-jdk15on.jar
*/
static {
Security.addProvider(new BouncyCastleProvider());
}
/**
* 加密
* @param content
* @param encryptKey
* @param iv
* @return
* @throws Exception
*/
public static String aesEncrypt(String content, String encryptKey) throws Exception {
byte[] input = content.getBytes(DEFAULT_CODING);
MessageDigest md = MessageDigest.getInstance("MD5");
byte[] thedigest = md.digest(encryptKey.getBytes(DEFAULT_CODING));
SecretKeySpec skc = new SecretKeySpec(thedigest, "AES");
IvParameterSpec ivspec = new IvParameterSpec(IV.getBytes(DEFAULT_CODING));
Cipher cipher = Cipher.getInstance(ALGORITHMSTR, "BC");
cipher.init(Cipher.ENCRYPT_MODE, skc, ivspec);
byte[] cipherText = new byte[cipher.getOutputSize(input.length)];
int ctLength = cipher.update(input, 0, input.length, cipherText, 0);
ctLength += cipher.doFinal(cipherText, ctLength);
return Base64.getEncoder().encodeToString(cipherText);
}
/**
* 解密
* @param tmp
* @param decryptKey
* @param iv
* @return
* @throws Exception
*/
public static String aesDecrypt(String tmp, String decryptKey) throws Exception {
byte[] keyb = decryptKey.getBytes(DEFAULT_CODING);
MessageDigest md = MessageDigest.getInstance("MD5");
byte[] thedigest = md.digest(keyb);
SecretKeySpec skey = new SecretKeySpec(thedigest, "AES");
IvParameterSpec ivspec = new IvParameterSpec(IV.getBytes(DEFAULT_CODING));
Cipher dcipher = Cipher.getInstance(ALGORITHMSTR, "BC");
dcipher.init(Cipher.DECRYPT_MODE, skey, ivspec);
byte[] clearbyte = dcipher.doFinal(Base64.getDecoder().decode(tmp));
return new String(clearbyte, DEFAULT_CODING);
}
public static void main(String[] args) throws Exception {
final String key = "alckdirtjgfl0tig";
String origin = "abcdefggghhhiiiijjjjjj中文测试";
String encryptstr = aesEncrypt(origin, key);
System.out.println(encryptstr);
String decryptstr= aesDecrypt(encryptstr, key);
System.out.println(decryptstr);
}
}
然后说说坑
1、JAVA256位秘钥需要JDK支持,据说JDK1.8某个小版本之后是支持的,所以不是所有JDK8都支持
2、IV生成时,可以给字符串,也可以给长度。如果给长度,根据代码不同,可能是长度个数的0(比如12个0),也可能是长度个数的随机数。后者需要将IV和加密字符串一起传递。但是如果你不清楚代码究竟用的哪种,那建议还是给定字符串吧。
3、NODEJS对于authTag的值并没有要求一定要拼接到加密结果(甚至解密的时候是要去掉的),但是JAVA是把authTag的值自行拼接到加密结果里面的。网上找的代码,是分离加密结果前16位作为authTag。但是我最后搞定是加密结果的最后16位……醉了。
4、NODEJS的hex,base64,binary转换我反正是晕了,现在这个是排列组合后成功的结果,别问我为什么是这样……JAVA就只要一个编码,真好。
5、这个代码里面没有加SALT盐值,有兴趣的可以自行探索。