
密码学实战
对称加密(AES-256-GCM)
import crypto from 'crypto'
const ALGORITHM = 'aes-256-gcm'
const KEY_LENGTH = 32 // 256 bits
// Generate a secure key
function generateKey(): Buffer {
return crypto.randomBytes(KEY_LENGTH)
}
function encrypt(plaintext: string, key: Buffer): {
ciphertext: string
iv: string
tag: string
} {
const iv = crypto.randomBytes(12) // 96-bit IV for GCM
const cipher = crypto.createCipheriv(ALGORITHM, key, iv)
const encrypted = Buffer.concat([
cipher.update(plaintext, 'utf8'),
cipher.final(),
])
return {
ciphertext: encrypted.toString('base64'),
iv: iv.toString('base64'),
tag: cipher.getAuthTag().toString('base64'),
}
}
function decrypt(
ciphertext: string,
iv: string,
tag: string,
key: Buffer
): string {
const decipher = crypto.createDecipheriv(
ALGORITHM,
key,
Buffer.from(iv, 'base64')
)
decipher.setAuthTag(Buffer.from(tag, 'base64'))
const decrypted = Buffer.concat([
decipher.update(Buffer.from(ciphertext, 'base64')),
decipher.final(),
])
return decrypted.toString('utf8')
}
// Usage
const key = generateKey()
const encrypted = encrypt('Sensitive data', key)
const decrypted = decrypt(encrypted.ciphertext, encrypted.iv, encrypted.tag, key)

密码哈希(Argon2)
import argon2 from 'argon2'
const HASH_OPTIONS = {
type: argon2.argon2id,
memoryCost: 65536, // 64MB
timeCost: 3, // iterations
parallelism: 4, // threads
saltLength: 32,
}
async function hashPassword(password: string): Promise<string> {
return argon2.hash(password, HASH_OPTIONS)
}
async function verifyPassword(hash: string, password: string): Promise<boolean> {
try {
return await argon2.verify(hash, password)
} catch {
return false
}
}
// Upgrade hash if needed (rehash on login)
async function loginUser(password: string, storedHash: string): Promise<boolean> {
const valid = await verifyPassword(storedHash, password)
if (!valid) return false
// Check if hash needs upgrade
if (argon2.needsRehash(storedHash, HASH_OPTIONS)) {
const newHash = await hashPassword(password)
await db.update('users', { password_hash: newHash }, { id: userId })
}
return true
}

从密码派生密钥(PBKDF2)
import os
import hashlib
import hmac
from base64 import b64encode, b64decode
def derive_key(password: str, salt: bytes = None, iterations: int = 600000) -> tuple:
if salt is None:
salt = os.urandom(32)
key = hashlib.pbkdf2_hmac(
'sha256',
password.encode('utf-8'),
salt,
iterations,
dklen=32, # 256-bit key
)
return key, salt
# Encrypt with derived key
def encrypt_with_password(plaintext: str, password: str) -> dict:
from cryptography.hazmat.primitives.ciphers.aead import AESGCM
key, salt = derive_key(password)
nonce = os.urandom(12)
aesgcm = AESGCM(key)
ciphertext = aesgcm.encrypt(nonce, plaintext.encode(), None)
return {
'ciphertext': b64encode(ciphertext).decode(),
'nonce': b64encode(nonce).decode(),
'salt': b64encode(salt).decode(),
'iterations': 600000,
}

RSA 密钥对操作
import crypto from 'crypto'
const { privateKey, publicKey } = crypto.generateKeyPairSync('rsa', {
modulusLength: 2048,
publicKeyEncoding: { type: 'spki', format: 'pem' },
privateKeyEncoding: { type: 'pkcs8', format: 'pem', cipher: 'aes-256-cbc', passphrase: 'mypassphrase' },
})
// Sign data
function sign(data: string, privKey: string): string {
return crypto.createSign('SHA256')
.update(data)
.sign({ key: privKey, passphrase: 'mypassphrase' }, 'base64')
}
// Verify signature
function verify(data: string, signature: string, pubKey: string): boolean {
return crypto.createVerify('SHA256')
.update(data)
.verify(pubKey, signature, 'base64')
}
// Encrypt with public key (for key exchange only, not bulk data)
function rsaEncrypt(data: string, pubKey: string): string {
return crypto.publicEncrypt(
{ key: pubKey, padding: crypto.constants.RSA_PKCS1_OAEP_PADDING, oaepHash: 'sha256' },
Buffer.from(data)
).toString('base64')
}
常见密码学错误
| 错误 |
影响 |
修复 |
| ECB 模式 |
模式可见 |
使用 GCM 或带 HMAC 的 CBC |
| 重用 IV/nonce |
灾难性 |
每条消息生成随机 IV |
| 使用 MD5/SHA1 处理密码 |
可暴力破解 |
使用 Argon2/bcrypt |
| 自定义加密 |
严重 |
仅使用标准库 |
| 在代码中存储密钥 |
密钥泄露 |
使用 Vault/KMS |
| 密钥过短 |
可暴力破解 |
AES-256, RSA-2048+ |