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 相关文章推荐
在Javascript中为String对象添加trim,ltrim,rtrim方法
Sep 22 Javascript
javascript String 对象
Apr 25 Javascript
JavaScript Undefined,Null类型和NaN值区别
Oct 22 Javascript
jquery中的$(document).ready()与window.onload的区别
Nov 18 Javascript
再次分享18个非常棒的jQuery表格插件
Apr 10 Javascript
JS 对象属性相关(检查属性、枚举属性等)
Apr 05 Javascript
jQuery实现的小图列表,大图展示效果幻灯片示例
Oct 25 Javascript
微信小程序 开发之全局配置
May 05 Javascript
jQuery制作全屏宽度固定高度轮播图(实例讲解)
Jul 08 jQuery
Nuxt.js之自动路由原理的实现方法
Nov 21 Javascript
微信小程序下拉加载和上拉刷新两种实现方法详解
Sep 05 Javascript
浅谈vue中document.getElementById()拿到的是原值的问题
Jul 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的网址
2006/11/25 PHP
yii实现级联下拉菜单的方法
2014/07/31 PHP
PHP lcfirst()函数定义与用法
2019/03/08 PHP
setInterval 和 setTimeout会产生内存溢出
2008/02/15 Javascript
很可爱的输入框
2008/08/03 Javascript
js select常用操作控制代码
2010/03/16 Javascript
node.js中的events.EventEmitter.listenerCount方法使用说明
2014/12/08 Javascript
再谈Javascript中的异步以及如何异步
2016/08/19 Javascript
实例分析nodejs模块xml2js解析xml过程中遇到的坑
2017/03/18 NodeJs
jq源码解析之绑在$,jQuery上面的方法(实例讲解)
2017/10/13 jQuery
js实现通过开始结束控制的计时器
2019/02/25 Javascript
[02:42]DOTA2英雄基础教程 杰奇洛
2013/12/23 DOTA
python实现dict版图遍历示例
2014/02/19 Python
python使用cPickle模块序列化实例
2014/09/25 Python
详解python 发送邮件实例代码
2016/12/22 Python
Python cookbook(数据结构与算法)在字典中将键映射到多个值上的方法
2018/02/18 Python
idea创建springMVC框架和配置小文件的教程图解
2018/09/18 Python
Python列表常见操作详解(获取,增加,删除,修改,排序等)
2019/02/18 Python
Django框架使用mysql视图操作示例
2019/05/15 Python
python+Django+pycharm+mysql 搭建首个web项目详解
2019/11/29 Python
Python进程Multiprocessing模块原理解析
2020/02/28 Python
python是怎么被发明的
2020/06/15 Python
Python filter()及reduce()函数使用方法解析
2020/09/05 Python
CSS超出文本指定宽度用省略号代替和文本不换行
2016/05/05 HTML / CSS
西班牙拥有最佳品牌的动物商店:Animalear.com
2018/01/05 全球购物
JSF面试题:如何管量web层中的Bean,用什么标签。如何通过jsp页面与Bean绑定在一起进行处理?
2012/10/05 面试题
如何查找和删除数据库中的重复数据
2014/11/05 面试题
Oracle里面常用的数据字典有哪些
2014/02/14 面试题
职业培训师职业生涯规划
2014/02/18 职场文书
公司保密承诺书
2014/03/27 职场文书
《最佳路径》教学反思
2014/04/13 职场文书
员工试用期转正自我评价
2015/03/10 职场文书
英语演讲开场白
2015/05/29 职场文书
2016形势与政策学习心得体会
2016/01/12 职场文书
vue项目多环境配置(.env)的实现
2021/07/21 Vue.js
源码安装apache脚本部署过程详解
2022/09/23 Servers