Node.js之crypto加密模块使用及说明
1、crypto模块概述
在Node.js中,使用OpenSSL类库作为其内部实现加密与解密处理的基础手段,这是因为目前OpenSSL已经成为了一个经过严格测试的可靠的加密与解密算法的实现工具。
在Node.js中,OpenSSL类库被封装在crypto模块中,因此开发者可以使用crypto模块来实现各种不同的加密与解密处理。例如,crypto模块中包含了类似MD5或SH-2之类的散列算法。开发者也可以通过crypto模块来实现HMAC运算 。在crypto模块中,提供了一些加密方法来实现数据的可靠加密。另外,在crypto模块中,也提供了一些利用HMAC运算来实现数字签名以及对数字签名进行验证的方法。
1.1、支持的加密算法
在crypto模块中,为每一种加密算法定义了一个类。可以使用getCiphers方法来查看Node.js中能够使用的所有加密算法。
该方法的使用方式如下所示。
crypto.getCiphers()
aes-128-cbc aes-128-cbc-hmac-sha1 aes-128-cbc-hmac-sha256 aes-128-ccm aes-128-cfb aes-128-cfb1 aes-128-cfb8 aes-128-ctr aes-128-ecb aes-128-gcm aes-128-ocb aes-128-ofb aes-128-xts aes-192-cbc aes-192-ccm aes-192-cfb aes-192-cfb1 aes-192-cfb8 aes-192-ctr aes-192-ecb aes-192-gcm aes-192-ocb aes-192-ofb aes-256-cbc aes-256-cbc-hmac-sha1 aes-256-cbc-hmac-sha256 aes-256-ccm aes-256-cfb aes-256-cfb1 aes-256-cfb8 aes-256-ctr aes-256-ecb aes-256-gcm aes-256-ocb aes-256-ofb aes-256-xts aes128 aes128-wrap aes192 aes192-wrap aes256 aes256-wrap aria-128-cbc aria-128-ccm aria-128-cfb aria-128-cfb1 aria-128-cfb8 aria-128-ctr aria-128-ecb aria-128-gcm aria-128-ofb aria-192-cbc aria-192-ccm aria-192-cfb aria-192-cfb1 aria-192-cfb8 aria-192-ctr aria-192-ecb aria-192-gcm aria-192-ofb aria-256-cbc aria-256-ccm aria-256-cfb aria-256-cfb1 aria-256-cfb8 aria-256-ctr aria-256-ecb aria-256-gcm aria-256-ofb aria128 aria192 aria256 bf bf-cbc bf-cfb bf-ecb bf-ofb blowfish camellia-128-cbc camellia-128-cfb camellia-128-cfb1 camellia-128-cfb8 camellia-128-ctr camellia-128-ecb camellia-128-ofb camellia-192-cbc camellia-192-cfb camellia-192-cfb1 camellia-192-cfb8 camellia-192-ctr camellia-192-ecb camellia-192-ofb camellia-256-cbc camellia-256-cfb camellia-256-cfb1 camellia-256-cfb8 camellia-256-ctr camellia-256-ecb camellia-256-ofb camellia128 camellia192 camellia256 cast cast-cbc cast5-cbc cast5-cfb cast5-ecb cast5-ofb chacha20 chacha20-poly1305 des des-cbc des-cfb des-cfb1 des-cfb8 des-ecb des-ede des-ede-cbc des-ede-cfb des-ede-ecb des-ede-ofb des-ede3 des-ede3-cbc des-ede3-cfb des-ede3-cfb1 des-ede3-cfb8 des-ede3-ecb des-ede3-ofb des-ofb des3 des3-wrap desx desx-cbc id-aes128-CCM id-aes128-GCM id-aes128-wrap id-aes128-wrap-pad id-aes192-CCM id-aes192-GCM id-aes192-wrap id-aes192-wrap-pad id-aes256-CCM id-aes256-GCM id-aes256-wrap id-aes256-wrap-pad id-smime-alg-CMS3DESwrap idea idea-cbc idea-cfb idea-ecb idea-ofb rc2 rc2-128 rc2-40 rc2-40-cbc rc2-64 rc2-64-cbc rc2-cbc rc2-cfb rc2-ecb rc2-ofb rc4 rc4-40 rc4-hmac-md5 seed seed-cbc seed-cfb seed-ecb seed-ofb sm4 sm4-cbc sm4-cfb sm4-ctr sm4-ecb sm4-ofb
1.2、支持的散列算法
使用getHashes方法查看所有散列算法:
crypto.getHashes()
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
2、散列算法
- 在Node.js中,为了使用散列算法,首先应该使用createHash方法创建一个hash对象。createHash方法的使用方式如下所示。
crypto.createHash(algorithm)
algorithm:一个在Node.js中可以使用的算法,例如’sha1’、‘md5’、‘sha256’、'sha512’和’ripemd160’等
- 在创建了一个hash对象后,可以通过使用该对象的update方法创建一个摘要。该方法的使用方式如下所示(代码中的hash代表一个hash对象)。可以在摘要被输出前使用多次update方法来添加摘要内容。
hash.update(data,[input_encoding])
data:一个Buffer对象或一个字符串,用于指定摘要内容input_encoding:可选,用于指定摘要内容所需使用的编码格式,可指定参数值为“utf8”、“ascii”或“binary”。如果不使用input_encoding参数,则data参数值必须为一个Buffer对象。
- 可以使用hash对象的digest方法来输出摘要内容。在使用了hash对象的digest方法后,不能再向hash对象中追加摘要内容。digest方法的使用方式如下所示(代码中的hash代表一个hash对象)。
hash.digest([encoding])
encoding:可选,参数值为一个字符串,用于指定输出摘要的编码格式,可指定参数值为“hex”、“binary”及“base64”。如果使用了该参数,那么digest方法返回字符串格式的摘要内容,如果不使用该参数,那么digest方法返回一个Buffer对象。在hash对象的digest方法被调用之后,该对象不能再被使用。
示例:sha1算法为app.js生成一个摘要
const crypto = require("crypto");
const fs = require("fs");
let shasum = crypto.createHash('sha1');
let s = fs.ReadStream('./index.js');
s.on('data', (data)=>{
shasum.update(data);
});
s.on('end', ()=>{
let d = shasum.digest('hex');
console.log(d);
});
//输出 68e7997830b957c2d58645561b5e17af57b8bdbd
//使用openssl验证
$ openssl dgst -sha1 index.js SHA1(index.js)= 68e7997830b957c2d58645561b5e17af57b8bdbd
3、HMAC算法
HMAC算法将散列算法与一个密钥结合在一起,以阻止对签名完整性的破坏。
- 在Node.js中,为了使用HMAC算法,首先应该使用createHmac方法创建一个hmac对象。createHmac方法的使用方式如下所示。
crypto.createHmac(algorithm,key)
algorithm:一个在Node.js中可以使用的算法,例如’sha1’、‘md5’、‘sha256’、'sha512’和’ripemd160’等key:为一个字符串,用于指定一个PEM格式的密钥。
在OpenSSL工具中,可以使用如下命令创建一个密钥。
openssl genrsa -out key.pem 1024
- 在创建了一个hmac对象后,可以通过使用该对象的update方法来创建一个摘要。该方法的使用方式如下所示(代码中的hmac代表一个hmac对象)。
hmac.update(data)
data:一个Buffer对象或一个字符串,用于指定摘要内容。可以在摘要被输出前使用多次update方法来添加摘要内容。
- 可以使用hmac对象的digest方法输出摘要内容。在使用了hmac对象的digest方法后,不能再向hmac对象中追加摘要内容。digest方法的使用方式如下所示(代码中的hmac代表一个hmac对象)。
hmac.digest([encoding])
encoding:可选,参数值为一个字符串,用于指定输出摘要的编码格式,可指定参数值为“hex”、“binary”及“base64”。如果使用了该参数,digest方法返回字符串格式的摘要内容,如果不使用该参数,digest方法返回一个Buffer对象。当hmac对象的digest方法被调用之后,该对象不能再被使用。
示例:使用sha1算法以及key.pem密钥index.js文件生成一个摘要
const crypto = require('crypto');
const fs = require('fs');
let pem = fs.readFileSync('key.pem');
let key = pem.toString('ascii');
let shasum = crypto.createHmac('sha1',key);
let s = fs.ReadStream('./index.js');
s.on('data', (d)=>{
shasum.update(d);
});
s.on('end', ()=>{
var d = shasum.digest('hex');
console.log(d);
});
48c97f16616f8aba06a8b9ec83989234e09f3b3d
4、公钥加密
Node.js提供了以下4个与公钥加密相关的类。
- Cipher类:用于加密数据。
- Decipher类:用于解密数据。
- Sign类:用于生成签名。
- Verify类:用于验证签名。
在使用HMAC算法时,只需要使用一个私钥,但在使用公钥加密技术时,需要使用公钥及私钥,其中私钥用于解密数据以及对数据进行签名,而公钥用于创建只有私钥的拥有者能够读出的加密数据,以及对私钥的拥有者的签名进行验证。
在OpenSSL工具中,可以使用如下命令来为一个私钥创建一个PEM格式的公钥。
openssl req -key key.pem -new -x509 -out cert.pem
4.1、Cipher加密数据
- 在crypto模块中,Cipher类用于对数据进行加密操作。在加密数据之前,首先需要创建一个cipher对象。可以通过如下所示的两种方法创建cipher对象。
createCipher方法(不推荐):该方法使用指定的算法与密码来创建cipher对象。该方法的使用方式如下所示。
crypto.createCipher(algorithm,password)
algorithm:指定在加密数据时所使用的算法,例如“blowfish’”、“aes-256-cbc”等。password:指定加密时所使用的密码,参数值必须为一个二进制格式的字符串或一个Buffer对象。
createCipheriv方法(推荐):该方法使用指定的算法、密码与初始向量(Initialization Vector,IV)来创建cipher对象。该方法的使用方式如下:
crypto.createCipheriv(algorithm,password,iv)
algorithm:指定在加密数据时所使用的算法,例如“blowfish’”、“aes-256-cbc”等。password:指定加密时所使用的密码,参数值必须为一个二进制格式的字符串或一个Buffer对象。iv:指定加密时所使用的初始向量,参数值必须为一个二进制格式的字符串或一个Buffer对象。
在Node.js中,使用分块加密法进行加密。分块加密法将固定长度的数据块或纯文本数据加密成长度相同的密码块数据。该转换的前提是用户提供密码。在解密时,要使用相同的密码对密码块数据进行逆转换。此处的“固定长度”称为数据块的尺寸。根据使用算法的不同,数据块的尺寸也各不相同。例如,在使用blowfish算法时,数据块的尺寸为40字节。分块加密法可以在网站被攻击时防止加密数据以及加密时所使用的密码泄漏。
- 在创建了一个cipher对象后,可以通过使用该对象的update方法来指定需要被加密的数据。该方法的使用方式如下所示(代码中的cipher代表一个cipher对象)。
cipher.update(data,[input_encoding],[output_encoding])
data:一个Buffer对象或一个字符串,用于指定需要加密的数据input_encoding:可选,指定被加密的数据所需使用的编码格式,可指定参数值为“utf8”、“ascii”及“binary”。如果不使用input_encoding参数,则data参数值必须为一个Buffer对象。output_encoding:可选,指定输出加密数据时使用的编码格式,可指定参数值为“hex”、“binary”或“base64”。
update方法返回被加密的数据。可以使用update方法多次以添加需要加密的数据。如果在update方法中使用output_encoding参数,则该方法返回被编码的字符串,如果在update方法中不使用output_encoding参数,则该方法返回一个存放了加密数据的Buffer对象。
与hash对象及hmac对象的update方法不同的是,cipher对象的update方法总是返回一个被分块的加密数据,因此块的大小是非常关键的。如果加密数据的字节数足够创建一个或多个块,update方法将返回被加密的数据。如果加密数据的字节数不足以创建一个块,加密数据将被缓存在cipher对象中。
- 使用cipher对象的final方法来返回加密数据。当该方法被调用时,任何cipher对象中所缓存的数据都将被加密,如果加密数据的字节数不足以创建一个块,将使用PKCS填充方式来填充这个块。在使用了cipher对象的final方法后,不能再向cipher对象中追加加密数据。final方法的使用方式如下所示(代码中的cipher代表一个cipher对象)。
cipher.final([output_encoding])
output_encoding:一个字符串,用于指定在输出加密数据的编码格式,可指定参数值为“hex”、“binary”及“base64”。如果使用了该参数,那么final方法返回字符串格式的加密数据,如果不使用该参数,那么final方法返回一个Buffer对象。当cipher对象的final方法被调用之后,该对象不能再被使用。
示例:使用cipher对象加密“test”字符串,加密算法为blowfish,密码从应用程序根目录下的key.pem私钥中读出,在完成数据加密后,在控制台中输出加密数据的hex编码格式字符串。
const crypto = require('crypto');
const fs = require('fs');
let pem = fs.readFileSync('key.pem');
let key = pem.toString('ascii');
let cipher = crypto.createCipheriv('blowfish', key, Buffer.from('12345678'));
let text = "test";
cipher.update(text,'binary','hex');
let crypted = cipher.final('hex')
console.log(crypted);
44e0d7b155a3a89f
4.2、Decipher解密
- 在crypto模块中,Decipher类用于对加密后的数据进行解密操作。在解密数据之前,首先需要创建一个decipher对象。可以通过如下所示的两种方法创建decipher对象。
createDecipher方法(不推荐):该方法使用指定的算法与密码来创建decipher对象。该方法的使用方式如下所示。
crypto.createDecipher(algorithm,password)
algorithm:指定在解密数据时所使用的算法,例如“blowfish”、“aes-256-cbc”等,该算法必须与加密该数据时所使用的算法保持一致。password:password参数用于指定解密时所使用的密码,其参数值必须为一个二进制格式的字符串或一个Buffer对象,该密码必须与加密该数据时所使用的密码保持一致。
createDecipheriv方法(推荐)*:该方法使用指定的算法、密码与初始向量来创建decipher对象。该方法的使用方式如下所示。
crypto.createDecipheriv(algorithm,password,iv)
algorithm:指定在解密数据时所使用的算法,例如“blowfish”、“aes-256-cbc”等,该算法必须与加密该数据时所使用的算法保持一致password:指定解密时所使用的密码,参数值必须为一个二进制格式的字符串或一个Buffer对象,该密码必须与加密该数据时所使用的密码保持一致iv:指定解密时所使用的初始向量,参数值必须为一个二进制格式的字符串或一个Buffer对象,该初始向量必须与加密该数据时所使用的初始向量保持一致。
- 在创建了一个decipher对象之后,可以通过使用该对象的update方法来指定需要被解密的数据。该方法的使用方式如下所示
decipher.update(data,[input_encoding],[output_encoding])
data:一个Buffer对象或一个字符串,用于指定需要被解密的数据input_encoding:可选,指定被解密的数据所使用的编码格式,可指定参数值为“hex”、“binary”及“base64”。如果不使用input_encoding参数,则data参数值必须为一个Buffer对象output_encoding:可选,指定输出解密数据时使用的编码格式,可指定参数值为“utf8”、“ascii”或“binary”
update方法返回被解密的数据。可以使用多次update方法来添加需要被解密的数据。如果在update方法中使用output_encoding参数,该方法返回被编码的字符串,如果在update方法中不使用output_encoding参数,该方法返回一个存放了解密数据的Buffer对象。
- 使用decipher对象的final方法来返回经过解密之后的原始数据。final方法的使用方式如下所示(代码中的decipher代表一个decipher对象)。
decipher.final([output_encoding])
output_encoding:可选,一个字符串,用于指定在输出解密数据时使用的编码格式,可指定参数值为“utf8”、“ascii”或“binary”。如果使用了该参数,final方法返回字符串格式的解密数据,如果不使用该参数,final方法返回一个Buffer对象。在decipher对象的final方法被调用之后,该对象不能再被使用。
示例:将经过加密后的数据进行解密,并且在控制台中输出经过解密操作后原始数据的utf8格式的字符串。
const crypto = require('crypto');
const fs = require('fs');
let pem = fs.readFileSync('key.pem');
let key = pem.toString('ascii');
let cipher = crypto.createCipheriv('blowfish', key, Buffer.from('12345678'));
let text = "test";
cipher.update(text,'binary','hex');
let crypted = cipher.final('hex')
console.log(crypted);
//解密
let decipher = crypto.createDecipheriv('blowfish', key, Buffer.from('12345678'));
let dec = decipher.update(crypted, 'hex', 'utf8');
dec += decipher.final('utf8');
console.log(dec);
44e0d7b155a3a89f test
4.3、Sign签名
在网络中,私钥的拥有者可以在一段数据被发送之前先对该数据进行签名操作,在签名的过程中,将对这段数据执行加密处理。在经过加密后的数据发送之后,数据的接收者可以通过公钥的使用来对该签名进行解密及验证操作,以确保这段数据是私钥的拥有者所发出的原始数据,且在网络的传输过程中未被修改。
- 在Node.js中,在进行签名操作之前,首先需要使用createSign方法创建一个sign对象,该方法的使用方式如下所示。
crypto.createSign(algorithm)
algorithm:指定在加密该数据时所使用的算法,例如“RSA-SHA256”。
- 在创建了一个sign对象后,可以通过使用该对象的update方法来指定需要被加密的数据。该方法的使用方式如下所示
sign.update(data)
data:一个Buffer对象或一个字符串,用于指定需要被加密的数据。可以在对数据进行签名前使用多次update方法来添加数据。
- 使用sign对象的sign方法对数据进行签名。在使用了sign对象的sign方法之后,不能再使用sign对象的update方法追加数据。sign方法的使用方式如下所示
sign.sign(private_key,[output_format])
private_key:一个字符串,用于指定PEM格式的私钥output_format:可选,指定签名输出时所使用的编码格式,可指定参数值为“hex”、“binary”或“base64”。如果使用了该参数,sign方法返回字符串格式的签名内容,如果不使用该参数,sign方法返回一个Buffer对象。当sign对象的sign方法被调用之后,该对象不能再被使用。
示例:使用sign对象对“test”字符串进行签名,加密算法为RSA-SHA256,私钥从应用程序根目录下的key.pem文件中读出,在数据签名完毕后,在控制台中输出数据签名的hex编码格式字符串。
const crypto = require('crypto');
const fs = require('fs');
let pem = fs.readFileSync('key.pem');
let key = pem.toString('ascii');
let sign = crypto.createSign('RSA-SHA256');
sign.update('test');
console.log(sign.sign(key,'hex'));
812da8dae8ec5b09f010d81c06afd2a3dc766801ea466f46763d31a98d04d7f5f55a2a0e7ef66a60551495ddbfde73c5ffc9d3822257a25195c115d2f41753d9685988653ddb0036adb753f8eff3ea0ec2248c32d5e19824b5d3dc2ed74bbb922b5b5457c243fd25b5399e061def56e788f9a4ec759ff4f764f95d0487954737
4.4、Verify验签
- 在crypto模块中,Verify类用于对签名进行验证操作。在对签名进行验证之前,首先需要创建一个verify对象,可以通过createVerify方法创建verify对象,该方法的使用方式如下所示。
crypto.createVerify(algorithm)
algorithm:指定在验证签名数据时所使用的算法,例如“RSA-SHA256”,该算法必须与加密该数据时所使用的算法保持一致。
- 通过使用该对象的update方法来指定需要被验证的数据。该方法的使用方式如下所示
verify.update(data)
data:一个Buffer对象或一个字符串,用于指定需要验证的数据。可以使用多次update方法来添加需要验证的数据。
- 使用verify对象的verify方法来对签名进行验证。verify方法的使用方式如下所示
verify.verify(object,signature,[signature_format])
object:指定验证时所使用的对象,参数值为一个字符串,该字符串值可以为一个RSA公钥、一个DSA公钥或一个X.509证书signature:必须为sign对象,用于指定被验证的签名signature_format:可选,指定在生成该签名时所使用的编码格式,可指定参数值为“hex”、“binary”及“base64”
示例:
使用sign对象对“test”字符串进行签名,加密算法为RSA-SHA256,私钥从应用程序根目录下的key.pem文件中读出,在数据签名完毕后,创建一个verify对象,指定验证数据为“test”字符串,使用从应用程序根目录下的cert.pem公钥文件中读取的公钥进行验证,最后在控制台中输出验证结果。
const crypto = require('crypto');
const fs = require('fs');
//私钥
let privatePem = fs.readFileSync('key.pem');
//包含公钥的证书
let publicPem = fs.readFileSync('cert.pem');
let key = privatePem.toString();
let pubkey = publicPem.toString();
let data = "test"
let sign = crypto.createSign('RSA-SHA256');
sign.update(data);
//签名
let sig = sign.sign(key, 'hex');
//验签
let verify = crypto.createVerify('RSA-SHA256');
verify.update(data);
console.log(verify.verify(pubkey, sig, 'hex'));
true
总结
以上为个人经验,希望能给大家一个参考,也希望大家多多支持脚本之家。
相关文章
Node.js学习之查询字符串解析querystring详解
这篇文章主要给大家介绍了关于Node.js查询字符串解析querystring的相关资料,文中通过示例代码介绍的非常详细,对大家学习或者使用node.js具有一定的参考学习价值,需要的朋友们下面来一起看看吧。2017-09-09


最新评论