node.JS的crypto加密模块使用方法详解(MD5,AES,Hmac,Diffie-Hellman加密)


Posted in Javascript onFebruary 06, 2020

node.JS的加密模块crypto提供了 HTTP 或 HTTPS 连接过程中封装安全凭证的方法。也提供了 OpenSSL 的哈希,hmac, 加密(cipher), 解密(decipher), 签名(sign) 和 验证(verify) 方法的封装

crypto模块使用方法

crypto.setEngine(engine[, flags])

为某些/所有 OpenSSL 函数加载并设置引擎(根据参数 flags 来设置)。

engine 可能是 id,或者是指向引擎共享库的路径。

flags是可选参数,默认值是ENGINE_METHOD_ALL ,可以是以下一个或多个参数的组合(在constants里定义)

ENGINE_METHOD_RSA

ENGINE_METHOD_DSA

ENGINE_METHOD_DH

ENGINE_METHOD_RAND

ENGINE_METHOD_ECDH

ENGINE_METHOD_ECDSA

ENGINE_METHOD_CIPHERS

ENGINE_METHOD_DIGESTS

ENGINE_METHOD_STORE

ENGINE_METHOD_PKEY_METH

ENGINE_METHOD_PKEY_ASN1_METH

ENGINE_METHOD_ALL

ENGINE_METHOD_NONE

crypto.getCiphers()

返回支持的加密算法名数组

var crypto = require('crypto');
console.log(crypto.getCiphers());
//[ 'aes-128-cbc', 'aes-128-ccm', 'aes-128-cfb', 'aes-128-cfb1', 'aes-128-cfb8', 'aes-128-ctr', 'aes-128-ecb', 'aes-128-gcm', '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-ofb', 'aes-256-cbc', 'aes-256-ccm', 'aes-256-cfb', 'aes-256-cfb1', 'aes-256-cfb8', 'aes-256-ctr', 'aes-256-ecb', 'aes-256-gcm', 'aes-256-ofb', 'aes-256-xts', 'aes128', 'aes192', 'aes256', 'bf', 'bf-cbc', 'bf-cfb', 'bf-ecb', 'bf-ofb', 'blowfish', 'camellia-128-cbc', 'camellia-128-cfb', 'camellia-128-cfb1', 'camellia-128-cfb8', 'camellia-128-ecb', 'camellia-128-ofb', 'camellia-192-cbc', 'camellia-192-cfb', 'camellia-192-cfb1', 'camellia-192-cfb8', 'camellia-192-ecb', 'camellia-192-ofb', 'camellia-256-cbc', 'camellia-256-cfb', 'camellia-256-cfb1', 'camellia-256-cfb8', 'camellia-256-ecb', 'camellia-256-ofb', 'camellia128', 'camellia192', 'camellia256', 'cast', 'cast-cbc', 'cast5-cbc', 'cast5-cfb', 'cast5-ecb', 'cast5-ofb', 'des', 'des-cbc', 'des-cfb', 'des-cfb1', 'des-cfb8', 'des-ecb', 'des-ede', 'des-ede-cbc', 'des-ede-cfb', 'des-ede-ofb', 'des-ede3', 'des-ede3-cbc', 'des-ede3-cfb', 'des-ede3-cfb1', 'des-ede3-cfb8', 'des-ede3-ofb', 'des-ofb', 'des3', 'desx', 'desx-cbc', 'id-aes128-CCM', 'id-aes128-GCM', 'id-aes128-wrap', 'id-aes192-CCM', 'id-aes192-GCM', 'id-aes192-wrap', 'id-aes256-CCM', 'id-aes256-GCM', 'id-aes256-wrap', 'id-smime-alg-CMS3DESwrap', 'idea', 'idea-cbc', 'idea-cfb', 'idea-ecb', 'idea-ofb', ... 15 more items ]

crypto.getCiphers()

返回支持的哈希算法名数组。

