AES-GCM在NODEJS和JAVA加解密的坑

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

发布了15 篇原创文章 · 获赞 3 · 访问量 6532

猜你喜欢

转载自blog.csdn.net/yushiershi/article/details/105064835