现代密码学速查:对称、非对称、哈希、签名你该用哪个
加密文件给朋友、给 API 请求加签名、把密码安全地存数据库——三个场景,三种密码学原语,用错一个就是漏洞。
很多开发者对密码学的认知停留在"加密"这个粗糙概念,结果常常是:本该用签名的地方用了加密,本该用 AEAD 的地方光用了 AES,本该用 Argon2 的地方用了 SHA-256。
这篇文章把现代密码学的四类核心原语(对称加密、非对称加密、哈希、数字签名)拆开讲清楚,配上一张决策树——以后写到密码学相关代码时,照树走就行。
四类原语:先把概念锁死
| 原语 | 方向 | 是否需要密钥 | 典型用途 | 速度 |
|---|---|---|---|---|
| 对称加密 | 双向 | 一把共享密钥 | 大量数据加密 | 快 |
| 非对称加密 | 双向 | 公钥 + 私钥 | 密钥交换、小量数据加密 | 慢 |
| 哈希 | 单向 | 无(HMAC 除外) | 完整性校验、指纹 | 极快 |
| 数字签名 | 单向 | 私钥签 + 公钥验 | 真实性 + 完整性 | 中等 |
每个原语解决的问题不同,不能互相替代。
对称加密(Symmetric Encryption)
核心思想
通信双方共享同一把密钥,发送方用密钥加密、接收方用同一把密钥解密。
主流算法
| 算法 | 推荐度 | 备注 |
|---|---|---|
| AES-128 / AES-256 | ⭐⭐⭐ | 业界标准,硬件加速广泛 |
| ChaCha20 | ⭐⭐⭐ | 软件实现下比 AES 快,移动端首选 |
| 3DES | ❌ | 已淘汰,性能差且强度不足 |
| DES | ❌ | 56 位密钥已被破,禁用 |
| RC4 | ❌ | 多个偏置漏洞,禁用 |
| Blowfish | ❌ | 64 位块大小问题(生日攻击),禁用 |
工作模式(Mode):比算法更容易出事
AES 本身只是个"块加密器"——一次加密 16 字节。要加密更长的数据,必须配一个工作模式。模式选错比算法选错更容易出漏洞。
| 模式 | 状态 | 备注 |
|---|---|---|
| ECB | ❌ 禁用 | 相同明文产生相同密文,泄漏模式(著名的 ECB 企鹅图) |
| CBC | ⚠️ 慎用 | 没有完整性校验,padding oracle 攻击 |
| CTR | ⚠️ 慎用 | 同样无完整性,nonce 重用直接泄露明文 |
| GCM(AES-GCM) | ⭐⭐⭐ | 推荐,自带认证(AEAD) |
| ChaCha20-Poly1305 | ⭐⭐⭐ | 推荐,AEAD 结构 |
关键概念:AEAD
AEAD = Authenticated Encryption with Associated Data(带关联数据的认证加密)。
它把加密和完整性校验合在一起——既保证别人看不懂密文,也保证密文没被篡改。现代代码只用 AEAD,不要再用纯 CBC/CTR。
AES vs ChaCha20 怎么选
| 维度 | AES-256-GCM | ChaCha20-Poly1305 |
|---|---|---|
| 硬件加速 | 主流 CPU 都有 AES-NI | 无专用指令但纯软件已经够快 |
| 移动端性能 | 中(依赖硬件) | 优(无硬件依赖) |
| 抗时序攻击 | 取决于实现 | 设计上就抗时序 |
| TLS 1.3 默认 | 二选一 | 二选一 |
结论:服务器端用 AES-256-GCM(CPU 有硬件加速),移动端 / IoT 用 ChaCha20-Poly1305。
非对称加密(Asymmetric Encryption)
核心思想
每个人有一对密钥:公钥公开、私钥保密。
| 用法 | 谁加密 | 谁解密 | 解决什么 |
|---|---|---|---|
| 加密通信 | 用对方公钥 | 对方用私钥 | 机密性 |
| 数字签名 | 用自己私钥 | 别人用你公钥 | 真实性 |
主流算法
| 算法 | 用途 | 密钥长度 | 推荐度 |
|---|---|---|---|
| RSA | 加密 / 签名 / 密钥交换 | 至少 2048 位 | ⭐⭐ 仍可用但偏老 |
| ECDH / ECDSA(NIST P-256/P-384) | 密钥交换 / 签名 | 256-384 位 | ⭐⭐ 性能好但有曲线选择争议 |
| X25519 / Ed25519 | 密钥交换 / 签名 | 256 位 | ⭐⭐⭐ 现代首选 |
| 后量子算法(Kyber / Dilithium) | 抗量子 | — | 实验阶段,开始关注 |
关键事实
- 非对称加密只用于小数据:RSA-2048 + OAEP-SHA256 一次最多加密 190 字节(公式:
key_size_bytes - 2 - 2*hash_len) - 真实做法是混合加密:用非对称交换一把对称密钥,剩下的数据用对称加密
- TLS 就是这么干的:握手用 ECDHE 协商出会话密钥,业务数据走 AES-GCM
RSA 的常见误用
另外,RSA 加密必须用 OAEP 填充,RSA 签名必须用 PSS 填充——PKCS1v15 在两个场景都已被证明有问题。
哈希(Hashing)
哈希的细节我之前在 Hash 算法横评 里展开讲过,这里只放速查结论:
| 场景 | 用什么 |
|---|---|
| 文件完整性(无对抗) | SHA-256 / BLAKE3 |
| 文件完整性(防伪造) | SHA-256 / BLAKE3 |
| 密码存储 | Argon2id(首选) / bcrypt(次选) |
| API 签名 / Webhook 验签 | HMAC-SHA256 |
| 通用指纹、缓存键 | SHA-256 / BLAKE3 |
| ❌ 永远不要用 | MD5 / SHA-1 用于安全场景 |
数字签名(Digital Signature)
核心思想
数字签名 = 私钥签 + 公钥验。证明两件事:
- 真实性:消息确实是签名者本人发的
- 完整性:消息从签名后没被改过
数字签名 vs 加密 vs HMAC
很多人把这三个混着用:
| 原语 | 谁有密钥 | 解决什么 | 是否抗抵赖 |
|---|---|---|---|
| 对称加密 | 双方共享同一把 | 机密性 | ❌ |
| HMAC | 双方共享 secret | 完整性 + 真实性 | ❌(双方都能签) |
| 数字签名 | 只有发件方有私钥 | 完整性 + 真实性 | ✅ |
关键差异:HMAC 双方都能签,无法对外证明"是 A 发的不是 B 发的"。数字签名的私钥只在 A 手里,A 签的东西全世界都能验。
算法选型
| 算法 | 推荐度 | 备注 |
|---|---|---|
| Ed25519 | ⭐⭐⭐ | 现代首选,签名快、密钥短、抗时序 |
| ECDSA P-256 | ⭐⭐ | 主流接受度高,TLS 证书常见 |
| RSA-PSS-2048+ | ⭐⭐ | 兼容性好,密钥/签名较长 |
| RSA-PKCS1v15 | ⚠️ | 兼容性原因仍在用,但 PSS 是更好的选择 |
| ECDSA P-384 / RSA-4096 | ⭐⭐ | 高安全场景 |
新项目优先 Ed25519——SSH 默认就在推它。
常见用途
- HTTPS 证书签名(CA 用私钥签你的证书)
- 软件包签名(apt、yum、npm provenance)
- JWT 的 RS256/ES256/EdDSA
- 区块链交易签名
- Webhook payload 签名(GitHub / Stripe / 飞书 webhook)
一张大决策树
把上面所有内容收成一张图:
5 个开发者最常做错的密码学决策
错误 1:用 AES 但忘了 AEAD
AES_MODE_CBC 在 2026 年应该和 MD5 享受同等待遇——能不用就不用。
错误 2:nonce / IV 重复
GCM、CTR、ChaCha20 都要求同一把密钥下 nonce 永远不能重复。重复一次就泄露明文。
或者用计数器(counter-based nonce),但要保证持久化、跨重启唯一。
错误 3:把签名当加密
"我把 token 用 HMAC-SHA256 签了,里面的用户 ID 别人就看不到了。"
❌ HMAC 是签名,不是加密。token 里的内容所有人都能读到,HMAC 只保证别人改不了。
要"看不到"必须用加密。JWE(JSON Web Encryption)才是加密版的 JWT。
错误 4:用通用哈希存密码
通用哈希太快,GPU 一秒几十亿次。密码必须用专门的算法(Argon2id / bcrypt)。详见 Hash 算法横评。
错误 5:自己实现密码学
90% 的密码学漏洞来自"自己实现 + 没考虑边界情况":
- 时序攻击(不用
compare_digest比较) - nonce 重用
- padding oracle
- 椭圆曲线点不在曲线上的恶意输入
永远用经过审计的库:
| 语言 | 推荐库 |
|---|---|
| Python | cryptography(不是 pycrypto) |
| Node.js | crypto(内置)/ libsodium-wrappers |
| Go | crypto/...(内置)/ golang.org/x/crypto |
| Rust | ring / rustcrypto |
| 跨语言 | libsodium(NaCl)—— 现代密码学最佳工具箱 |
不要碰:OpenSSL EVP API(除非你真的懂),任何"我自己写的 AES 实现"。
libsodium:一站式现代密码学
如果只能推荐一个库,那就是 libsodium(基于 Daniel J. Bernstein 的 NaCl 库):
- 默认就是安全的算法(XSalsa20-Poly1305 / X25519 / Ed25519 / Argon2id / BLAKE2b)
- API 设计上几乎不可能误用
- 几乎所有语言都有封装
简明示例
写出来比手搓 OpenSSL 短了 80%,而且默认就是当前最佳实践。
关于后量子密码
量子计算的进展让经典非对称算法(RSA / ECC)面临未来风险——量子计算机能在多项式时间内破解这些。
NIST 在 2024 年标准化了第一批后量子算法:
| 算法 | 用途 | 状态 |
|---|---|---|
| CRYSTALS-Kyber(ML-KEM) | 密钥封装 | 已标准化 |
| CRYSTALS-Dilithium(ML-DSA) | 数字签名 | 已标准化 |
| SPHINCS+(SLH-DSA) | 数字签名(基于哈希) | 已标准化 |
当前应该做什么:
- 关注但不用紧急切换——量子破解还要 10-15 年
- 设计新协议时考虑"密码学敏捷性"(agility),让算法可替换
- TLS 1.3 + 混合 KEM(X25519 + Kyber)已经在 Chrome / Cloudflare 部分启用
总结
把这张速查图钉在脑子里:
| 场景 | 用什么 |
|---|---|
| 加密大量数据 | AES-256-GCM / ChaCha20-Poly1305 |
| 双方协商共享密钥 | X25519(ECDHE) |
| 数字签名 | Ed25519 > ECDSA > RSA-PSS |
| 共享 secret 验消息 | HMAC-SHA256 |
| 密码存储 | Argon2id > bcrypt |
| 文件指纹 | SHA-256 / BLAKE3 |
| 生成 API Key | 32 字节随机数(不是哈希什么) |
三条铁律:
- 永远用 AEAD——不要用裸 CBC/CTR
- 永远用经过审计的库——优先 libsodium
- 永远不要自己实现密码学——除非你写过博士论文
写完代码用站内的 Hash 生成器、密码生成器、Token 生成器 验证关键参数——配合一个靠谱的密码学库,已经能覆盖 95% 的开发需求。