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 相关文章推荐
在标题栏显示新消息提示,很多公司项目中用到这个方法
Nov 04 Javascript
jCallout 轻松实现气泡提示功能
Sep 22 Javascript
JavaScript如何从listbox里同时删除多个项目
Oct 12 Javascript
jquery实现网页查找功能示例分享
Feb 12 Javascript
jQuery插件EnPlaceholder实现输入框提示文字
Jun 05 Javascript
js贪吃蛇网页版游戏特效代码分享(挑战十关)
Aug 24 Javascript
JS实现兼容各种浏览器的高级拖动方法完整实例【测试可用】
Jun 21 Javascript
详解vue-Resource(与后端数据交互)
Jan 16 Javascript
分分钟学会vue中vuex的应用(入门教程)
Sep 14 Javascript
vue中axios处理http发送请求的示例(Post和get)
Oct 13 Javascript
通过vue-router懒加载解决首次加载时资源过多导致的速度缓慢问题
Apr 08 Javascript
详解angular分页插件tm.pagination二次触发问题解决方案
Jul 20 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将任何格式视频转为flv的代码
2009/09/03 PHP
ThinkPHP、ZF2、Yaf、Laravel框架路由大比拼
2015/03/25 PHP
php好代码风格的阶段性总结
2016/06/25 PHP
thinkPHP框架实现类似java过滤器的简单方法示例
2018/09/05 PHP
showModelessDialog()使用详解
2006/09/07 Javascript
基础的prototype.js常用函数及其用法
2007/03/10 Javascript
不用构造函数(Constructor)new关键字也能实现JavaScript的面向对象
2013/01/11 Javascript
jquery获得keycode的示例代码
2013/12/30 Javascript
js中生成map对象的方法
2014/01/09 Javascript
jQuery中用dom操作替代正则表达式
2014/12/29 Javascript
js实时获取并显示当前时间的方法
2015/07/31 Javascript
jQuery实现ctrl+enter(回车)提交表单
2015/10/19 Javascript
使用基于Node.js的构建工具Grunt来发布ASP.NET MVC项目
2016/02/15 Javascript
Node.js环境下编写爬虫爬取维基百科内容的实例分享
2016/06/12 Javascript
基于JS实现弹出一个隐藏的div窗口body页面变成灰色并且不可被编辑
2016/12/14 Javascript
JavaScript 程序错误Cannot use 'in' operator to search的解决方法
2017/07/10 Javascript
微信小程序下拉刷新界面的实现
2017/09/28 Javascript
Popup弹出框添加数据实现方法
2017/10/27 Javascript
vue中实现滚动加载更多的示例
2017/11/08 Javascript
javascript全局自定义鼠标右键菜单
2020/12/08 Javascript
js实现简单的轮播图效果
2020/12/13 Javascript
自动化Nginx服务器的反向代理的配置方法
2015/06/28 Python
通过数据库向Django模型添加字段的示例
2015/07/21 Python
Python的自动化部署模块Fabric的安装及使用指南
2016/01/19 Python
Python3 执行Linux Bash命令的方法
2019/07/12 Python
简单了解python变量的作用域
2019/07/30 Python
jupyter notebook快速入门及使用详解
2020/11/13 Python
意大利拉斐尔时尚购物网:Raffaello Network(支持中文)
2018/11/09 全球购物
单位办理社保介绍信
2014/01/10 职场文书
电子商务专业求职信
2014/03/08 职场文书
四则混合运算教学反思
2016/02/23 职场文书
干货:我将这样书写我的演讲稿!
2019/05/09 职场文书
python3.7.2 tkinter entry框限定输入数字的操作
2021/05/22 Python
Python编程super应用场景及示例解析
2021/10/05 Python
Apache Kafka 分区重分配的实现原理解析
2022/07/15 Servers