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鼠标滑过弹出层的定位IE6bug解决办法
Dec 26 Javascript
深入分析原生JavaScript事件
Dec 29 Javascript
javascript中callee与caller的区别分析
Apr 20 Javascript
jQuery模拟黑客帝国矩阵效果实例
Jun 28 Javascript
JavaScript实现瀑布流布局
Jun 28 Javascript
jQuery 表单序列化实例代码
Jun 11 jQuery
react-native DatePicker日期选择组件的实现代码
Sep 12 Javascript
JS的函数调用栈stack size的计算方法
Jun 24 Javascript
微信小程序中为什么使用var that=this
Aug 27 Javascript
Vue 自定义指令实现一键 Copy功能
Sep 16 Javascript
JavaScript实现秒杀时钟倒计时
Sep 29 Javascript
vue 添加和编辑用同一个表单,el-form表单提交后清空表单数据操作
Aug 03 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 数组实例说明
2008/08/18 PHP
PHP屏蔽蜘蛛访问代码及常用搜索引擎的HTTP_USER_AGENT
2013/03/06 PHP
基于PHP遍历数组的方法汇总分析
2013/06/08 PHP
php中smarty变量修饰用法实例分析
2015/06/11 PHP
PHP汉字转换拼音的函数代码
2015/12/30 PHP
jquery imgareaselect 使用利用js与程序结合实现图片剪切
2009/07/30 Javascript
javaScript 删除字符串空格多种方法小结
2012/10/24 Javascript
js实现单击图片放大图片的方法
2015/02/17 Javascript
学习Bootstrap组件之下拉菜单
2015/07/28 Javascript
AngularJS通过ng-route实现基本的路由功能实例详解
2016/12/13 Javascript
JS正则匹配URL网址的方法(可匹配www,http开头的一切网址)
2017/01/06 Javascript
用nodeJS搭建本地文件服务器的几种方法小结
2017/03/16 NodeJs
JavaScript数据结构学习之数组、栈与队列
2017/05/02 Javascript
jquery自定义显示消息数量
2017/12/19 jQuery
全面介绍vue 全家桶和项目实例
2017/12/27 Javascript
JS实现的透明度渐变动画效果示例
2018/04/28 Javascript
Bootstrap table表格初始化表格数据的方法
2018/07/25 Javascript
浅谈ElementUI中switch回调函数change的参数问题
2018/08/24 Javascript
你不知道的Vue技巧之--开发一个可以通过方法调用的组件(推荐)
2019/04/15 Javascript
vue中封装axios并实现api接口的统一管理
2020/12/25 Vue.js
[01:16:16]DOTA2-DPC中国联赛定级赛 RNG vs Phoenix BO3第二场 1月8日
2021/03/11 DOTA
python 读写、创建 文件的方法(必看)
2016/09/12 Python
python,Java,JavaScript实现indexOf
2020/09/09 Python
Python os库常用操作代码汇总
2020/11/03 Python
CSS3 实现的加载动画
2020/12/07 HTML / CSS
html5指南-6.如何创建离线web应用程序实现离线访问
2013/01/07 HTML / CSS
梅西百货官网:Macy’s
2020/08/04 全球购物
你所在的项目是如何确定版本号的
2015/12/28 面试题
生物技术专业研究生自荐信
2013/09/22 职场文书
升职自荐信
2013/11/28 职场文书
微博营销计划书
2014/01/10 职场文书
开学典礼策划方案
2014/05/28 职场文书
教师求职自荐信范文
2015/03/04 职场文书
被告代理词范文
2015/05/25 职场文书
新闻稿怎么写
2015/07/18 职场文书
拔河比赛队名及霸气口号
2015/12/24 职场文书