🔑 对称 & 非对称加密

AES-GCM、RSA、椭圆曲线——每个 SaaS 都在用但多数人做错的密码学基础。
从 TLS 握手到 API 密钥存储,把原理搞清楚才能做出正确的架构选择。

🔐 对称加密:快、可靠、无处不在

对称加密使用同一个密钥进行加密和解密。它是现代密码学的基石——你每天上网产生的加密流量,99% 都是对称加密。

为什么对称加密是主流?

✅ 优势

  • 极快:AES-GCM 在现代 CPU 上可达 10+ Gbps(AES-NI 硬件加速)
  • 安全:AES-256 无已知实际攻击,NIST 标准化
  • 紧凑:密钥仅 128/192/256 位
  • AEAD:GCM 模式同时提供加密和认证

❌ 局限

  • 密钥分发问题:双方如何安全共享密钥?
  • 规模问题:N 个人两两通信需要 N(N-1)/2 个密钥
  • 无不可否认性:有密钥的人都能加密/解密

AES:高级加密标准

AES (Advanced Encryption Standard) 由 NIST 于 2001 年发布(FIPS 197),替代了已不安全的 DES。它是一种分组密码,固定块大小 128 位,支持 128/192/256 位密钥。

AES 变体密钥长度轮数安全级别典型用途
AES-128128 bit10足够安全(截至 2025 无已知实际攻击)TLS、磁盘加密
AES-192192 bit12高安全政府/军事
AES-256256 bit14极高安全,抗量子(Grover 算法下仍安全)金融、合规要求

AES 的工作模式

AES 本身只能加密 128 位的块。工作模式定义了如何处理超过 128 位的数据:

模式类型并行加密并行解密认证推荐度
GCMAEAD✅ GMAC⭐⭐⭐⭐⭐ 首选
CTR流式⭐⭐⭐ 可用但需配合 HMAC
CBC分组⭐⭐ 遗留系统
ECB分组❌ 禁止使用
AES-GCM 为什么是首选?GCM (Galois/Counter Mode) 是一种 AEAD (Authenticated Encryption with Associated Data) 模式——它同时提供机密性(加密)和完整性(认证)。用 GCM 时你不需要再单独加 HMAC。RFC 5288 定义了 TLS 中 AES-GCM 的使用规范,AES-GCM 在硬件实现中可达 10 Gbps 以上。

ChaCha20-Poly1305:AES-NI 不可用时的替代

Google 的 Adam Langley 设计了 ChaCha20-Poly1305 作为 AES-GCM 的替代方案。在移动设备(没有 AES-NI 指令集的 ARM 芯片)上,ChaCha20-Poly1305 比 AES-GCM 快得多。TLS 1.3 中两者都是必须支持的密码套件。

特性AES-256-GCMChaCha20-Poly1305
加密算法AES (分组密码)ChaCha20 (流密码)
认证GHASH (Galois Field)Poly1305 (MAC)
有 AES-NI 时极快(~10 Gbps)快(~5 Gbps)
无 AES-NI 时慢(~1 Gbps)极快(~5 Gbps)
nonce 重用后果灾难性(丢失认证+机密性)仅丢失认证
TLS 1.3 支持必须必须

🔓 非对称加密:公钥革命的产物

1976 年 Diffie 和 Hellman 的论文《New Directions in Cryptography》开创了公钥密码学。非对称加密使用一对密钥:公钥公开,私钥保密。它解决了对称加密的核心问题——密钥分发。

三大非对称加密体系

🔢 RSA

基础:大整数分解难题

密钥大小:2048/3072/4096 bit

速度:慢(加密/解密约 1ms 级)

用途:密钥交换、数字签名、证书

注意:RSA-2048 是当前最低安全要求,RSA-1024 已被破解

📐 椭圆曲线 (ECC)

基础:椭圆曲线离散对数难题 (ECDLP)

密钥大小:256/384/521 bit

速度:比 RSA 快得多

等效安全:256-bit ECC ≈ 3072-bit RSA

曲线:P-256 (NIST)、Curve25519 (Bernstein)、secp256k1 (Bitcoin)

🔮 后量子 (PQC)

基础:格密码 (Lattice)、编码、哈希

标准:CRYSTALS-Kyber (加密/KEM)、CRYSTALS-Dilithium (签名)

NIST 状态:2024 年正式标准化 (FIPS 203/204/205)

迁移:Google/Apple/Cloudflare 已开始混合部署

密钥长度安全等效对照表

安全级别 (bit)SymmetricRSAECC说明
80801024160⚠️ 已不安全
1121122048224⚠️ 最低可用
1281283072256✅ 当前标准
1921927680384✅ 高安全
25625615360521✅ 最高安全 / 抗量子
为什么 ECC 更好?同样 128-bit 安全级别,RSA 需要 3072 位密钥,ECC 只需 256 位。更短的密钥意味着更快的计算、更小的存储、更低的带宽。新项目应该优先使用 ECC(特别是 Curve25519/Ed25519)。

