require('crypto')
来访问。crypto
模块提供了加密功能,包含对 OpenSSL
的哈希、HMAC、加密、解密、签名以及验证功能的一整套封装,核心模块,使用时不需安装。
所谓加密是以某种算法改变原有的信息数据,使得未授权用户即使获得了已加密信息,因不知解密的方法,无法得知信息真正的含义,通过这种方式提高网络数据传输的安全性,加密算法常见的有哈希算法、HMAC 算法、签名、对称性加密算法和非对称性加密算法,加密算法也分为可逆和不可逆,比如 md5
就是不可逆加密,只能暴力破解(撞库)。
crypto模块
NodeJS中通过引入crypto
来进行访问加密。
const crypto = require('crypto') console.log(crypto) //打印出crypto模块中包含的所有属性,其中就包括常用的createHash、createHmac等加密方法
// 返回支持的加密算法名数组。 const ciphers = crypto.getCiphers() // 返回支持的哈希算法名数组。 const hashes = crypto.getHashes() console.log(hashes) //[ 'RSA-MD4', 'RSA-MD5', 'RSA-MDC2', 'RSA-RIPEMD160', 'RSA-SHA1', 'RSA-SHA1-2', 'RSA-SHA224', 'RSA-SHA256', 'RSA-SHA3-224', 'RSA-SHA3-256', 'RSA-SHA3-384', 'RSA-SHA3-512', 'RSA-SHA384', 'RSA-SHA512', 'RSA-SHA512/224', 'RSA-SHA512/256', 'RSA-SM3', 'blake2b512', 'blake2s256', 'id-rsassa-pkcs1-v1_5-with-sha3-224', 'id-rsassa-pkcs1-v1_5-with-sha3-256', 'id-rsassa-pkcs1-v1_5-with-sha3-384', 'id-rsassa-pkcs1-v1_5-with-sha3-512', 'md4', 'md4WithRSAEncryption', 'md5', 'md5-sha1', 'md5WithRSAEncryption', 'mdc2', 'mdc2WithRSA', 'ripemd', 'ripemd160', 'ripemd160WithRSA', 'rmd160', 'sha1', 'sha1WithRSAEncryption', 'sha224', 'sha224WithRSAEncryption', 'sha256', 'sha256WithRSAEncryption', 'sha3-224', 'sha3-256', 'sha3-384', 'sha3-512', 'sha384', 'sha384WithRSAEncryption', 'sha512', 'sha512-224', 'sha512-224WithRSAEncryption', 'sha512-256', 'sha512-256WithRSAEncryption', 'sha512WithRSAEncryption', 'shake128', 'shake256', 'sm3', 'sm3WithRSAEncryption', 'ssl3-md5', 'ssl3-sha1', 'whirlpool' ]
哈希算法(Hash)
哈希算法也叫散列算法,用来把任意长度的输入变换成固定长度的输出,常见的有 'sha1'
、'md5'
、'sha256'
、'sha512'
等。
md5
md5
是开发中经常使用的算法之一。它具有不可逆;不管加密的内容多长,最后输出的结果长度都相等;内容不同输出的结果完全不同,内容相同输出的结果完全相同的特点。
由于相同的输入经过 md5
加密后返回的结果完全相同,所以破解时通过 “撞库” 进行暴力破解,当连续被 md5
加密 3
次以上时就很难被破解了,所以使用 md5
一般会进行多次加密。
const crypto = require('crypto') // createHash() 创建加密方式 // update() 要加密的内容 // digest() 加密后输出结果,不传参默认返回加密后的 Buffer,常用的参数有 hex、Base64和binary, hex 代表十六进制,加密后长度为 32,Base64 的结果长度为 24,以 == 结尾。 let md5Text1 = crypto.createHash('md5') .update("love") .digest() let md5Text2 = crypto.createHash('md5') .update("love") .digest("base64") console.log(md5Text1) // <Buffer b5 c0 b1 87 fe 30 9a f0 f4 d3 59 82 fd 96 1d 7e> console.log(md5Text2) // tcCxh/4wmvD001mC/ZYdfg==
update
方法的返回值就是 this
,即当前实例,所以支持链式调用,较长的信息也可以多次调用 update
方法进行分段加密,调用 digest
方法同样会返回整个加密后的值。
const crypto = require('crypto') let md5Text3 = crypto.createHash('md5') .update("lo") .update("ve") .digest("base64") console.log(md5Text2) // tcCxh/4wmvD001mC/ZYdfg==
sha1
const crypto = require('crypto') let shaText = crypto.createHash('sha1') .update("love") .digest("hex") console.log(md5Text2) // 9f2feb0f1ef425b292f2f94bc8482494df430413
Hmac算法
Hmac 算法又称加盐算法,是将哈希算法与一个密钥结合在一起,用来阻止对签名完整性的破坏,同样具备 md5
加密的几个特点。
let password = '123' let hmacText1 = crypto.createHmac('sha1',password) .update("love") .digest('base64') let hmacText2 = crypto.createHmac('sha1',password) .update("love") .digest('hex') console.log(hmacText1) // WbuOiMJPQ6q+tQXpL10bJEv/0E4= console.log(hmacText2) // 59bb8e88c24f43aabeb505e92f5d1b244bffd04e
crytpo.createHmac
第一个参数同 crytpo.createHash
,为加密的算法,常用 sha1
和 sha256
,第二个参数为密钥。
digest
方法生成的加密结果长度要大于 md5
,hex
生成的结果长度为 64
,Base64
生成的结果长度为 44
,以 =
结尾。
安全性高于 md5,通过密钥来加密,不知道密钥无法破解,缺点是密钥传输的过程容易被劫持,可以通过一些生成随机密钥的方式避免
对称性加密(Cipher)(官方已弃用)
对称性加密是发送数据时使用密钥和加密算法进行加密,接收数据时需要使用相同的密钥和加密算法的逆算法(解密算法)进行解密,也就是说对称性加密的过程是可逆的,crytpo
中使用的算法为 blowfish
。
本地生成SSH key秘钥
上面提到对称加密要用到秘钥,所以我们第一步应该生成秘钥。生成秘钥的方法有很多种,我们就以下为例,通过命令行来生成秘钥。
首先你可以你先看一下本地是否已经生成过秘钥:
~/.ssh bash: /c/Users/admin/.ssh: Is a directory //此时说明已经存在
如果没有生成,那你就可以通过一下命令来生成秘钥:
ssh-keygen -t rsa -C "your_email@example.com"
生成后你就可以在本地文件夹找到生成的 key 文件,一般包括 id_rsa.pub(公钥)和 id_rsa(私钥)两个文件夹。然后你就可以通过你喜欢的编辑工具来打开查看(默认情况下,生成秘钥在/c/Users/admin/.ssh
文件夹内)。当然你也可以参考链接。
createCipher加密
接着就可以通过crypto.createCipher
方法加密,使用 crypto.createDecipher
方法解密。但是使用的算法和密钥必须相同,需要注意的是解密过程中 update
中需要在第二个参数中指定加密时的格式,如 hex
,在 final
还原数据时需要指定加密字符的编码格式,如 utf8
。
由于createCipher需要的秘钥是用来派生key 和IV,它必须是一个'binary'
编码的字符串或者一个buffer。所以我们可以通过fs
模块来获取一个buffer格式的秘钥。
const crypto = require('crypto') const fs = require('fs') const path = require('path') //path.join(address,name) 第一个参数为文件地址,第二个为文件名称 let privateKey = fs.readFileSync(path.join('C:/Users/admin/.ssh',"id_rsa")) // 加密 let cipher = crypto.createCipher("blowfish", privateKey); cipher.update("love"); // final 方法不能链式调用 let result = cipher.final("hex"); console.log(result); // ff541470a00e5e04 // 解密 let decipher = crypto.createDecipher("blowfish", privateKey); decipher.update(result, "hex"); let data = decipher.final("utf8"); console.log(data); // love
非对称性加密
非对称性加密相也是可逆的,较于对称性加密要更安全,消息传输方和接收方都会在本地创建一对密钥,公钥和私钥,互相将自己的公钥发送给对方,每次消息传递时使用对方的公钥加密,对方接收消息后使用他的的私钥解密,这样在公钥传递的过程中被截获也无法解密,因为公钥加密的消息只有配对的私钥可以解密。
const crypto = require('crypto') const fs = require('fs') const path = require('path') // 获取公钥和私钥 let publicKey = fs.readFileSync(path.join('C:/Users/admin/.ssh', "id_rsa.pub")); let privateKey = fs.readFileSync(path.join('C:/Users/admin/.ssh',"id_rsa")); // 加密 let encodeData = crypto.publicEncrypt(publicKey, Buffer.from("love")); //然然,加密传入的秘钥也可以是私钥,同样能解密出来,主要是在不方便公开的情况下,建议使用公钥 //let encodeData = crypto.publicEncrypt(privateKey, Buffer.from("love")); // 解密 let decodeData = crypto.privateDecrypt(privateKey, encodeData); console.log(decodeData.toString()); // love
签名sign
使用crypto.createSign()
方法用于创建Sign
实例。参数是要使用的哈希函数的字符串名称。不能使用关键字Sign
直接创建对象。
const crypto = require('crypto') const fs = require('fs') const path = require('path') let publicKey = fs.readFileSync(path.join('C:/Users/admin/.ssh', "id_rsa.pub")); let privateKey = fs.readFileSync(path.join('C:/Users/admin/.ssh',"id_rsa")); const sign = crypto.createSign('SHA256'); sign.write('some data to sign'); sign.end(); const signature = sign.sign(privateKey, 'hex'); // console.log(signature) // 验证签名 const verify = crypto.createVerify('SHA256'); verify.write('some data to sign'); verify.end(); console.log(verify.verify(publicKey, signature, 'hex')); //true // console.log(verify.verify(privateKey, signature, 'hex')); //true