🏠 总目录📚 本教程 07 · 工作模式
📑 本页目录(点开跳转)

07 · 工作模式

26 分钟 | ⭐ 一张图就能看懂 ECB 为什么该死


🎯 一句话

分组密码一次只能加密 128 bit。长消息怎么办? 答案是「工作模式」—— 而选错模式,比选错算法后果严重得多。


🐧 一、ECB:那张著名的企鹅图

   最直觉的做法:把消息切块,每块单独加密

   C₁ = E_K(M₁)
   C₂ = E_K(M₂)
   C₃ = E_K(M₃)
   ...

为什么它是错的

   E_K 是【确定性】的
   → 相同的明文块 ⟹ 相同的密文块 ⭐

   → 明文的【结构】原样保留在密文里
   著名的实验:把一张企鹅图片用 ECB 加密

   原图:  🐧 清晰的企鹅
   ECB后: 🐧 还是能看出企鹅的轮廓!只是颜色变成噪声

   因为图片里大片相同颜色的区域 → 加密成大片相同的密文块

🔑 这也是第 8 章 IND-CPA 的第一个反例: 攻击者提交 m₀ = "AAAA...AAAA"(两个相同块)和 m₁ = "AAAA...BBBB"(两个不同块), 看密文的两个块是否相同就能百分之百分辨 —— 连一点点优势都不需要猜。

一句话结论ECB 在任何安全场景下都不该出现。 如果你在代码里看到 AES/ECB/PKCS5Padding,那基本就是一个 bug。


🔗 二、CBC:把前一块的密文混进来

   C₀ = IV                        ← 随机初始向量
   Cᵢ = E_K(Mᵢ ⊕ Cᵢ₋₁)

   解密:Mᵢ = D_K(Cᵢ) ⊕ Cᵢ₋₁
   M₁      M₂      M₃
   │       │       │
   ⊕◄─IV   ⊕◄──┐   ⊕◄──┐
   │       │   │   │   │
  [E]     [E]  │  [E]  │
   │       │   │   │   │
   C₁──────────┘   C₂──┘   C₃

💡 人话每一块加密前先和上一块的密文异或 —— 让相同的明文块产生不同的密文块。

性质 CBC
加密 ❌ 不能并行(Cᵢ 依赖 Cᵢ₋₁)
解密 ✅ 可以并行(Mᵢ 只需要 Cᵢ 和 Cᵢ₋₁)
IV 要求 必须随机且不可预测(不只是唯一!)
需要填充 ✅ 是 —— 这是它最大的麻烦来源

💥 CBC 的两个真实事故

① BEAST 攻击(2011)—— IV 可预测

   TLS 1.0 用【上一条消息的最后一个密文块】当下一条的 IV
   → IV 变得【可预测】

   → 攻击者可以构造明文,验证他对某个明文块的猜测 ⭐
   → 逐字节破解 cookie

🔑 这就是为什么 CBC 的 IV 必须"随机且不可预测",而不只是"唯一"。 对比 CTR 模式的 nonce:那个只需要唯一。这是两个不同的要求。

② Padding Oracle 攻击 —— 填充错误泄露信息

   CBC 需要填充到分组边界,解密后要检查填充是否合法

   如果服务器对"填充错误"和"其他错误"返回不同的响应
   (不同的错误码、不同的响应时间、甚至只是不同的日志)
   → 攻击者获得了一个【解密预言机】⭐

   → 每次询问泄露 1 bit,约 256 次询问就能解出 1 个字节
   → 完整解密任意密文,【完全不需要密钥】💀

💥 打穿过:ASP.NET(2010,可读取 web.config)、Ruby on Rails、 Java 的多个框架、以及大量自制的"加密 cookie"方案。

🔑 教训这就是第 1 章说的 CCA 攻击的真实形态。 而它的根本解法不是"隐藏错误信息"(太容易漏),而是 —— 先验证 MAC,验证不通过就根本不解密第 11 章的 Encrypt-then-MAC)。


