《NodeJS开发教程14-Crypto加密与解密》
简介
这节我们来一块研究一下NodeJS提供的关于秘钥(加密和解密)的相关算法API。根据平时常用的加解密算法我归纳了如下四种类别:(算法不止这些这里只是抛砖引玉)
1.内容编解码类(Base64)
2.内容摘要类(MD5、SHA1、SHA256、SHA512)
3.内容加密解密类 又分为:对称加密解密(AES),非对称加密解密(RSA)
4.内容签名类(RSA+SHA1 或 RSA+SHA256 或 RSA+MD5等等)
NodeJS中处理秘钥相关的模块是 crypto ,需要首先引入
/*crypto加密解密*/
let crypto;
try {
crypto = require('crypto');//如果不支持 crypto模块则会抛出异常
......//进行编解码操作
}catch (err) {
console.log('不支持 crypto!');
}
1.内容编解码类
- Base64编解码
Base64编解码其实算不上是加密解密算法,顶多算是一种编解码算法,因为Base64算法首先是可逆的,而且是非常容易被破译的,所以我们一般用Base64都是去编码信息内容,基本不用它去加密,即使加了也和没加一样,呵呵。。。接下来晒出nodejs进行Base64编解码示例:
//BASE64编解码
//编码
var bc = new Buffer("Base64编解码内容");
var bec = bc.toString("base64");
console.log("Base64编码后结果: %s",bec);
//log打印:Base64编码后结果: QmFzZTY057yW6Kej56CB5YaF5a65
//解码
var bdc = new Buffer(bec, "base64");
var bdcs = bdc.toString();
console.log("Base64解码后结果: %s",bdcs);
//log打印:Base64解码后结果: Base64编解码内容
2.内容摘要类(MD5、SHA1、SHA256、SHA512)
摘要算法又称哈希/散列算法。它通过一个函数,把任意长度的数据转换为一个长度固定的数据串(通常用16进制的字符串表示)。算法不可逆。
摘要一般用作验证内容的完整性,真实性(比如我们上传一个压缩文件使用MD5进行加密,得到一个摘要串,下载我们这个压缩文件的一方只需要同样使用MD5算法进行加密后,得到摘要串和我们上传时得到的摘要串做对比,一致则说明下载的文件是完整的可信的,可以放心使用!_)
接下来我列举几个NodeJS的摘要算法:
- MD5加密摘要算法使用
//MD5-产生128位的加密结果【不可逆】
var hash_md5=crypto.createHash("md5");
hash_md5.update("加密内容ABCD1234");
var md5c=hash_md5.digest("hex");
console.log("MD5加密后结果: %s",md5c);
log输出:MD5加密后结果: 84111fcac08f3a3e30c5df95cf6585a4
这里标记一下update()这个方法,它可以追加内容字符串,追加后得到的摘要结果和上面得到的结果是一样的:
//md5-update追加字符串(不可逆)
hash_md5=crypto.createHash("md5");
hash_md5.update("加密内容");
hash_md5.update("ABCD");
hash_md5.update("1234");
md5c=hash_md5.digest("hex");
console.log("MD5update追加:%s",md5c);
log输出:MD5update追加:84111fcac08f3a3e30c5df95cf6585a4
- SHA1加密摘要算法使用
//SHA1-产生160位的加密结果【不可逆】
var hash_sha1=crypto.createHash("sha1");
hash_sha1.update("加密内容ABCD1234");
var sha1c=hash_sha1.digest("hex");//显示为16进制
console.log("SHA1加密后结果:%s",sha1c);
log输出:SHA1加密后结果:f4c1ec92573c29e0220b82f81cb33f304116391d
- SHA256加密摘要算法使用
//SHA256-产生256位的加密结果【不可逆】
var hash_sha256=crypto.createHash("sha256");
hash_sha256.update("加密内容ABCD1234");
var sha256c=hash_sha256.digest("hex");//显示为16进制
console.log("SHA256加密后结果:%s",sha256c);
log输出:SHA256加密后结果:e7c9f5967b1d9e5a997aeb18d8555d5de7a284a9c7e6deb296b90492bc48dd79
-
SHA256加密(Hmac方式)
HMAC是密钥相关的哈希运算消息认证码,HMAC运算利用哈希算法,以一个密钥和一个消息为输入,生成一个消息摘要作为输出。
HMAC这种方式(需要额外key)要比传统摘要方式(不需要额外key)更加保密,不易破解!
除了支持SHA256还支持其它HMAC方式的摘要算法,比如SHA1、SHA512、MD5等。
//sha256加密(Hmac方式)拥有一个加密Key,这样更安全一些
const secret="AK41abud";//秘钥
var hmac=crypto.createHmac("sha256",secret);
var content=hmac.update("加密内容ABCD1234");
var cryptoContent=content.digest("hex");
console.log("sha256(Hmac方式)加密后结果:%s",cryptoContent);
log输出:sha256(Hmac方式)加密后结果:b4e164a61eb1d0dd532988fe5448d47d65f1ee4d6caf6132200727d1d81dddc4
3.内容加密解密类又分为: 对称加密解密(AES),非对称加密解密(RSA)
对信息进行加密解密,有很多种算法,当然不只是AES和RSA这两种,这两种只是我们平时比较常用的。
加密解密算法,大致又分为对称加密和非对称加密两大类,当然这些加密算法都是可逆的,如果像上面介绍的MD5、SHA1是不可逆的,那我们把内容加完秘后解不开了,不就悲剧了。。。呵呵
-
AES对称加密
对称加密大概可以理解为,加密和解密,使用的是同一个秘钥key。
//AES对称加密
var secretkey="passwd";//唯一(公共)秘钥
var content="需要加密的内容ABC";
var cipher=crypto.createCipher('aes192', secretkey);//使用aes192加密
var enc=cipher.update(content,"utf8","hex");//编码方式从utf-8转为hex;
enc+=cipher.final('hex');//编码方式转为hex;
//
//AES对称解密
var decipher=crypto.createDecipher('aes192', secretkey);
var dec=decipher.update(enc,"hex", "utf8");
dec+=decipher.final("utf8");
console.log("AES对称解密结果:"+dec);
log输出:AES对称解密结果:需要加密的内容ABC
-
RSA非对称加密
非对称加密大致可以这样理解:加密和解密,使用的是不同的秘钥,通常我们使用算法会得到一对秘钥(一个叫做私钥,另一个叫做公钥),公钥一般用来进行加密,而私钥一般用来进行解密,当然你也可以颠倒过来使用,私钥加密公钥解密都是可以的(只是一般不这么使用)。
接下来我介绍一下在nodejs中如何使用RSA进行加密和解密:
- 首先我们需要得到公钥和私钥:
使用OpenSSL算法可以得到RSA公钥和私钥(如果没安装OpenSSL,可以百度一下先安装一下)
安装好OpenSSL后cmd生成RSA私钥:
openssl genrsa -out privatekey.pem 1024
cmd再生成公钥:
openssl rsa -in privatekey.pem -pubout -out publickey.pem
这样在当前目录中就会生成 ‘privatekey.pem’(私钥) 和 ‘publickey.pem’ (公钥)这两个文件,之后我们就使用这两个文件进行RSA非对称加密和解密。
//RSA非对称加密解密
const fs=require("fs");
const privatepem2=fs.readFileSync("./privatekey.pem");//私有key【需要 pem 编码的key】server.pem
const publicpem2=fs.readFileSync("./publickey.pem");//公有key【需要 pem 编码的key】cert.pem
const prikey2=privatepem2.toString();
const pubkey2=publicpem2.toString();
// 加密方法
var encrypt = (data, key) => {
// 注意,第二个参数是Buffer类型
return crypto.publicEncrypt(key, Buffer.from(data));
};
// 解密方法
var decrypt = (encrypted, key) => {
// 注意,encrypted是Buffer类型
return crypto.privateDecrypt(key, encrypted);
};
const plainText = "我是RSA非对称加密字符串内容";
const crypted = encrypt(plainText, pubkey2); // 加密
const decrypted = decrypt(crypted, prikey2); // 解密
console.log("RSA非对称解密结果:%s",decrypted.toString());
log输出:RSA非对称解密结果:我是RSA非对称加密字符串内容
4.内容签名类(RSA+SHA1 或 RSA+SHA256 或 RSA+MD5等等)
“信息内容签名”其实和我们日常中对纸质文件进行签名是一个道理。又称为“数字签名”,包括报文摘要。报文摘要和非对称加密一起,提供数字签名的方法。
数字签名主要是保证信息的完整和提供信息发送者的身份认证和不可抵赖性,这其中,“完整性”主要就是由报文摘要提供的,报文摘要就是用来防止发送的报文被篡改。
使用流程:
- 使用RSA私钥进行签名(对信息报文生成的摘要进行私钥签名)生成签名串,一般是16进制字符串
- 使用RSA公钥进行签名校验(验明正身)
其实很好理解,你可以想象成我们手签的名字,通过独一无二的书写字体去验证是否是出自于你的笔记。。。
//非对称签名校验
const privatepem=fs.readFileSync("./privatekey.pem");//私有key【需要 pem 编码的key】server.pem
const publicpem=fs.readFileSync("./publickey.pem");//公有key【需要 pem 编码的key】cert.pem
const otherkeys=require("./otherkeys.js");//其它公钥和私钥(测试用,如果用其它的公钥进行校验签名肯定是无法通过的)
const prikey=privatepem.toString();
const pubkey=publicpem.toString();
var data = "我是信息内容摘要"
var sign = crypto.createSign('RSA-SHA256');//创建签名算法
sign.update(data);
var sig = sign.sign(prikey, 'hex');//得到签名
var verify = crypto.createVerify('RSA-SHA256');
verify.update(data);
var t=verify.verify(pubkey, sig, 'hex');
// var t=verify.verify(otherkeys.pubKey, sig, 'hex');//用其它公钥校验无法通过!
console.log("非对称签名校验结果结果:"+t);
log输出:非对称签名校验结果结果:true
这里我把其它的公钥和秘钥对也贴出来,otherkeys.js内容:
//其它私钥和公钥
exports.privKey = `-----BEGIN RSA PRIVATE KEY-----
MIICXQIBAAKBgQDFWnl8fChyKI/Tgo1ILB+IlGr8ZECKnnO8XRDwttBbf5EmG0qV
8gs0aGkh649rb75I+tMu2JSNuVj61CncL/7Ct2kAZ6CZZo1vYgtzhlFnxd4V7Ra+
aIwLZaXT/h3eE+/cFsL4VAJI5wXh4Mq4Vtu7uEjeogAOgXACaIqiFyrk3wIDAQAB
AoGBAKdrunYlqfY2fNUVAqAAdnvaVOxqa+psw4g/d3iNzjJhBRTLwDl2TZUXImEZ
QeEFueqVhoROTa/xVg/r3tshiD/QC71EfmPVBjBQJJIvJUbjtZJ/O+L2WxqzSvqe
wzYaTm6Te3kZeG/cULNMIL+xU7XsUmslbGPAurYmHA1jNKFpAkEA48aUogSv8VFn
R2QuYmilz20LkCzffK2aq2+9iSz1ZjCvo+iuFt71Y3+etWomzcZCuJ5sn0w7lcSx
nqyzCFDspQJBAN3O2VdQF3gua0Q5VHmK9AvsoXLmCfRa1RiKuFOtrtC609RfX4DC
FxDxH09UVu/8Hmdau8t6OFExcBriIYJQwDMCQQCZLjFDDHfuiFo2js8K62mnJ6SB
H0xlIrND2+/RUuTuBov4ZUC+rM7GTUtEodDazhyM4C4Yq0HfJNp25Zm5XALpAkBG
atLpO04YI3R+dkzxQUH1PyyKU6m5X9TjM7cNKcikD4wMkjK5p+S2xjYQc1AeZEYq
vc187dJPRIi4oC3PN1+tAkBuW51/5vBj+zmd73mVcTt28OmSKOX6kU29F0lvEh8I
oHiLOo285vG5ZtmXiY58tAiPVQXa7eU8hPQHTHWa9qp6
-----END RSA PRIVATE KEY-----
`;
exports.pubKey = `-----BEGIN PUBLIC KEY-----
MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDFWnl8fChyKI/Tgo1ILB+IlGr8
ZECKnnO8XRDwttBbf5EmG0qV8gs0aGkh649rb75I+tMu2JSNuVj61CncL/7Ct2kA
Z6CZZo1vYgtzhlFnxd4V7Ra+aIwLZaXT/h3eE+/cFsL4VAJI5wXh4Mq4Vtu7uEje
ogAOgXACaIqiFyrk3wIDAQAB
-----END PUBLIC KEY-----
`;
这里只给出一种签名算法,其实NodeJS为我们还提供了很多其它的签名算法,具体可以查看官方API文档。
签名算法,我们在平时用的还是很多的,比如 微信支付、支付宝支付等等,使用签名需要去验明信息来源身份的有效性,这样使我们的信息传递交互更安全可靠!!
谢谢大家!共勉!