转自廖雪峰的博客。javascript
crypto模块的目的是为了提供通用的加密和哈希算法。用纯JavaScript代码实现这些功能不是不可能,但速度会很是慢。Nodejs用C/C++实现这些算法后,经过cypto这个模块暴露为JavaScript接口,这样用起来方便,运行速度也快。java
MD5是一种经常使用的哈希算法,用于给任意数据一个“签名”。这个签名一般用一个十六进制的字符串表示:node
const crypto = require('crypto'); const hash = crypto.createHash('md5'); // 可任意屡次调用update(): hash.update('Hello, world!'); hash.update('Hello, nodejs!'); console.log(hash.digest('hex')); // 7e1977739c748beac0c0fd14fd26a544
update()
方法默认字符串编码为UTF-8
,也能够传入Buffer。算法
若是要计算SHA1,只须要把'md5'
改为'sha1'
,就能够获得SHA1的结果1f32b9c9932c02227819a4151feed43e131aca40
。安全
还可使用更安全的sha256
和sha512
。服务器
Hmac算法也是一种哈希算法,它能够利用MD5或SHA1等哈希算法。不一样的是,Hmac还须要一个密钥:函数
const crypto = require('crypto'); const hmac = crypto.createHmac('sha256', 'secret-key'); hmac.update('Hello, world!'); hmac.update('Hello, nodejs!'); console.log(hmac.digest('hex')); // 80f7e22570...
只要密钥发生了变化,那么一样的输入数据也会获得不一样的签名,所以,能够把Hmac理解为用随机数“加强”的哈希算法。测试
AES是一种经常使用的对称加密算法,加解密都用同一个密钥。crypto模块提供了AES支持,可是须要本身封装好函数,便于使用:ui
const 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; } 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 = aesEncrypt(data, key); var decrypted = aesDecrypt(encrypted, key); console.log('Plain text: ' + data); console.log('Encrypted text: ' + encrypted); console.log('Decrypted text: ' + decrypted);
运行结果以下:this
Plain text: Hello, this is a secret message! Encrypted text: 8a944d97bdabc157a5b7a40cb180e7... Decrypted text: 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格式。
DH算法是一种密钥交换协议,它可让双方在不泄漏密钥的状况下协商出一个密钥来。DH算法基于数学原理,好比小明和小红想要协商一个密钥,能够这么作:
小明先选一个素数和一个底数,例如,素数p=23
,底数g=5
(底数能够任选),再选择一个秘密整数a=6
,计算A=g^a mod p=8
,而后大声告诉小红:p=23,g=5,A=8
;
小红收到小明发来的p
,g
,A
后,也选一个秘密整数b=15
,而后计算B=g^b mod p=19
,并大声告诉小明:B=19
;
小明本身计算出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算法以下:
const crypto = require('crypto'); // xiaoming's keys: var ming = crypto.createDiffieHellman(512); var ming_keys = ming.generateKeys(); var prime = ming.getPrime(); var generator = ming.getGenerator(); console.log('Prime: ' + prime.toString('hex')); 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); // print secret: console.log('Secret of Xiao Ming: ' + ming_secret.toString('hex')); console.log('Secret of Xiao Hong: ' + hong_secret.toString('hex'));
运行后,能够获得以下输出:
$ node dh.js Prime: a8224c...deead3 Generator: 02 Secret of Xiao Ming: 695308...d519be Secret of Xiao Hong: 695308...d519be
注意每次输出都不同,由于素数的选择是随机的。
crypto模块也能够处理数字证书。数字证书一般用在SSL链接,也就是Web的https链接。通常状况下,https链接只须要处理服务器端的单向认证,如无特殊需求(例如本身做为Root给客户发认证证书),建议用反向代理服务器如Nginx等Web服务器去处理证书。