密语 · CIPHER | 目录 CH.08 / 26
绝密
Phase 2 · 对称密码

分组模式与 ECB 企鹅

同一个 AES,选错模式就等于裸奔。这一章有密码学史上最著名的一张翻车图。

阅读 ~14 分钟 前置 第 7 章 Demo 图像 ECB vs CBC 对比

AES 只能加密 16 字节。可你要加密的东西——一张图片、一段 JSON、一个视频——几乎总是更长。怎么把它切成很多块、逐块加密,再拼回去?这个"怎么衔接"的规则,就叫分组模式(mode of operation)。选对了,安全;选错了,再强的 AES 也保护不了你。

一、最自然、也最危险的做法:ECB

最直觉的办法:把数据切成 16 字节一块,每块各自独立地用同一把密钥加密,再首尾拼起来。这叫 ECB(电子密码本)模式。

C₁ = E(K, P₁)   C₂ = E(K, P₂)   C₃ = E(K, P₃)   …

问题一句话就能说清:相同的明文块,永远产生相同的密文块。这不就是我们第 3 章反复处刑的那个幽灵吗——确定性映射保留了统计结构。凯撒密码里"相同字母→相同密文"被频率分析击穿;ECB 里"相同数据块→相同密文块",会让明文里的重复模式原封不动地透过密文显形。

二、亲眼见证:ECB 企鹅

光说不够。下面的 Demo 现场画一只企鹅(大片纯色区域),然后分别用 ECB 思路CBC/CTR 思路去"加密"它的像素块。ECB 下,大片相同颜色的区域被加密成大片相同的密文——于是企鹅的轮廓完好地泄露了出来;而链式模式下,一切都变成了均匀噪声。

DEMO ECB vs CBC 图像加密
原图(明文)
ECB 加密 ✗
CBC / CTR 加密 ✓
ECB 保留了轮廓 CBC 看起来像噪声
这就是真实存在的 "ECB 企鹅"(原图是 Linux 吉祥物 Tux)。ECB 加密后,你依然一眼认出那是只企鹅——加密完全失败。密文看起来"乱"不代表安全,它必须看起来随机
铁律:永远不要用 ECB

ECB 几乎是唯一一个"名字很正经、实际几乎永远是错的"模式。任何时候你在代码里看到 AES/ECB/...,都应该当成一个 bug。它不仅泄露图像轮廓,也泄露数据库里"哪些记录字段相同"、Cookie 里"哪些用户权限相同"等结构信息。可惜很多语言/库的默认模式恰好是 ECB(比如 Java 的 Cipher.getInstance("AES") 不指定模式时就是 ECB!),这是无数真实漏洞的源头。

三、让密文"随机"起来:IV 与链式模式

ECB 的病根是"确定性"。治法是给加密注入随机性,让同样的明文每次加密都得到不同的密文。做这件事的关键零件叫 IV(初始化向量)——一个每次加密都重新随机生成的值。

CBC(密码块链接)

CBC 的思路是"链式":加密每一块之前,先把它和上一块的密文 XOR;第一块则和 IV XOR。

C₁ = E(K, P₁ ⊕ IV)    Cᵢ = E(K, Pᵢ ⊕ Cᵢ₋₁)

这样每一块都"记得"它前面所有块,重复的明文块不再产生重复的密文块,ECB 的病根被拔除。只要 IV 每次随机,同一段明文两次加密的结果就完全不同。

CTR(计数器)

CTR 更漂亮:它不直接加密明文,而是加密一串"nonce+计数器",用输出当密钥流,再去 XOR 明文——换句话说,CTR 把分组密码变成了流密码(第 6 章的思想在这里回归)。它天然支持并行、随机访问,是现代 AEAD(第 9 章 GCM)的基础。

模式相同明文→相同密文?需要 IV/nonce?能并行?提供完整性?
ECB是(致命)
CBC是(随机 IV)加密否
CTR是(nonce)
IV 的两条纪律

CBC 的 IV 必须不可预测(随机):如果攻击者能预测下一个 IV,就能发起 BEAST 那类攻击。② CTR/GCM 的 nonce 必须唯一、绝不重复——这又回到了第 5、6 章的两次一密。IV/nonce 通常和密文一起明文传输(它不是秘密),但它的"新鲜度"是安全的一部分。把 IV 写死成全零,是仅次于用 ECB 的第二大经典错误。

四、还差最后一块拼图:完整性

看上面的表格最后一列:CBC 和 CTR 都不提供完整性。它们只保证 Eve 读不懂,却挡不住 Mallory 篡改。

CTR 模式尤其危险:它本质是流密码,攻击者翻转密文的某一位,解密后明文对应位就精确翻转,且解密方毫无察觉。想象一段加密的转账报文 amount=0100,攻击者不需要密钥,只要知道字段位置,就能翻几个比特把它改成 amount=9100——这就是第 1 章开篇警告的"加密 ≠ 完整性"的具体形态。

CBC 也好不到哪去,它对特定篡改敏感,还催生了臭名昭著的 padding oracle 攻击:仅仅因为服务器会对"填充格式错误"和"其它错误"返回不同的响应,攻击者就能像第 1 章说的"预言机"一样,一次问出一个字节,最终解出整段密文——完全不需要密钥。

"加密了"三个字害惨了多少人

Padding oracle 不是理论。2010 年的 POODLE、针对 ASP.NET 的攻击、以及无数自研"加密"接口,都栽在同一件事上:只加密,不认证。教训极其明确——加密和完整性必须捆绑在一起做,而且要用经过验证的方式做。这正是下一章 AEAD 存在的全部理由。

于是我们来到了对称密码的终点站:一个既保机密、又保完整、还防重放的"全家桶"——AEAD。这也是你在 Android 上唯一应该选择的对称加密方式。下一章,我们把它讲透,并亲手篡改一段密文看它当场报警。

本章要点

  • 分组模式规定"多个 16 字节块如何衔接"。选错模式,AES 再强也没用。
  • ECB 独立加密每块,相同明文块→相同密文块,泄露重复结构("ECB 企鹅")。永远不要用。
  • CBC(链式)、CTR(计数器)借助随机 IV/nonce 消除确定性;CTR 把分组密码变成流密码。
  • 但 CBC/CTR 都不提供完整性——催生 padding oracle 等攻击,引出下一章的 AEAD。