var crypto = require('crypto');
console.log(crypto.getHashes());
//[ 'DSA', 'DSA-SHA', 'DSA-SHA1', 'DSA-SHA1-old', 'RSA-MD4', 'RSA-MD5', 'RSA-MDC2', 'RSA-RIPEMD160', 'RSA-SHA', 'RSA-SHA1', 'RSA-SHA1-2', 'RSA-SHA224', 'RSA-SHA256', 'RSA-SHA384', 'RSA-SHA512', 'dsaEncryption', 'dsaWithSHA', 'dsaWithSHA1', 'dss1', 'ecdsa-with-SHA1', 'md4', 'md4WithRSAEncryption', 'md5', 'md5WithRSAEncryption', 'mdc2', 'mdc2WithRSA', 'ripemd', 'ripemd160', 'ripemd160WithRSA', 'rmd160', 'sha', 'sha1', 'sha1WithRSAEncryption', 'sha224', 'sha224WithRSAEncryption', 'sha256', 'sha256WithRSAEncryption', 'sha384', 'sha384WithRSAEncryption', 'sha512', 'sha512WithRSAEncryption', 'shaWithRSAEncryption', 'ssl2-md5', 'ssl3-md5', 'ssl3-sha1', 'whirlpool' ]

crypto.getCurves()

返回支持的椭圆曲线名数组。

var crypto = require('crypto');
console.log(crypto.getCurves());
//[ 'Oakley-EC2N-3', 'Oakley-EC2N-4', 'brainpoolP160r1', 'brainpoolP160t1', 'brainpoolP192r1', 'brainpoolP192t1', 'brainpoolP224r1', 'brainpoolP224t1', 'brainpoolP256r1', 'brainpoolP256t1', 'brainpoolP320r1', 'brainpoolP320t1', 'brainpoolP384r1', 'brainpoolP384t1', 'brainpoolP512r1', 'brainpoolP512t1', 'c2pnb163v1', 'c2pnb163v2', 'c2pnb163v3', 'c2pnb176v1', 'c2pnb208w1', 'c2pnb272w1', 'c2pnb304w1', 'c2pnb368w1', 'c2tnb191v1', 'c2tnb191v2', 'c2tnb191v3', 'c2tnb239v1', 'c2tnb239v2', 'c2tnb239v3', 'c2tnb359v1', 'c2tnb431r1', 'prime192v1', 'prime192v2', 'prime192v3', 'prime239v1', 'prime239v2', 'prime239v3', 'prime256v1', 'secp112r1', 'secp112r2', 'secp128r1', 'secp128r2', 'secp160k1', 'secp160r1', 'secp160r2', 'secp192k1', 'secp224k1', 'secp224r1', 'secp256k1', 'secp384r1', 'secp521r1', 'sect113r1', 'sect113r2', 'sect131r1', 'sect131r2', 'sect163k1', 'sect163r1', 'sect163r2', 'sect193r1', 'sect193r2', 'sect233k1', 'sect233r1', 'sect239k1', 'sect283k1', 'sect283r1', 'sect409k1', 'sect409r1', 'sect571k1', 'sect571r1', 'wap-wsg-idm-ecid-wtls1', 'wap-wsg-idm-ecid-wtls10', 'wap-wsg-idm-ecid-wtls11', 'wap-wsg-idm-ecid-wtls12', 'wap-wsg-idm-ecid-wtls3', 'wap-wsg-idm-ecid-wtls4', 'wap-wsg-idm-ecid-wtls5', 'wap-wsg-idm-ecid-wtls6', 'wap-wsg-idm-ecid-wtls7', 'wap-wsg-idm-ecid-wtls8', 'wap-wsg-idm-ecid-wtls9' ]

crypto的MD5加密

MD5是一种常用的哈希算法,用于给任意数据一个“签名”。这个签名通常用一个十六进制的字符串表示:

crypto.createHash(algorithm)

创建并返回一个哈希对象,使用指定的算法来生成哈希摘要。

参数 algorithm 取决于平台上 OpenSSL 版本所支持的算法。例如,'sha1', 'md5', 'sha256', 'sha512' 等等

hash.update(data[, input_encoding])

