哈希(不可逆)
import { createHash } from 'crypto';
const hash = createHash('sha256').update('hello').digest('hex');
console.log(hash); // 2cf24dba5fb0a30e26e83b2ac5b9e29e1b161e5c1fa7425e73043362938b9824
常用算法:sha256 / sha512 / md5(已不安全)/ sha3-256。
// 哈希文件
import { createHash } from 'crypto';
import { createReadStream } from 'fs';
async function hashFile(path) {
const hash = createHash('sha256');
for await (const chunk of createReadStream(path)) {
hash.update(chunk);
}
return hash.digest('hex');
}
console.log(await hashFile('big.iso'));
密码哈希(不用 sha256!)
⚠ 存密码必须用专门密码哈希函数——sha256 / bcrypt / argon2:
// 推荐:argon2
import argon2 from 'argon2';
const hash = await argon2.hash('mypassword'); // 存数据库
const ok = await argon2.verify(hash, 'mypassword'); // 验证
或 bcrypt:
import bcrypt from 'bcrypt';
const hash = await bcrypt.hash('mypassword', 12); // 12 = rounds
const ok = await bcrypt.compare('mypassword', hash);
⚠ 绝对不能存明文密码——任何拖库事故都会让你上头条。
HMAC(带 key 的哈希,常用于签名)
import { createHmac } from 'crypto';
const sig = createHmac('sha256', 'secret-key')
.update('important data')
.digest('hex');
console.log(sig);
应用:webhook 签名验证、API 签名、JWT。
随机数(密码学安全)
import { randomBytes, randomUUID, randomInt } from 'crypto';
// 字节
const bytes = randomBytes(32); // Buffer
const hex = randomBytes(32).toString('hex'); // 64 字符
const b64 = randomBytes(24).toString('base64url');
// UUID v4
const id = randomUUID(); // '550e8400-e29b-41d4-a716-446655440000'
// 整数
const n = randomInt(0, 100); // 0-99
⚠ Math.random() 不是密码学安全的——生成 token / API key / session ID 必须用 crypto.randomBytes / randomUUID。
对称加密(AES)
import { randomBytes, createCipheriv, createDecipheriv } from 'crypto';
// 加密
const key = randomBytes(32); // 256 位 key
const iv = randomBytes(16); // 初始化向量
const cipher = createCipheriv('aes-256-gcm', key, iv);
const encrypted = Buffer.concat([cipher.update('secret message', 'utf8'), cipher.final()]);
const tag = cipher.getAuthTag(); // GCM 模式的认证标签
// 解密
const decipher = createDecipheriv('aes-256-gcm', key, iv);
decipher.setAuthTag(tag);
const decrypted = Buffer.concat([decipher.update(encrypted), decipher.final()]).toString('utf8');
⚠ 新代码用 AES-GCM(含认证)—— CBC / ECB 模式不带认证,容易被篡改。
非对称(RSA / ECDSA)
import { generateKeyPairSync, publicEncrypt, privateDecrypt } from 'crypto';
// 生成 key pair
const { publicKey, privateKey } = generateKeyPairSync('rsa', {
modulusLength: 2048,
publicKeyEncoding: { type: 'spki', format: 'pem' },
privateKeyEncoding: { type: 'pkcs8', format: 'pem' },
});
// 公钥加密
const encrypted = publicEncrypt(publicKey, Buffer.from('secret'));
// 私钥解密
const decrypted = privateDecrypt(privateKey, encrypted);
console.log(decrypted.toString());
应用:JWT 签名、TLS 证书。
JWT(实战中常用库)
import jwt from 'jsonwebtoken';
const token = jwt.sign({ userId: 42 }, 'secret', { expiresIn: '1h' });
try {
const decoded = jwt.verify(token, 'secret');
console.log(decoded.userId);
} catch (err) {
console.error('Invalid token');
}
用 RS256(非对称)比 HS256 安全——签名用私钥,验证用公钥:
jwt.sign({ id: 42 }, privateKey, { algorithm: 'RS256' });
jwt.verify(token, publicKey, { algorithms: ['RS256'] });
实战:webhook 签名验证
GitHub / Stripe / 支付平台发 webhook 通常带签名。验证:
import { createHmac, timingSafeEqual } from 'crypto';
function verify(payload, signature, secret) {
const expected = createHmac('sha256', secret)
.update(payload)
.digest('hex');
// ★ 用 timingSafeEqual 防止时序攻击
return timingSafeEqual(
Buffer.from(expected, 'hex'),
Buffer.from(signature, 'hex')
);
}
⚠ 比较密文 / 签名永远用 timingSafeEqual——普通 === 容易被时序攻击。
坑
- 密码用 argon2 / bcrypt,别用 sha / md5
- Math.random 不安全——token 用 crypto
- AES 必须 GCM 模式(带认证),避免 CBC / ECB
- 比较密文用
timingSafeEqual,别 === - 密钥永远不要 commit 到 git——见
linux/29-secrets
至此核心 API 10 篇完成。下一篇起进入 Web 框架。