⚡ 三、CTR:把分组密码变成流密码

   Cᵢ = Mᵢ ⊕ E_K(nonce ‖ i)
                     ↑ 计数器递增

   ⭐ 注意:加密和解密【完全相同】,都是异或
   ⭐ 注意:E_K 从来没有被"反着用"过 —— 只用了加密方向
性质 CTR
加密/解密 都能完全并行
需要填充 ❌ 不需要(明文多长密文多长)
随机访问 ✅ 想解第 n 块直接算 E_K(nonce‖n)
只需要 E_K ✅ 硬件/代码更小(不用实现解密方向)
nonce 要求 唯一即可,不需要随机
nonce 重复 💀 等于密钥复用的 OTP第 5 章

🔑 CTR 是现代的默认选择 —— GCM、ChaCha20-Poly1305 内部都是 CTR 结构。


📊 四、对比表(可以直接拿去用)

ECB CBC CTR
安全性 💀 绝不使用 ⚠️ 能用但坑多 ✅ 好
并行加密
并行解密
需要填充
IV/nonce 随机且不可预测 唯一即可
随机访问 部分
提供完整性

注意最后一行:三个都不提供完整性。 这就是为什么这三个模式今天都不该单独使用 —— 见下。


✅ 五、那实际该用什么

   ❌ 不要单独用任何一个上面的模式

   ✅ 用【认证加密 AEAD】:
   ├─ AES-GCM              ← CTR + GMAC,有硬件加速时最快
   ├─ ChaCha20-Poly1305    ← 无硬件加速时更快,移动端首选
   └─ AES-GCM-SIV          ⭐ nonce 重复时"只泄露该消息重复",不会灾难性崩溃
# ✅ 这才是你应该写的代码
from cryptography.hazmat.primitives.ciphers.aead import AESGCM
import os

key = AESGCM.generate_key(bit_length=256)
aead = AESGCM(key)

nonce = os.urandom(12)                        # ⚠️ 每条消息必须不同
ct = aead.encrypt(nonce, b"transfer 100 CNY", b"account-id-42")
#                                              ↑ AAD:不加密但被认证的数据

pt = aead.decrypt(nonce, ct, b"account-id-42")  # 篡改任一部分都会抛异常 ⭐

💡 AAD(附加认证数据)是个好东西: 像消息头、路由信息这类需要明文可见、但不能被篡改的字段,放进 AAD。

AES-GCM-SIV 值得单独提一句: 普通 GCM 一旦 nonce 重复就是灾难(能恢复认证密钥,伪造任意消息)。 SIV 模式做到"nonce 误用弹性" —— 重复时最多泄露"这两条消息相同", 不会让整个密钥崩溃。如果你无法保证 nonce 唯一,就用它。


🔗 和站内其他章的关系

相关的地方 和这一章的关系
第 6 章 分组密码只能处理 128 bit 工作模式解决"长消息怎么办"
第 6 章 PRP 可当 PRF CTR 模式的理论依据
第 5 章 nonce 铁律 CTR 直接继承
第 1 章 CCA 攻击 padding oracle 是它的真实形态
第 11 章 Encrypt-then-MAC padding oracle 的根本解法
大模型 · 合规与伦理 ⚠️ ECB 那只企鹅在脱敏场景里天天重演:用确定性映射替换 PII,相同的值仍然落到相同的标记,结构原封不动地留在数据里 ⭐
智能体教程 · 工具设计 一个正面冲突的取舍:那边要求错误信息尽量详细("写给模型看的教学材料"),而在这里,详细的错误信息就是 padding oracle ⭐
AI 基础设施 · 流水线并行 CBC 加密不可并行(Cᵢ 依赖 Cᵢ₋₁)和流水线的「气泡」是同一种顺序依赖,缓解办法也一样:切小块,让它们互相重叠