根据 data 来更新哈希内容,编码方式根据 input_encoding 来定,有 'utf8', 'ascii' 或 'binary'。如果没有传入值,默认编码方式是'utf8'。如果 data 是 Buffer, input_encoding 将会被忽略。

因为它是流式数据,所以可以使用不同的数据调用很多次。

hash.digest([encoding])

计算传入的数据的哈希摘要。encoding 可以是 'hex', 'binary' 或 'base64',如果没有指定encoding ,将返回 buffer。

[注意]调用 digest() 后不能再用 hash 对象。

var crypto = require('crypto');

var hash = crypto.createHash('md5');

// 可任意多次调用update():

hash.update('Hello, world!');

hash.update('Hello, nodejs!');

console.log(hash.digest('hex')); // 7e1977739c748beac0c0fd14fd26a544

crypto的Hmac加密

Hmac算法也是一种哈希算法,它可以利用MD5或SHA1等哈希算法。不同的是,Hmac还需要一个密钥:

crypto.createHmac(algorithm, key)

创建并返回一个 hmac 对象,用指定的算法和秘钥生成 hmac 图谱。

它是可读写的流 stream 。写入的数据来用计算 hmac。当写入流结束后,使用 read() 方法来获取计算后的值。也支持老的 update 和 digest 方法。

参数 algorithm 取决于平台上 OpenSSL 版本所支持的算法,参见前面的 createHash。key是 hmac 算法中用的 key

hmac.update(data)

根据 data 更新 hmac 对象。因为它是流式数据,所以可以使用新数据调用多次。

hmac.digest([encoding])

计算传入数据的 hmac 值。encoding可以是 'hex', 'binary' 或 'base64',如果没有指定encoding ,将返回 buffer。

[注意]调用 digest() 后不能再用 hmac 对象

var crypto = require('crypto');

var hmac = crypto.createHmac('sha256', 'match');

hmac.update('Hello, world!');

hmac.update('Hello, nodejs!');

//e82a58066cae2fae4f44e58be1d589b66a5d102c2e8846d796607f02a88c1649

console.log(hmac.digest('hex'));

crypto的AES加密

AES是一种常用的对称加密算法,加解密都用同一个密钥。crypto模块提供了AES支持,但是需要自己封装好函数,便于使用:

crypto.createCipher(algorithm, password)

使用传入的算法和秘钥来生成并返回加密对象。

algorithm 取决于 OpenSSL,例如'aes192'等。password 用来派生 key 和 IV,它必须是一个'binary' 编码的字符串或者一个buffer。

它是可读写的流 stream 。写入的数据来用计算 hmac。当写入流结束后,使用 read() 方法来获取计算后的值。也支持老的update 和 digest 方法。

cipher.update(data[, input_encoding][, output_encoding])

根据 data 来更新哈希内容,编码方式根据 input_encoding 来定,有 'utf8', 'ascii' or 'binary'。如果没有传入值,默认编码方式是'binary'。如果data 是 Buffer,input_encoding 将会被忽略。

output_encoding 指定了输出的加密数据的编码格式,它可用是 'binary', 'base64' 或 'hex'。如果没有提供编码,将返回 buffer 。

返回加密后的内容,因为它是流式数据,所以可以使用不同的数据调用很多次。

cipher.final([output_encoding])

返回加密后的内容,编码方式是由 output_encoding 指定,可以是 'binary', 'base64' 或 'hex'。如果没有传入值,将返回 buffer。

[注意]cipher 对象不能在 final() 方法之后调用。

var crypto = require('crypto');
function aesEncrypt(data, key) {
  const cipher = crypto.createCipher('aes192', key);
  var crypted = cipher.update(data, 'utf8', 'hex');
  crypted += cipher.final('hex');
  return crypted;
}
var data = 'Hello, this is a secret message!';
var key = 'Password!';
var encrypted = aesEncrypt(data, key);
//8a944d97bdabc157a5b7a40cb180e713f901d2eb454220d6aaa1984831e17231f87799ef334e3825123658c80e0e5d0c
console.log(encrypted);

crypto.createDecipher(algorithm, password)

根据传入的算法和密钥,创建并返回一个解密对象。这是 createCipher() 的镜像

