一个最常见的加密误区
「我用了 AES,所以安全」——这句话对问题的回答其实只进行了一半。AES 是分组加密算法,它只定义了单个固定长度数据块的加密变换;怎么把这段任意长度的明文切成块、怎么让块之间互相影响、怎么防止篡改,这些答案组成了工作模式(mode of operation)。选错模式,AES 的保护力会大打折扣。
本文用图解把最常用的三种模式讲清楚,并给出能直接落地的选型。
先认识分组
AES 一次处理 16 字节的块。明文太长就分成多个 16 字节块;最后一块不足 16 字节时做 PKCS7 填充。
明文: [12 字节区] → 需补齐 4 字节
实际: [12 字节区][\x04\x04\x04\x04] → 补到 16
填充满整块时也会额外补一整块(\x10×16),这样解密时永远能区分「真数据」和「已填充」。
ECB:块与块之间完全独立
明文块1 ──AES──▶ 密文块1
明文块2 ──AES──▶ 密文块2 (块之间不互相影响)
明文块3 ──AES──▶ 密文块3
相同的明文块 → 相同的密文块。这正是它的致命伤:加密后数据里的重复图案原样保留。默认不要用 ECB,它只适合加密单块固定长度且不可预测的数据。
CBC:把上一个块接到下一个块
IV(随机) 密文块1 密文块2
│ │ │
明文块1 ─┴─∈⊕────────► AES ──►┐ 明文块2─┴─∈⊕─► AES ──►
CBC 让每个明文块先与前一个密文块异或再加密,形成一条链:
- IV 使命:第一个块的前任由随机的初始化向量(IV)担任;
- IV 规则:每次加密都要新鲜随机、绝不复用;把 IV 明文拼在密文首部;
- 缺点:加密必须逐块串行(不能并行),若某块密文损坏,会波及后续一块的明文。
GCM:加密 + 认证一起搞定
GCM 用 CTR 计数器流模式加密(可并行、无需填充),同时在字段上算了认证标签:
nonce ──▶ 计数器(1) ─▶ 密钥流 ∈⊕ 明文块1 ─▶ 密文块1 ─┐
nonce ──▶ 计数器(2) ─▶ 密钥流 ∈⊕ 明文块2 ─▶ 密文块2 ─┼──▶ GMAC 认证标签
AAD ─┘
- 输出 =
nonce ‖ 密文 ‖ tag,tag 用于检测任何篡改; - 96 位 nonce 随机且唯一,同 key 下绝不重用;
- TLS 1.3 与主流库(OpenSSL、WebCrypto、Go、.NET)的默认首选。
三模式速查
| 特性 | ECB | CBC | GCM |
|---|---|---|---|
| 机密性 | ✔ | ✔ | ✔ |
| 完整性/防篡改 | ✘ | ✘(额外 HMAC) | ✔(自带认证标签) |
| 填充 | PKCS7 | PKCS7 | 无(流式) |
| 可并行加密 | ✔ | ✘ | ✔ |
| 随机 IV/nonce | 不需要 | 必须 | 必须 |
| 缺点 | 泄漏模式 | 串行、易错拼 HMAC | 实现需经认证 |
常见坑清单
- ECB 用于多变长数据 → 形象、结构泄漏;改 GCM。
- CBC 复用/硬编码 IV → 共前缀泄漏;IV 每次随机并前置。
- 只用加密不做认证 → 密文可被翻转仍解出内容;GCM 自带认证。
- GCM nonce 重用 → 密钥流复用,灾难性;每 key 每 nonce 唯一。
- 密钥硬编码进代码 → 无论多强算法都会失效;密钥单独安全管理。
动手验证与自查
选一个加密工具库(浏览器可用 WebCrypto 的 AES-GCM),把同一段带重复图案的文本分别用 CBC 与 GCM 加密两次——你会看到 CBC 的 IV 不同导致两次密文不同,这正好体现了随机 IV 的作用。再把 GCM 密文的任一字节改掉,用 tag 验证会直接失败,这就是「认证加密」的价值。
记住主线即可:默认 AES-256-GCM,IV/nonce 随机唯一,密文连同 nonce 与 tag 一起保存。