Hash 算法
查看 crypto 模块支持的 hash 函数:crypto.getHashes()
[ 'RSA-MD4',
'RSA-MD5',
'RSA-MDC2',
'RSA-RIPEMD160',
'RSA-SHA1',
'RSA-SHA1-2',
'RSA-SHA224',
'RSA-SHA256',
'RSA-SHA384',
'RSA-SHA512',
'blake2b512',
'blake2s256',
'md4',
'md4WithRSAEncryption',
'md5',
'md5-sha1',
'md5WithRSAEncryption',
'mdc2',
'mdc2WithRSA',
'ripemd',
'ripemd160',
'ripemd160WithRSA',
'rmd160',
'sha1',
'sha1WithRSAEncryption',
'sha224',
'sha224WithRSAEncryption',
'sha256',
'sha256WithRSAEncryption',
'sha384',
'sha384WithRSAEncryption',
'sha512',
'sha512WithRSAEncryption',
'ssl3-md5',
'ssl3-sha1',
'whirlpool' ]
SHA-256
示例:使用 hash.update()
和 hash.digest()
:
const crypto = require('crypto');
const hash = crypto.createHash('sha256'); // 创建哈希函数 sha256
hash.update('some data to hash', 'utf8');
console.log(hash.digest('hex'));
// 输出
// 6a2da20943931e9834fc12cfe5bb47bbd9ae43489a30726962b576f4e3993e50
PBKDF2
PBKDF2 是 Node.js 的 crypto 模块原生支持的标准方法。
const crypto = require('crypto');
function pbkdf2_encrypt(username, password) {
// crypto.randomBytes()方法生成 32 字节的随机数 - 这里作为盐值
crypto.randomBytes(32, (err, salt) => {
if (err) throw err;
// 参数列表:(密码,盐值,迭代次数,密钥长度,摘要函数)
crypto.pbkdf2(password, salt, 4096, 512, 'sha256', (err, key) => {
if (err) throw err;
// 将用户名、密码哈希值和盐值存入数据库
console.log(username, key.toString('hex'), salt.toString('hex'));
});
});
}
pbkdf2_encrypt('zhangSan', '123456');
同步函数示例:
const salt = crypto.randomBytes(32);
const result = crypto.pbkdf2Sync(password, salt, 4096, 512, 'sha256');
Hmac 算法
const crypto = require('crypto');
const key = 'BDvDYUmfdykkBLgX';
const hmac = crypto.createHmac('sha1', key.toString('ascii'));
hmac.update('data to crypt');
console.log(hmac.digest('hex'));
// 输出
// a43bfb9f12b6f69ad9fcd4338a981efbed2569ae
加盐(salt)
盐值就是随机数值,用于在计算密码的哈希值时加强数据的安全性,可以有效抵御诸如字典攻击、彩虹表攻击等密码攻击媒介。
常见的 Hash 算法使用示例:
const crypto = require('crypto');
const md5 = crypto.createHash('md5');
let password = '123456';
// 直接对密码原文进行 Hash
md5.update(password);
console.log(md5.digest('hex'));
// 输出
// e10adc3949ba59abbe56e057f20f883e
// 可以通过 <https://www.cmd5.com/> 反向查询得到密码,不够安全。
加 “盐” 的 Hash 算法:
const crypto = require('crypto');
const md5 = crypto.createHash('md5');
let password = '123456';
let salt = 'hhug6dcKyCNBQ5sUC0i6hja5dCTqdSzV'; // 盐值
// 将密码拼接上任意长度的随机字符串后,再进行 Hash
md5.update(password+salt);
console.log(md5.digest('hex'));
// 输出
// b2d23b0443c319e574f1ea3f8bddc6e0
crypto.randomBytes()
方法可以生成随机数,用于作为密钥或者盐值:
const crypto = require('crypto');
// crypto.randomBytes() 生成强加密的伪随机数
crypto.randomBytes(16, (err, buf) => {
if (err) throw err;
console.log(`${buf.length} bytes of random data: ${buf.toString('hex')}`);
});
// 打印
// 16 bytes of random data: 411fd0a79188f2fde58a91ea0302f148
使用 Node crypto 模块为哈希函数生成随机的盐值:
const crypto = require('crypto');
// 1. 异步方法
crypto.randomBytes(32, (err, salt) => {
if (err) throw err;
// 记录盐值可读的字符串版本
console.log('salt: ' + salt.toString('hex'));
// 后续步骤:使用盐值
});
// 2. 同步方法
const buf = crypto.randomBytes(256);
加密(Cipher)和解密(Decipher)算法
对称加密算法
查看 Openssl 对称加密算法列表:
$ openssl list -cipher-algorithms
$ openssl list-cipher-algorithms # 旧版本命令
AES-256-GCM 示例 1:
'use strict';
const crypto = require('crypto');
// 初始化参数
const text = 'Encryption Testing AES GCM mode'; // 要加密和解密的数据
const key = crypto.randomBytes(32); // 256 位的共享密钥
const iv = crypto.randomBytes(16); // 初始向量,16 字节
const algorithm = 'aes-256-gcm'; // 加密算法和操作模式
// 加密
const cipher = crypto.createCipheriv(algorithm, key, iv); // 初始化加密算法
let encrypted = cipher.update(text, 'utf8', 'hex');
encrypted += cipher.final('hex');
const tag = cipher.getAuthTag(); // 生成标签,用于验证密文的来源
// 解密
const decipher = crypto.createDecipheriv(algorithm, key, iv); // 初始化解密算法
decipher.setAuthTag(tag); // 传入验证标签,验证密文的来源
let decrypted = decipher.update(encrypted, 'hex', 'utf8');
decrypted += decipher.final('utf8');
console.log(decrypted); // Encryption Testing AES GCM mode
AES-256-GCM 示例 2:
- Github - crypto-aes-256-gem-demo.js
- GitHub: AES 简介
'use strict';
const crypto = require('crypto');
const aes256gcm = key => {
const ALGO = 'aes-256-gcm';
// encrypt returns base64-encoded ciphertext
const encrypt = str => {
// Hint: the 'iv' should be unique (but not necessarily random).
// 'randomBytes' here are (relatively) slow but convenient for demonstration
const iv = new Buffer.from(crypto.randomBytes(16), 'utf8');
const cipher = crypto.createCipheriv(ALGO, key, iv);
// Hint: Larger inputs (it's GCM, after all!) should use the stream API
let enc = cipher.update(str, 'utf8', 'base64');
enc += cipher.final('base64');
return [ enc, iv, cipher.getAuthTag() ];
};
// decrypt decodes base64-encoded ciphertext into a utf8-encoded string
const decrpt = (enc, iv, authTag) => {
const decipher = crypto.createDecipheriv(ALGO, key, iv);
decipher.setAuthTag(authTag);
let str = decipher.update(enc, 'base64', 'utf8');
str += decipher.final('utf8');
return str;
};
return { encrypt, decrpt };
};
// 加密密钥
// Note: a key size of 16 bytes will use AES-128, 24 => AES-192, 32 => AES-256
const KEY = new Buffer.from(crypto.randomBytes(32), 'utf8');
const aesCipher = aes256gcm(KEY);
// 加密
const [ encrypted, iv, authTag ] = aesCipher.encrypt('hello world');
// 解密
const decrypted = aesCipher.decrpt(encrypted, iv, authTag);
console.log(decrypted); // hello world
非对称加密算法的使用,1. 公钥加密 - 私钥解密;2. 私钥加密 - 公钥解密。
crypto.publicEncrypt(key, buffer)
crypto.publicDecrypt(key, buffer)
crypto.privateEncrypt(privateKey, buffer)
crypto.privateDecrypt(privateKey, buffer)
使用 RSA 2048 进行公钥加密,私钥解密
使用 OpenSSL 生成 2048 位 RSA 密钥:
# 1.生成 2048位 RSA 私钥
$ openssl genrsa -out rsa_private_key.pem 2048
# 2. 通过私钥生成公钥
$ openssl rsa -in rsa_private_key.pem -pubout -out rsa_public_key.pem
使用公钥加密数据,再使用私钥解密数据:
const crypto = require('crypto');
const fs = require('fs');
const path = require('path');
const root = __dirname;
const publicKey = fs.readFileSync(path.join(root, './rsa_public_key.pem')).toString('ascii');
const privateKey = fs.readFileSync(path.join(root, './rsa_private_key.pem')).toString('ascii');
const data = 'data to crypt';
// 公钥加密
const encryptData = crypto.publicEncrypt(publicKey, Buffer.from(data)).toString('base64');
console.log('encode', encryptData);
// 私钥解密
const decryptData = crypto.privateDecrypt(privateKey, Buffer.from(encryptData.toString('base64'), 'base64'));
console.log('decode', decryptData.toString());
签名(Sign)和验证(Verify)算法
签名算法示例:
const crypto = require('crypto');
const fs = require('fs');
const path = require('path');
const root = __dirname;
// 加载私钥文件
const pem = fs.readFileSync(path.join(root, './rsa_private_key.pem'));
const key = pem.toString('ascii');
const sign = crypto.createSign('RSA-SHA256'); // 创建签名算法
sign.update('data to sign'); // 更新待签名内容
const result = sign.sign(key, 'hex'); // 生成并返回签名
console.log('result:', result);
// 打印
// 3a26e0c3ec5ad1f517aad27f0f3a48cc0db156c2221800e4c2f995220f62a9077b4507a72ac6b0f1f50ee72386191d5ada4013945ff2afdb1ea6aa4e8d51a94f186089a67c2d6f4236150d59a1db134286fd7ba8deab0fc28f97eba7ad35820113f24dc6e30c6d11f0773527c4d81582c2b42016c9f972b5f4809e6d1f9115c2fb6db3168c68af3cfca986a458e3ea1c15606e7d33ee0bf5400ca460cf5371a51e800abe7ac943f16d9546239949bf7eec77488212d815b6a013e68fc2f456c4f2eb9d38c551e3f94168b076c3f761341afa3b724d8e73f486aacbace5a09412ef16c9b88182fcaeaaf97d96fdb402832530708a915d34c42131d7a26e4f04d4
验证算法示例:
const crypto = require('crypto');
const fs = require('fs');
const path = require('path');
const root = __dirname;
// 加载公钥文件
const publicKey = fs.readFileSync(path.join(root, './rsa_public_key.pem')).toString('ascii');
const verify = crypto.createVerify('RSA-SHA256'); // 创建验证算法
verify.update('data to sign');
console.log(verify.verify(publicKey, result, 'hex'));
// 打印
// true
参考
Node.js 的加密模块 crypto 之使用 Hash 类计算数据的哈希值
Node.js 的加密模块 crypto 之使用 Hmac 类计算哈希密钥
Node.js 的加密模块 crypto 之使用 Cipher 类加密数据
Node.js 的加密模块 crypto 之使用 Decipher 类解密数据
Node.js 的加密模块 crypto 之使用 Sign 类生成数字签名并使用 Verify 类验证数字签名
Node.js 的加密模块 crypto 之使用 DiffieHellman 类生成交换密钥
Node.js 的加密模块 crypto 之使用 ECDH 类生成 EC Diffie-Hellman 交换密钥
Identity and Data Security for Web DevelopmentReport abuse
Node.js 使用 RSA 加密 / 解密
相关第三方库
- bcrypt—— 密码哈希函数
- bcrypt.js
- node-rsa
- node-forge