decipher.update(data[, input_encoding][, output_encoding])

使用参数 data 更新需要解密的内容,其编码方式是 'binary','base64' 或 'hex'。如果没有指定编码方式,则把 data 当成 buffer 对象。

如果 data 是 Buffer,则忽略 input_encoding 参数。

参数 output_decoding 指定返回文本的格式,是 'binary', 'ascii' 或 'utf8' 之一。如果没有提供编码格式,则返回 buffer。

decipher.final([output_encoding])

返回剩余的解密过的内容,参数 output_encoding 是 'binary', 'ascii' 或 'utf8',如果没有指定编码方式,返回 buffer。

[注意]decipher对象不能在 final() 方法之后使用。

var crypto = require('crypto');
function aesDecrypt(encrypted, key) {
  const decipher = crypto.createDecipher('aes192', key);
  var decrypted = decipher.update(encrypted, 'hex', 'utf8');
  decrypted += decipher.final('utf8');
  return decrypted;
}
var data = 'Hello, this is a secret message!';
var key = 'Password!';
var encrypted = '8a944d97bdabc157a5b7a40cb180e713f901d2eb454220d6aaa1984831e17231f87799ef334e3825123658c80e0e5d0c';
var decrypted = aesDecrypt(encrypted, key);
console.log(decrypted);//Hello, this is a secret message!

可以看出,加密后的字符串通过解密又得到了原始内容。

注意到AES有很多不同的算法,如aes192,aes-128-ecb,aes-256-cbc等,AES除了密钥外还可以指定IV(Initial Vector),不同的系统只要IV不同,用相同的密钥加密相同的数据得到的加密结果也是不同的。

加密结果通常有两种表示方法:hex和base64,这些功能Nodejs全部都支持,但是在应用中要注意,如果加解密双方一方用Nodejs,另一方用Java、PHP等其它语言,需要仔细测试。

如果无法正确解密,要确认双方是否遵循同样的AES算法,字符串密钥和IV是否相同,加密后的数据是否统一为hex或base64格式

crypto.createCipheriv(algorithm, key, iv)

创建并返回一个加密对象,用指定的算法,key 和 iv。

algorithm 参数和 createCipher() 一致。key 在算法中用到.iv 是一个initialization vector.

key 和 iv 必须是 'binary' 的编码字符串或buffers.

crypto.createDecipheriv(algorithm, key, iv)

根据传入的算法,密钥和 iv,创建并返回一个解密对象。这是 createCipheriv() 的镜像。

const crypto = require('crypto');
function aesEncryptiv(data, key,iv) {
  const cipher = crypto.createCipher('aes192', key, iv);
  var crypted = cipher.update(data, 'utf8', 'hex');
  crypted += cipher.final('hex');
  return crypted;
}
function aesDecryptiv(encrypted, key,iv) {
  const decipher = crypto.createDecipher('aes192', key, iv);
  var decrypted = decipher.update(encrypted, 'hex', 'utf8');
  decrypted += decipher.final('utf8');
  return decrypted;
}
var data = 'Hello, this is a secret message!';
var key = 'Password!';
var iv = 'match';
var encrypted = aesEncryptiv(data, key, iv);
var decrypted = aesDecryptiv(encrypted, key, iv);
//Hello, this is a secret message!
console.log(data);
//8a944d97bdabc157a5b7a40cb180e713f901d2eb454220d6aaa1984831e17231f87799ef334e3825123658c80e0e5d0c
console.log(encrypted);
//Hello, this is a secret message!
console.log(decrypted);

crypto Diffie-Hellman

crypto.createDiffieHellman(prime[, prime_encoding][, generator][, generator_encoding])

使用传入的 prime 和 generator 创建 Diffie-Hellman 秘钥交互对象。

generator 可以是数字,字符串或Buffer。如果没有指定 generator,使用 2

prime_encoding 和 generator_encoding 可以是 'binary', 'hex', 或 'base64'。

如果没有指定 prime_encoding, 则 Buffer 为 prime。如果没有指定 generator_encoding ,则 Buffer 为 generator。