ECC 关键曲线详解

曲线设计者用途特点
Curve25519Daniel J. Bernstein密钥交换 (ECDH)常量时间实现、无侧信道、速度极快。TLS 1.3 默认
Ed25519Bernstein 等数字签名EdDSA 签名方案、确定性行为、无随机数失败风险
P-256NIST通用最广泛部署的 NIST 曲线、硬件加速好、但实现需注意侧信道
secp256k1CerticomBitcoin/EthereumKoblitz 曲线、特殊结构使计算更快、中本聪的选择
P-384 / P-521NIST高安全192/256-bit 安全级别、政府/军事用途

数字签名:非对称加密的核心应用

数字签名是"非对称加密的逆向使用"——用私钥签名,用公钥验证。它提供两个保证:

签名算法类型签名大小速度标准
RSA-PSSRSA256-512 字节PKCS#1 v2.2
ECDSA (P-256)ECC64 字节中等FIPS 186-4
Ed25519ECC (EdDSA)64 字节极快RFC 8032
Dilithium后量子~2.4 KB中等FIPS 204
Falcon后量子~1.3 KB中等FIPS 205

🤝 TLS 1.3 握手过程详解

TLS (Transport Layer Security) 是对称加密和非对称加密协作的完美范例:用非对称加密安全协商对称密钥,然后用对称加密传输数据。TLS 1.3 (RFC 8446) 大幅简化了握手流程。

TLS 1.3 完整握手流程

👤 Client ClientHello
TLS 1.3, 密码套件列表, KeyShare (ECDH 公钥), SNI
🖥️ Server
🖥️ Server ServerHello
选定密码套件, KeyShare (ECDH 公钥)
🔐 双方计算共享密钥 EncryptedExtensions
加密扩展
Certificate
服务器证书
CertificateVerify
签名验证
Finished
握手完成验证
👤 Client
👤 Client Finished
握手完成验证
📦 应用数据
AES-256-GCM 加密传输
🖥️ Server

TLS 1.3 vs TLS 1.2 关键改进

特性TLS 1.2TLS 1.3
握手 RTT2 RTT1 RTT
0-RTT 恢复不支持支持(PSK 模式)
密码套件37+ 种(很多不安全)5 种(全部 AEAD)
密钥交换静态 RSA 或 DHE仅 (EC)DHE(前向保密强制)
加密算法AES-CBC, RC4 等AES-GCM, ChaCha20-Poly1305
前向保密可选强制
已知漏洞BEAST, POODLE, RC4 等无已知漏洞
⚠️ 前向保密 (Forward Secrecy) 为什么重要?没有前向保密时,如果服务器的长期私钥泄露,攻击者可以解密所有历史流量记录。TLS 1.3 强制使用 ECDHE(临时密钥交换),每次会话用不同的临时密钥,即使长期密钥泄露也不影响过去的会话。如果你还在用 TLS 1.2 的 RSA 密钥交换,立刻迁移!

TLS 1.3 支持的 5 个密码套件

TLS_AES_128_GCM_SHA256          # AES-128-GCM + SHA-256, 最快
TLS_AES_256_GCM_SHA384          # AES-256-GCM + SHA-384, 更安全
TLS_CHACHA20_POLY1305_SHA256    # ChaCha20-Poly1305 + SHA-256, 移动端首选
TLS_AES_128_CCM_SHA256          # AES-128-CCM + SHA-256, IoT 设备
TLS_AES_128_CCM_8_SHA256        # AES-128-CCM-8 + SHA-256, 极短标签

🗝️ API 密钥 & 密码存储:绝大多数人做错了

❌ 常见错误

✅ 正确做法:密码存储

密码存储的黄金标准:bcrypt / scrypt / Argon2。它们是"慢哈希"——故意设计成计算缓慢,使暴力破解的成本极高。

算法类型抗 GPU抗 ASIC内存硬度推荐度
Argon2idPassword Hashing Competition 冠军✅ 极强✅ 极强可调(推荐 ≥64MB)⭐⭐⭐⭐⭐
bcryptBlowfish 变体✅ 强⚠️ 中等4KB 固定⭐⭐⭐⭐
scrypt内存硬哈希✅ 强✅ 强可调⭐⭐⭐⭐
PBKDF2NIST 标准❌ 弱❌ 弱⭐⭐ 遗留系统
SHA-256+salt普通哈希❌ 无❌ 无❌ 不够
MD5 / SHA-1已破解N/AN/A❌ 禁止

✅ 正确做法:API 密钥存储

