Node.js Crypto模块核心加密技术详解:MD5、AES、Hmac与Diffie-Hellman
本文详细解析Node.js中crypto模块的加密功能,涵盖MD5哈希、Hmac签名、AES对称加密及Diffie-Hellman密钥交换。通过具体代码示例,介绍createHash、createHmac、createCipher及createDiffieHellman等API的使用方法与注意事项,帮助开发者实现安全的数据传输与存储。
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参数。
由于哈希对象是流式的,因此可以多次调用update方法传入不同数据。
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),但其独特之处在于需要提供一个密钥:
crypto.createHmac(algorithm, key)
此方法创建并返回一个hmac对象,使用指定的算法和密钥生成Hmac摘要。
该对象是一个可读写的流(stream)。写入的数据用于计算Hmac,写入结束后,可通过read()方法获取计算结果。此外,也支持旧的update和digest方法。
参数algorithm取决于平台OpenSSL版本支持的算法,参考前文createHash部分。key是Hmac算法中使用的密钥。
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)。写入的数据用于计算加密内容,写入结束后,可通过read()方法获取结果。此外,也支持旧的update和digest方法。
cipher.update(data[, input_encoding][, output_encoding])
根据data更新加密内容,编码方式由input_encoding指定,可选值包括'utf8'、'ascii'或'binary'。若未传入该参数,默认编码为'binary'。如果data是Buffer类型,则忽略input_encoding参数。
output_encoding指定了输出加密数据的编码格式,可选值包括'binary'、'base64'或'hex'。若未提供编码,则返回Buffer对象。
返回加密后的内容。由于是流式数据,可以多次调用该方法传入不同数据。
cipher.final([output_encoding])
返回加密后的内容,编码方式由output_encoding指定,可选值包括'binary'、'base64'或'hex'。若未传入该参数,则返回Buffer对象。
[注意]在调用final()方法后,该cipher对象将不再可用。
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对象。
[注意]在调用final()方法后,该decipher对象将不再可用。
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(初始向量)。只要IV不同,即使使用相同的密钥加密相同的数据,得到的加密结果也会不同。
加密结果通常有两种表示方法:hex和base64,Node.js均支持这些功能。但在实际应用中需注意,如果加解密双方一方使用Node.js,另一方使用Java、PHP等其他语言,需要进行仔细测试以确保兼容性。
如果无法正确解密,应确认双方是否遵循相同的AES算法,字符串密钥和IV是否一致,以及加密后的数据是否统一为hex或base64格式。
crypto.createCipheriv(algorithm, key, iv)
创建并返回一个加密对象,使用指定的算法、key和iv。
algorithm参数与createCipher()一致。key在算法中使用。iv是一个初始化向量(initialization vector)。
key和iv必须是'binary'编码的字符串或Buffer。
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加密方法请点击下面的相关链接
macOS卸载Node.js指南:区分Homebrew与pkg安装包的处理方式
2026-11-05 11:28:12
JS逆向入门:浏览器Console调试与Node.js环境搭建指南
2026-11-05 10:57:15
Node.js 大版本升级需要注意哪些破坏性变更
2026-11-03 20:35:44
Node.js官网LTS版与Current版有什么区别
2026-11-03 19:33:49
Windows彻底卸载Node.js:清理AppData残留与PATH环境变量
2026-11-03 19:15:35