diffieHellman.generateKeys([encoding])

生成秘钥和公钥,并返回指定格式的公钥。这个值必须传给其他部分。编码方式: 'binary', 'hex', 或 'base64'。如果没有指定编码方式,将返回 buffer。

diffieHellman.getPrime([encoding])

用参数 encoding 指明的编码方式返回 Diffie-Hellman 质数,编码方式为: 'binary', 'hex', 或 'base64'。 如果没有指定编码方式,将返回 buffer。

diffieHellman.getGenerator([encoding])

用参数 encoding 指明的编码方式返回 Diffie-Hellman 生成器,编码方式为: 'binary', 'hex', 或 'base64'. 如果没有指定编码方式 ,将返回 buffer。

diffieHellman.computeSecret(other_public_key[, input_encoding][, output_encoding])

使用 other_public_key 作为第三方公钥来计算并返回共享秘密(shared secret)。秘钥用input_encoding 编码。编码方式为:'binary', 'hex', 或 'base64'。如果没有指定编码方式 ,默认为 buffer。

如果没有指定返回编码方式,将返回 buffer。

DH算法

DH算法是一种密钥交换协议,它可以让双方在不泄漏密钥的情况下协商出一个密钥来。DH算法基于数学原理,比如小明和小红想要协商一个密钥,可以这么做:

1、小明先选一个素数和一个底数,例如,素数p=23,底数g=5(底数可以任选),再选择一个秘密整数a=6,计算A=g^a mod p=8,然后大声告诉小红:p=23,g=5,A=8;

2、小红收到小明发来的p,g,A后,也选一个秘密整数b=15,然后计算B=g^b mod p=19,并大声告诉小明:B=19;

3、小明自己计算出s=B^a mod p=2,小红也自己计算出s=A^b mod p=2,因此,最终协商的密钥s为2。

在这个过程中,密钥2并不是小明告诉小红的,也不是小红告诉小明的,而是双方协商计算出来的。第三方只能知道p=23,g=5,A=8,B=19,由于不知道双方选的秘密整数a=6和b=15,因此无法计算出密钥2。

用crypto模块实现DH算法如下:

var crypto = require('crypto');
// xiaoming's keys:
var ming = crypto.createDiffieHellman(512);
var ming_keys = ming.generateKeys();
var prime = ming.getPrime();
var generator = ming.getGenerator();
//Prime: 8df777257625c66821af697652f28e93af05b9f779af919111b89816faa11c36fcf9df04c76811471a6099800213c4fe8e3fbec8d2f90bd00795e4b7fd241603
console.log('Prime: ' + prime.toString('hex'));
//Generator: 02
console.log('Generator: ' + generator.toString('hex'));
// xiaohong's keys:
var hong = crypto.createDiffieHellman(prime, generator);
var hong_keys = hong.generateKeys();
// exchange and generate secret:
var ming_secret = ming.computeSecret(hong_keys);
var hong_secret = hong.computeSecret(ming_keys);
//Secret of Xiao Ming: 4237157ab4c9211f78ffdb67d127d749cec91780d594b81a7e75f1fb591fecb84f33ae6591e1edda4bc9685b503010fe8f9928c6ed69e4ff9fdb44adb9ba1539
console.log('Secret of Xiao Ming: ' + ming_secret.toString('hex'));
//Secret of Xiao Hong: 4237157ab4c9211f78ffdb67d127d749cec91780d594b81a7e75f1fb591fecb84f33ae6591e1edda4bc9685b503010fe8f9928c6ed69e4ff9fdb44adb9ba1539
console.log('Secret of Xiao Hong: ' + hong_secret.toString('hex'))

[注意]每次输出都不一样,因为素数的选择是随机的。

更多关于node.JS加密方法请点击下面的相关链接