API 密钥和密码不同——API 密钥需要可验证但不可逆

生产环境密钥管理清单:
  1. ❌ 不在代码/配置文件中硬编码密钥 → ✅ 使用环境变量或密钥管理服务 (AWS KMS / HashiCorp Vault)
  2. ❌ 不明文存储密钥 → ✅ 存哈希,验证时比对哈希
  3. ❌ 不在日志中打印密钥 → ✅ 日志脱敏
  4. ❌ 不用同一个密钥做所有事 → ✅ 密钥分离(加密密钥 ≠ 签名密钥 ≠ 认证密钥)
  5. ✅ 定期轮换密钥(90天或更短)
  6. ✅ 密钥撤销机制(泄露时一键失效)

密钥轮换策略

密钥轮换是安全运维的基本功。好的轮换策略应该:

🎫 JWT vs Session:安全分析

JWT (JSON Web Token)

  • 机制:服务端签发 Token,客户端存储,每次请求携带
  • 状态:无状态 — 服务端不存储会话信息
  • 签名:使用 HMAC-SHA256 或 RSA/ECDSA 签名
  • 结构:Header.Payload.Signature(Base64URL 编码)
  • 撤销困难:Token 签发后无法主动使其失效
  • 体积大:包含 claims 的 Token 可达数 KB
  • 适合:微服务、跨域认证、第三方授权

Session

  • 机制:服务端创建会话,返回 Session ID,客户端存 Cookie
  • 状态:有状态 — 服务端存储会话数据
  • 安全:Session ID 泄露可被劫持,但服务端可即时撤销
  • 结构:仅一个随机字符串 ID
  • 撤销简单:服务端删除 Session 即可
  • 体积小:Cookie 中仅存 Session ID
  • 适合:单体应用、需要即时撤销的场景

JWT 安全最佳实践

JWT 的问题不是 JWT 本身,而是大多数人的用法:

⚠️ JWT 最常见的安全漏洞:alg: none 攻击。如果服务端不强制指定算法,攻击者可以将 Header 中的 alg 改为 none,然后提交一个没有签名的 Token。很多 JWT 库会接受这种 Token。永远在验证时指定算法,不要从 Token Header 中读取。

💻 代码示例

Python:AES-GCM 加密解密

from cryptography.hazmat.primitives.ciphers.aead import AESGCM
import os

# 生成 256 位密钥
key = AESGCM.generate_key(bit_length=256)
aesgcm = AESGCM(key)

# 加密
nonce = os.urandom(12)  # GCM 推荐 96 位 nonce
plaintext = b"Hello, AI World! 这是一条加密消息。"
aad = b"additional-data"  # 关联数据(不加密但认证)
ciphertext = aesgcm.encrypt(nonce, plaintext, aad)
print(f"密文: {ciphertext.hex()[:64]}...")

# 解密
decrypted = aesgcm.decrypt(nonce, ciphertext, aad)
print(f"明文: {decrypted.decode()}")

# ⚠️ 注意:同一个密钥 + 同一个 nonce 不能加密不同的消息!
# nonce 重复会导致密钥流重用,破坏机密性和认证性

Python:RSA 密钥对生成 & 签名

from cryptography.hazmat.primitives.asymmetric import rsa, padding
from cryptography.hazmat.primitives import hashes, serialization

# 生成 RSA-2048 密钥对(推荐 4096 用于新项目)
private_key = rsa.generate_private_key(
    public_exponent=65537,  # 标准公钥指数
    key_size=2048,
)
public_key = private_key.public_key()

# 签名(使用 PSS 填充,比 PKCS1v15 更安全)
message = b"Important AI model hash: sha256:abc123..."
signature = private_key.sign(
    message,
    padding.PSS(
        mgf=padding.MGF1(hashes.SHA256()),
        salt_length=padding.PSS.MAX_LENGTH
    ),
    hashes.SHA256()
)

# 验证
try:
    public_key.verify(
        signature,
        message,
        padding.PSS(
            mgf=padding.MGF1(hashes.SHA256()),
            salt_length=padding.PSS.MAX_LENGTH
        ),
        hashes.SHA256()
    )
    print("✅ 签名验证通过")
except Exception as e:
    print(f"❌ 签名验证失败: {e}")

# 导出密钥(私钥加密存储)
pem_private = private_key.private_bytes(
    encoding=serialization.Encoding.PEM,
    format=serialization.PrivateFormat.PKCS8,
    encryption_algorithm=serialization.BestAvailableEncryption(b"my-password")
)
pem_public = public_key.public_bytes(
    encoding=serialization.Encoding.PEM,
    format=serialization.PublicFormat.SubjectPublicKeyInfo
)

Python:Ed25519 签名(推荐用于新项目)

