如题,主要是做一个开发笔记。
设定和算法
本示例基于Nodejs和标准crypto模块。相关使用的函数和算法配置信息如下:
const { createCipheriv, createDecipheriv, randomBytes} = require("crypto");
// algo config
let ALGO = {
Name : "chacha20-poly1305",
tagLength : 16,
ivLength : 12
}
简单的说明一下:
- 算法为chacha20-poly1305,是一个对称加密算法
- 流式加密,密钥长度256,可以替换RC4
- 带消息验证码(poly1305)安全程度更高
- 和AES相比,软件计算性能更高
- Nodejs内置支持(基于crypto模块)
- iv的长度为12字节,tag为16,这是标准确定的
- iv应当使用随机生成的信息
加密过程和函数
加密的相关代码
const encrypt = (data, key)=>{
let
iv = randomBytes(ALGO.ivLength),
aData = randomBytes(ALGO.tagLength);
// construct the cipher and set AAD
const cipher = createCipheriv(ALGO.Name , key, iv, { authTagLength: ALGO.tagLength })
.setAAD(aData);
let ctext = Buffer.concat([
cipher.update(data, 'utf-8'),
cipher.final(),
cipher.getAuthTag(),
iv,aData]).toString("base64");
return ctext;
}
简单说明:
- aad数据理论上可以使用任意附加明文数据,这里简化使用一个定长的随机信息
- chacha20使用256位的密钥
- 使用算法名词、key、iv、add创建加密实例
- 加密的结果,由加密密文、tag、iv和aad构成
- 原始信息为一个多个部分组成的buffer,最终编码为base64
解密过程和函数
解密的相关代码和函数如下:
const decrypt = (edata, key)=>{
let barray = Buffer.from(edata,"base64");
let adata2 = barray.subarray(-ALGO.tagLength);
let iv2 = barray.subarray(-(ALGO.ivLength+ALGO.tagLength), -ALGO.tagLength);
let tag2 = barray.subarray(-(ALGO.ivLength+2*ALGO.tagLength),-(ALGO.ivLength+ALGO.tagLength));
barray = barray.subarray(0,-(ALGO.ivLength+2*ALGO.tagLength));
try {
// create decipher
let decipher = createDecipheriv(ALGO.Name, key, iv2, { authTagLength: ALGO.tagLength })
.setAuthTag(tag2)
.setAAD(adata2);
let otext = decipher.update(barray).toString("utf-8");
decipher.final();
return otext;
} catch (err) {
console.error(err.message);
}
return null;
}
简单说明:
- 参数为密文和密钥
- 密文先转换为字节数组,然后按照加密的设置和规则,拆分为iv、tag、adata、密文等组成部分
- 基于iv、tag和adata创建解密实例
- 使用解密实例进行解密
- 解密结果输出为utf8字符串
- 为了调用和使用方便,对相关操作和数据进行了简单的自定义封装
测试
相关测试代码如下:
const test = ()=>{
// data to encrypto
let ptext = "China中国";
let ekey = Buffer.alloc(32, 0x01); // 256bit key
let eContent = encrypt(ptext, ekey);
console.log("oText:", Buffer.from(ptext).byteLength, ptext);
console.log("Encrypt:", Buffer.from(eContent,"base64").length, eContent);
let otext = decrypt(eContent, ekey);
console.log("Decrypt:", otext === ptext, otext);
}; test();
-- 运行参考结果
oText: 11 China中国
Encrypt: 55 0zz8Rcss7GCd0AxTRfQwe5CqdGHHh6cGko7BH9fpS/BbLDSD/7DeeCpfeTXeUfKACisP8mkchw==
Decrypt: true China中国
这里的要点如下:
- 简单起见,加密内容是标准utf-8字符串
- 密钥为一个固定的256位密钥,真正使用环境中,应当是一个外部加载或者协商的密钥
- 加密和解密当然应当使用相同的密钥,实际环境中,应当有更安全的交换或者传输机制
- 加密结果为标准Base64字符串
- 由于有iv(12),tag(16),add(16),所以,除了加密信息之外,有固定长度44字节的附加信息。
完整代码
完整的可执行代码如下:
小结
本文简单记录了使用nodejs crypto模块实现的chacha20-poly1305算法的数据加解密操作过程。可以看到,整个流程和设置,基本上和AES加解密没有很大的差异,在nodejs中的实现和操作也非常简单方便,只需要注意有一些设置的要求。