Javascript 相关文章推荐
js类的静态属性和实例属性的理解
Oct 01 Javascript
javascript与asp.net(c#)互相调用方法
Dec 13 Javascript
通过DOM脚本去设置样式信息
Sep 19 Javascript
纯JS实现动态时间显示代码
Feb 08 Javascript
JS自调用匿名函数具体实现
Feb 11 Javascript
JavaScript转换二进制编码为ASCII码的方法
Apr 16 Javascript
jQuery的事件委托实例分析
Jul 15 Javascript
JS实现的简洁纵向滑动菜单(滑动门)效果
Oct 19 Javascript
JavaScript正则表达式的分组匹配详解
Feb 13 Javascript
解决jQuery ajax请求在IE6中莫名中断的问题
Jun 20 Javascript
JS 实现随机验证码功能
Feb 15 Javascript
Vue项目部署在Spring Boot出现页面空白问题的解决方案
Nov 26 Javascript
node.js开发辅助工具nodemon安装与配置详解
Feb 06 #Javascript
Vue表单控件数据绑定方法详解
Feb 05 #Javascript
Vue过渡效果之CSS过渡详解(结合transition,animation,animate.css)
Feb 05 #Javascript
vue 翻页组件vue-flip-page效果
Feb 05 #Javascript
在博客园博文中添加自定义右键菜单的方法详解
Feb 05 #Javascript
Vue中多元素过渡特效的解决方案
Feb 05 #Javascript
Vue路由管理器Vue-router的使用方法详解
Feb 05 #Javascript
You might like
PHP的一个完美GIF等比缩放类,附带去除缩放黑背景
2014/04/01 PHP
windows下配置php5.5开发环境及开发扩展
2014/12/25 PHP
php实现汉字验证码和算式验证码的方法
2015/03/07 PHP
PHP编程基本语法快速入门手册
2016/01/07 PHP
CentOS7.0下安装PHP5.6.30服务的教程详解
2018/09/29 PHP
JavaScript While 循环基础教程
2007/04/05 Javascript
javascript 日期常用的方法
2009/11/11 Javascript
JavaScript 笔记二 Array和Date对象方法
2010/05/22 Javascript
关于js datetime的那点事
2011/11/15 Javascript
javascript当中的代码嗅探扩展原生对象和原型(prototype)
2013/01/11 Javascript
javascript禁制后退键(Backspace)实例代码
2013/11/15 Javascript
JS实现的打字机效果完整实例
2016/06/20 Javascript
JS图片左右无缝隙滚动的实现(兼容IE,Firefox 遵循W3C标准)
2016/09/23 Javascript
Dropzone.js实现文件拖拽上传功能(附源码下载)
2016/11/22 Javascript
react路由配置方式详解
2017/08/07 Javascript
jQuery-ui插件sortable实现自由拖动排序
2018/12/01 jQuery
深入理解令牌认证机制(token)
2019/08/22 Javascript
JavaScript 实现拖拽效果组件功能(兼容移动端)
2020/11/11 Javascript
[02:02]特效爆炸!DOTA2珍宝之瓶待你开启
2018/08/21 DOTA
python制作花瓣网美女图片爬虫
2015/10/28 Python
Python3学习urllib的使用方法示例
2017/11/29 Python
python smtplib模块自动收发邮件功能(一)
2018/05/22 Python
Python实现二叉树前序、中序、后序及层次遍历示例代码
2019/05/18 Python
python可视化爬虫界面之天气查询
2019/07/03 Python
Python的matplotlib绘图如何修改背景颜色的实现
2019/07/16 Python
Django使用unittest模块进行单元测试过程解析
2019/08/02 Python
python3 深浅copy对比详解
2019/08/12 Python
Pytorch通过保存为ONNX模型转TensorRT5的实现
2020/05/25 Python
python怎么提高计算速度
2020/06/11 Python
Groupon荷兰官方网站:高达70%的折扣
2019/11/01 全球购物
怎样写好自荐信和推荐信
2013/12/26 职场文书
小学教师评语大全
2014/04/23 职场文书
大专毕业生求职信
2014/07/05 职场文书
2016应届大学生自荐信模板
2016/01/28 职场文书
Golang 1.18 多模块Multi-Module工作区模式的新特性
2022/04/11 Golang
SQL Server一个字符串拆分多行显示或者多行数据合并成一个字符串
2022/05/25 SQL Server