正在加载,请稍候…

Cryptography in Practice: Encryption, Hashing, and Key Management

Implement cryptography correctly in Node.js and Python. Learn AES encryption, RSA, bcrypt hashing, key derivation with PBKDF2/Argon2, and common cryptographic pitfalls.

Cryptography in Practice

Symmetric Encryption (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)

Password Hashing (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
}

Key Derivation from Passwords (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 Key Pair Operations

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')
}

Common Cryptographic Mistakes

Mistake Impact Fix
ECB mode Patterns visible Use GCM or CBC with HMAC
Reusing IV/nonce Catastrophic Generate random IV per message
MD5/SHA1 for passwords Brute-forceable Use Argon2/bcrypt
Custom crypto Critical Use standard libraries only
Storing keys in code Key exposure Use Vault/KMS
Short keys Brute-force AES-256, RSA-2048+