from cryptography.hazmat.primitives.asymmetric.ed25519 import Ed25519PrivateKey
from cryptography.hazmat.primitives import serialization

# 生成 Ed25519 密钥对(比 RSA 快 100 倍,签名更短)
private_key = Ed25519PrivateKey.generate()
public_key = private_key.public_key()

# 签名 - 极其简单,不需要指定填充和哈希
message = b"Model inference result hash: sha256:xyz789..."
signature = private_key.sign(message)
print(f"签名大小: {len(signature)} 字节")  # 64 字节

# 验证
try:
    public_key.verify(signature, message)
    print("✅ Ed25519 签名验证通过")
except Exception:
    print("❌ 签名验证失败")

Python:Argon2id 密码哈希

# pip install argon2-cffi
from argon2 import PasswordHasher
from argon2.exceptions import VerifyMismatchError

# 创建哈希器(推荐参数)
ph = PasswordHasher(
    time_cost=3,       # 迭代次数(默认 3)
    memory_cost=65536, # 内存使用 64MB(默认 102400 = 100MB)
    parallelism=4,     # 并行度(匹配 CPU 核数)
    hash_len=32,       # 输出哈希长度
    salt_len=16,       # 盐长度
    type=argon2.Type.ID # Argon2id(推荐,抗侧信道+抗GPU)
)

# 哈希密码
password = "user-password-123"
hash_str = ph.hash(password)
print(f"哈希: {hash_str}")
# 输出示例: $argon2id$v=19$m=65536,t=3,p=4$c2FsdHNhbHQ$hash...

# 验证密码
try:
    ph.verify(hash_str, password)
    print("✅ 密码正确")
except VerifyMismatchError:
    print("❌ 密码错误")

# 检查是否需要重新哈希(参数更新后)
if ph.check_needs_rehash(hash_str):
    new_hash = ph.hash(password)
    print("密码哈希已更新到新参数")

OpenSSL 命令行实战

# 生成 RSA-4096 私钥
openssl genrsa -out private.pem 4096

# 从私钥提取公钥
openssl rsa -in private.pem -pubout -out public.pem

# 生成 ECC (P-256) 私钥
openssl ecparam -name prime256v1 -genkey -noout -out ecc-private.pem

# 生成 Ed25519 私钥
openssl genpkey -algorithm ED25519 -out ed25519-private.pem

# 用 RSA 私钥签名文件
openssl dgst -sha256 -sign private.pem -out signature.bin message.txt

# 用 RSA 公钥验证签名
openssl dgst -sha256 -verify public.pem -signature signature.bin message.txt

# AES-256-GCM 加密文件
openssl enc -aes-256-gcm -salt -in plaintext.txt -out ciphertext.enc -pass pass:your-password

# 生成自签名证书(仅开发用!)
openssl req -x509 -newkey rsa:4096 -keyout key.pem -out cert.pem -days 365 -nodes

# 查看证书详情
openssl x509 -in cert.pem -text -noout

# 测试 TLS 连接
openssl s_client -connect example.com:443 -tls1_3

Node.js:JWT 安全实践

const jose = require('jose'); // 推荐:jose 库比 jsonwebtoken 更现代

// 生成 EdDSA (Ed25519) 密钥对用于 JWT 签名
const { publicKey, privateKey } = await jose.generateKeyPair('EdDSA', {
  crv: 'Ed25519'
});

// 签发 JWT
const jwt = await new jose.SignJWT({
  sub: 'user-123',
  role: 'admin',
  model: 'gpt-4'
})
  .setProtectedHeader({ alg: 'EdDSA' })  // 明确指定算法!
  .setIssuedAt()
  .setIssuer('https://api.example.com')
  .setAudience('https://app.example.com')
  .setExpirationTime('15m')  // 短过期时间
  .sign(privateKey);

// 验证 JWT(强制指定算法,防止 alg 混淆攻击)
const { payload } = await jose.jwtVerify(jwt, publicKey, {
  algorithms: ['EdDSA'],  // 白名单算法
  issuer: 'https://api.example.com',
  audience: 'https://app.example.com',
});

✅ 实战检查清单

检查项要求状态
TLS 版本仅允许 TLS 1.2+,优先 TLS 1.3
密码套件仅 AEAD 套件(AES-GCM / ChaCha20-Poly1305)
证书使用受信任 CA 签发的证书,开启 HSTS
密码存储Argon2id 或 bcrypt,不用 SHA/MD5
API 密钥存储只存哈希,验证时比对
密钥管理使用 KMS/Vault,不硬编码
密钥轮换DEK 90天轮换,KEK 年度轮换
JWT 安全短过期 + 算法白名单 + HttpOnly Cookie
密钥长度RSA ≥2048,ECC ≥256,AES ≥128
后量子准备了解 PQC 迁移路径,混合部署