关键词:Node.js Crypto、哈希算法(MD5/SHA1/Bcrypt)、Hmac、AES、RSA、Diffie-Hellman 密钥交换、JavaScript 加密
为什么用 Node.js 做加密?
浏览器端可以用 WebCrypto API,但在服务端处理 数据完整性校验、密码存储 或者 Token 加密 时,Node.js 自带的 node:crypto 模块仍然是最高效、最灵活的选择。它底层用 C/C++ 实现,暴露了简洁的 JavaScript 接口,既不会拖慢服务器性能,又能让你用几行代码完成复杂的安全逻辑。
1. 哈希算法:MD5 / SHA 家族
哈希算法会“压扁”任意长度的输入,输出固定长度的 十六进制摘要。只要 1 bit 有变,整个结果面目全非。
import crypto from 'node:crypto';
const hash = crypto.createHash('sha256') // 或 'md5'、'sha512'
.update('Hello, Node.js!')
.digest('hex');
console.log(hash); // e868...常见场景
- 文件完整性校验:比对前后哈希值。
- 密码存储:避免明文保存,但请继续往下看 再决定是否用 MD5。
✅ 进阶提示
MD5 已被彩虹表击穿,生产环境建议使用 bcrypt 或 Argon2。如果仅限于校验文件完整性,sha256是目前最通用且运行速度适中的选择。
2. Hmac:给哈希加一把钥匙
在 JWT Token、支付回调签名校验 等场景里,只靠哈希是不够的;还要用一把只有前后端知道的密钥(Secret)重新算一次摘要,这就是 Hmac。
const hmac = crypto
.createHmac('sha256', process.env.API_SECRET) // Secret 建议 32+ 字节,不硬编码
.update('orderId=123&amount=100')
.digest('hex');只要密钥变了,同一消息也得出相反的结果,解决 数据伪造 问题。
3. AES 对称加密:加解密同钥
与 HTTPS 使用的 SSL 不同,AES 是对称算法,加密、解密共用一个 key + IV。
import crypto from 'node:crypto';
function aesEncrypt(key, iv, msg) {
const cipher = crypto.createCipheriv('aes-256-cbc', key, iv);
return cipher.update(msg, 'utf8', 'hex') + cipher.final('hex');
}
function aesDecrypt(key, iv, encrypted) {
const decipher = crypto.createDecipheriv('aes-256-cbc', key, iv);
return decipher.update(encrypted, 'hex', 'utf8') + decipher.final('utf8');
}
const key = Buffer.alloc(32, 'Passw0rd'); // 32 字节
const iv = Buffer.alloc(16, 'Node.jsIV'); // 16 字节
const msg = 'Keep it secret, keep it safe';
const encrypted = aesEncrypt(key, iv, msg);
console.log(encrypted); // 7828...关键踩坑
- IV(Initial Vector)每加密一次必须更换,否则相同明文产生相同密文。
- 与 Java / PHP / Go 互操作时,统一输出格式(hex vs base64)与填充方式(PKCS#7)即可避免多种“能加密不能解密”的噩梦。
4. 非对称加密:Diffie-Hellman 密钥交换
作为 密钥协商(Key Agreement) 而非消息加密的方案,DH 可以让双方在不泄露私钥的情况下,算出共享的秘密 对称密钥。
import crypto from 'node:crypto';
// 甲方
const alice = crypto.createDiffieHellman(512);
const alicePublicKey = alice.generateKeys();
const prime = alice.getPrime();
const generator = alice.getGenerator();
// 乙方
const bob = crypto.createDiffieHellman(prime, generator);
const bobPublicKey = bob.generateKeys();
// 双方各自计算共享密钥
const aliceSecret = alice.computeSecret(bobPublicKey);
const bobSecret = bob.computeSecret(alicePublicKey);
console.log(aliceSecret.equals(bobSecret)); // true用这段共享密钥可直接做 AES 加解密,实现 双向安全信道。
5. RSA:非对称加解密的瑞士军刀
RSA 一对密钥(公钥可公开,私钥必须保密)广泛用于:
- 发放授权 Token(私钥签名,公钥验证)
- 加密对称密钥(公钥加密,私钥解密,缓解 AES 密钥分发难题)
5.1 生成密钥对
# 生成 2048 位私钥,用 AES256 套外壳
openssl genrsa -aes256 -out rsa-key.pem 2048
# 提取原始私钥
openssl rsa -in rsa-key.pem -out rsa-prv.pem
# 导出公钥
openssl rsa -in rsa-key.pem -pubout -out rsa-pub.pem5.2 实战:公钥加密、私钥解密
import fs from 'node:fs';
import crypto from 'node:crypto';
const pubPem = fs.readFileSync('./rsa-pub.pem', 'utf8');
const prvPem = fs.readFileSync('./rsa-prv.pem', 'utf8');
const msg = 'Top Secret';
const encrypted = crypto.publicEncrypt(pubPem, Buffer.from(msg));
const decrypted = crypto.privateDecrypt(prvPem, encrypted);
console.log(decrypted.toString()); // Top Secret注意
RSA 块大小上限 = 密钥长度/8 - 11 字节,2048 位最多能加密 245 字节数据。常见套路是:
- 服务器随机生成 AES Key
- 用 RSA 公钥把 AES Key 加密
- 用 AES Key 把大量数据加密
- 客户端拿到两部分——AES 密文 + RSA 加密后的 Key,顺序相反即可还原。
FAQ:Node.js Crypto 最常用的六个疑问
Q1:MD5 和 SHA1 还能用吗?
A:做 文件完整性校验 可以;做 密码哈希 基本禁止。推荐 bcrypt(自带 salt)、Argon2id(抗 GPU 暴力穷举)。
Q2:为什么 AES 需要 IV?
A:若没有随机 IV,同一明文 + 同一密钥会生成同一密文,攻击者可据此推断 数据规律 甚至实施重放攻击。
Q3:如何防止 AES 密钥在内存中被 dump?
A:可借助 @napi-rs/argon2 这类 C 拓展,把密钥存到进 C 堆而不是 V8 堆,降低被 Node.js 堆快照泄漏的风险。
Q4:Diffie-Hellman 的密钥长度怎么选?
A:许多行业规范建议 DH 至少 2048 位,等量 RSA 建议 3072 位开始。但如果你使用的是 ECDH(椭圆曲线),256 位就能达到类似安全水平且性能更优。
Q5:RSA 签名与加密有什么区别?
A:
- 签名:私钥加密摘要,公钥验证完整性 & 来源可信。
- 加密:公钥加密明文,私钥解密明文,保证只有私钥拥有者可见。
Q6:生产环境要用到 HTTPS,证书管理能用 Crypto 吗?
A:完全可加载 PEM 证书做 校验,但证书部署与刷新仍建议交给 Nginx / Traefik / Caddy 这类反向代理,省时省力;只有在写 私有 CA 或 IoT 设备端 TLS 通信 时才“深入” Crypto API。
小结与快速清单
| 任务 | 推荐做法 |
|---|---|
| 密码存储 | bcrypt 或 Argon2id |
| Token 签名 | Hmac + Secret |
| 通道对称加密 | AES-256-CBC + 随机 IV |
| 小数据非对称加密 | RSA-2048 / RSA-4096 |
| 密钥交换 | ECDH-256 |
把以上五类算法组合起来,你就能在 Node.js 环境里搭出可扩展、可审计、跨语言互操作的安全模块。祝你写代码也像马拉松一样,节奏稳健、一骑绝尘!