最終成果是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鹽值,有興趣的可以自行探索。