哈希(不可逆)

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 框架。