AES-GCM、RSA、椭圆曲线——每个 SaaS 都在用但多数人做错的密码学基础。
从 TLS 握手到 API 密钥存储,把原理搞清楚才能做出正确的架构选择。
对称加密使用同一个密钥进行加密和解密。它是现代密码学的基石——你每天上网产生的加密流量,99% 都是对称加密。
AES (Advanced Encryption Standard) 由 NIST 于 2001 年发布(FIPS 197),替代了已不安全的 DES。它是一种分组密码,固定块大小 128 位,支持 128/192/256 位密钥。
| AES 变体 | 密钥长度 | 轮数 | 安全级别 | 典型用途 |
|---|---|---|---|---|
| AES-128 | 128 bit | 10 | 足够安全(截至 2025 无已知实际攻击) | TLS、磁盘加密 |
| AES-192 | 192 bit | 12 | 高安全 | 政府/军事 |
| AES-256 | 256 bit | 14 | 极高安全,抗量子(Grover 算法下仍安全) | 金融、合规要求 |
AES 本身只能加密 128 位的块。工作模式定义了如何处理超过 128 位的数据:
| 模式 | 类型 | 并行加密 | 并行解密 | 认证 | 推荐度 |
|---|---|---|---|---|---|
| GCM | AEAD | ✅ | ✅ | ✅ GMAC | ⭐⭐⭐⭐⭐ 首选 |
| CTR | 流式 | ✅ | ✅ | ❌ | ⭐⭐⭐ 可用但需配合 HMAC |
| CBC | 分组 | ❌ | ✅ | ❌ | ⭐⭐ 遗留系统 |
| ECB | 分组 | ✅ | ✅ | ❌ | ❌ 禁止使用 |
Google 的 Adam Langley 设计了 ChaCha20-Poly1305 作为 AES-GCM 的替代方案。在移动设备(没有 AES-NI 指令集的 ARM 芯片)上,ChaCha20-Poly1305 比 AES-GCM 快得多。TLS 1.3 中两者都是必须支持的密码套件。
| 特性 | AES-256-GCM | ChaCha20-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》开创了公钥密码学。非对称加密使用一对密钥:公钥公开,私钥保密。它解决了对称加密的核心问题——密钥分发。
基础:大整数分解难题
密钥大小:2048/3072/4096 bit
速度:慢(加密/解密约 1ms 级)
用途:密钥交换、数字签名、证书
注意:RSA-2048 是当前最低安全要求,RSA-1024 已被破解
基础:椭圆曲线离散对数难题 (ECDLP)
密钥大小:256/384/521 bit
速度:比 RSA 快得多
等效安全:256-bit ECC ≈ 3072-bit RSA
曲线:P-256 (NIST)、Curve25519 (Bernstein)、secp256k1 (Bitcoin)
基础:格密码 (Lattice)、编码、哈希
标准:CRYSTALS-Kyber (加密/KEM)、CRYSTALS-Dilithium (签名)
NIST 状态:2024 年正式标准化 (FIPS 203/204/205)
迁移:Google/Apple/Cloudflare 已开始混合部署
| 安全级别 (bit) | Symmetric | RSA | ECC | 说明 |
|---|---|---|---|---|
| 80 | 80 | 1024 | 160 | ⚠️ 已不安全 |
| 112 | 112 | 2048 | 224 | ⚠️ 最低可用 |
| 128 | 128 | 3072 | 256 | ✅ 当前标准 |
| 192 | 192 | 7680 | 384 | ✅ 高安全 |
| 256 | 256 | 15360 | 521 | ✅ 最高安全 / 抗量子 |
| 曲线 | 设计者 | 用途 | 特点 |
|---|---|---|---|
| Curve25519 | Daniel J. Bernstein | 密钥交换 (ECDH) | 常量时间实现、无侧信道、速度极快。TLS 1.3 默认 |
| Ed25519 | Bernstein 等 | 数字签名 | EdDSA 签名方案、确定性行为、无随机数失败风险 |
| P-256 | NIST | 通用 | 最广泛部署的 NIST 曲线、硬件加速好、但实现需注意侧信道 |
| secp256k1 | Certicom | Bitcoin/Ethereum | Koblitz 曲线、特殊结构使计算更快、中本聪的选择 |
| P-384 / P-521 | NIST | 高安全 | 192/256-bit 安全级别、政府/军事用途 |
数字签名是"非对称加密的逆向使用"——用私钥签名,用公钥验证。它提供两个保证:
| 签名算法 | 类型 | 签名大小 | 速度 | 标准 |
|---|---|---|---|---|
| RSA-PSS | RSA | 256-512 字节 | 慢 | PKCS#1 v2.2 |
| ECDSA (P-256) | ECC | 64 字节 | 中等 | FIPS 186-4 |
| Ed25519 | ECC (EdDSA) | 64 字节 | 极快 | RFC 8032 |
| Dilithium | 后量子 | ~2.4 KB | 中等 | FIPS 204 |
| Falcon | 后量子 | ~1.3 KB | 中等 | FIPS 205 |
TLS (Transport Layer Security) 是对称加密和非对称加密协作的完美范例:用非对称加密安全协商对称密钥,然后用对称加密传输数据。TLS 1.3 (RFC 8446) 大幅简化了握手流程。
| 特性 | TLS 1.2 | TLS 1.3 |
|---|---|---|
| 握手 RTT | 2 RTT | 1 RTT |
| 0-RTT 恢复 | 不支持 | 支持(PSK 模式) |
| 密码套件 | 37+ 种(很多不安全) | 5 种(全部 AEAD) |
| 密钥交换 | 静态 RSA 或 DHE | 仅 (EC)DHE(前向保密强制) |
| 加密算法 | AES-CBC, RC4 等 | AES-GCM, ChaCha20-Poly1305 |
| 前向保密 | 可选 | 强制 |
| 已知漏洞 | BEAST, POODLE, RC4 等 | 无已知漏洞 |
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, 极短标签
密码存储的黄金标准:bcrypt / scrypt / Argon2。它们是"慢哈希"——故意设计成计算缓慢,使暴力破解的成本极高。
| 算法 | 类型 | 抗 GPU | 抗 ASIC | 内存硬度 | 推荐度 |
|---|---|---|---|---|---|
| Argon2id | Password Hashing Competition 冠军 | ✅ 极强 | ✅ 极强 | 可调(推荐 ≥64MB) | ⭐⭐⭐⭐⭐ |
| bcrypt | Blowfish 变体 | ✅ 强 | ⚠️ 中等 | 4KB 固定 | ⭐⭐⭐⭐ |
| scrypt | 内存硬哈希 | ✅ 强 | ✅ 强 | 可调 | ⭐⭐⭐⭐ |
| PBKDF2 | NIST 标准 | ❌ 弱 | ❌ 弱 | 无 | ⭐⭐ 遗留系统 |
| SHA-256+salt | 普通哈希 | ❌ 无 | ❌ 无 | 无 | ❌ 不够 |
| MD5 / SHA-1 | 已破解 | N/A | N/A | 无 | ❌ 禁止 |
API 密钥和密码不同——API 密钥需要可验证但不可逆:
sk_live_abc1...xyz9)密钥轮换是安全运维的基本功。好的轮换策略应该:
JWT 的问题不是 JWT 本身,而是大多数人的用法:
decode() 不验证签名,必须用 verify()alg 白名单:防止算法混淆攻击(将 RS256 改为 HS256)alg: none 攻击。如果服务端不强制指定算法,攻击者可以将 Header 中的 alg 改为 none,然后提交一个没有签名的 Token。很多 JWT 库会接受这种 Token。永远在验证时指定算法,不要从 Token Header 中读取。
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 重复会导致密钥流重用,破坏机密性和认证性
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
)
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("❌ 签名验证失败")
# 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("密码哈希已更新到新参数")
# 生成 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
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 迁移路径,混合部署 | ☐ |