正在加载,请稍候…

密码学实战:加密、哈希与密钥管理

在 Node.js 和 Python 中正确实现密码学,学习 AES 加密、RSA、bcrypt 哈希、PBKDF2/Argon2 密钥派生及常见密码学陷阱。

密码学实战:加密、哈希与密钥管理

密码学实战

对称加密(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+