✅ 检查点

  1. ECB 为什么不安全?用 IND-CPA 的语言说一遍。
  2. CBC 的公式是什么?它的 IV 有什么要求?和 CTR 的 nonce 要求有什么不同?
  3. BEAST 攻击利用了什么?
  4. Padding oracle 攻击的原理是什么?根本解法是什么?
  5. CTR 模式有哪四个优点?
  6. 为什么 CTR 模式只需要实现 E_K,不需要 D_K?
  7. 三种模式都不提供什么?该用什么代替?
  8. AES-GCM-SIV 解决了什么问题?
👀 答案
  1. 因为 E_K 是确定性的,相同明文块产生相同密文块,明文结构原样保留(企鹅图)。IND-CPA 语言:攻击者提交 m₀="AAAA...AAAA"(两块相同)和 m₁="AAAA...BBBB"(两块不同),看密文两块是否相同就 100% 分辨
  2. Cᵢ = E_K(Mᵢ ⊕ Cᵢ₋₁),C₀ = IV。IV 必须随机且不可预测;CTR 的 nonce 只需要唯一,不需要随机。这是两个不同的要求。
  3. 利用 TLS 1.0 用上一条消息的最后一个密文块当下一条的 IV,使 IV 可预测,攻击者能构造明文验证他对某明文块的猜测,逐字节破解 cookie。
  4. 服务器对"填充错误"和其他错误返回不同响应(错误码/时间/日志),攻击者获得解密预言机,每次询问泄露 1 bit,约 256 次解出一字节,不需要密钥就能完整解密。根本解法:Encrypt-then-MAC——先验证 MAC,验证不通过就根本不解密
  5. ①加解密都能完全并行不需要填充 ③支持随机访问 ④只需实现加密方向。
  6. 因为解密是 Mᵢ = Cᵢ ⊕ E_K(nonce‖i)——E_K 从来没被反着用过,加解密都是"生成密钥流再异或"。
  7. 都不提供完整性。该用 AEAD:AES-GCM、ChaCha20-Poly1305、AES-GCM-SIV。
  8. nonce 误用弹性。普通 GCM 一旦 nonce 重复是灾难(能恢复认证密钥、伪造任意消息),SIV 重复时最多泄露"这两条消息相同",不会让密钥崩溃。无法保证 nonce 唯一时就用它。

🛑 可以停在这里

走神救援

分组密码一次只能加密128bit,长消息靠「工作模式」——选错模式比选错算法后果严重ECB(每块单独加密)💀绝不使用:E_K 确定性 → 相同明文块产生相同密文块 → 明文结构原样保留(企鹅图);IND-CPA 语言:提交"AAAA|AAAA"和"AAAA|BBBB",看密文两块是否相同就100%分辨CBC:Cᵢ=E_K(Mᵢ⊕Cᵢ₋₁),⭐IV 必须随机且不可预测(不只是唯一)——💥BEAST 就是因为 TLS1.0 用上条消息的末块当IV使其可预测,逐字节破解cookie;💥Padding Oracle:服务器对"填充错误"返回不同响应 = 送给攻击者一个解密预言机,约256次询问解出一字节,完全不需要密钥(打穿过 ASP.NET/Rails)→ ⭐根本解法不是隐藏错误信息,而是 Encrypt-then-MAC:先验MAC,不过就根本不解密CTR:Cᵢ=Mᵢ⊕E_K(nonce‖i),⭐加解密完全并行、不需填充、支持随机访问、只需实现加密方向(E_K 从没被反着用过);nonce 只需唯一,但重复=密钥复用的OTP。⭐三种模式都不提供完整性 → 都不该单独使用。✅实际用 AEAD:AES-GCM(有硬件加速最快)、ChaCha20-Poly1305(移动端)、⭐AES-GCM-SIVnonce 误用弹性——普通GCM的nonce重复会泄露认证密钥导致可伪造任意消息,SIV 最多泄露"两条消息相同")。AAD 放需要明文可见但不能被篡改的字段(消息头、路由信息)。

下一节 👉 08-安全定义.md ⭐⭐

打卡记录保存在你的浏览器里,首页能看到总进度