3.3 分组密码工作模式:ECB 的教训与 GCM 的胜出


3.3 分组密码工作模式:ECB 的教训与 GCM 的胜出

本节摘要:分组密码只处理固定长度的块,把任意长度的消息串成块序列的方案叫工作模式。模式选错,再强的算法也会当场泄露内容:ECB 模式下密文会原样复刻明文的块级结构,著名的"密文企鹅"就拜它所赐。本节对比 ECB、CBC、CTR 与 GCM 四种代表模式的结构与适用场景,并用一个实验证明"随机数重用"为什么是灾难级事故。

密文为什么会画出企鹅

1990 年代,密码学圈流传出一张著名图片:一张企鹅照片被 ECB 模式加密后,密文图像里依然能清楚看出企鹅轮廓。这不是算法被攻破,是模式背叛了算法。ECB(电子密码本)模式把消息切成块,每块独立加密:同一把密钥下,相同的明文块必然产生相同的密文块。分组密码内部那 16 轮 Feistel 或 SPN 搅碎了"块内"的统计结构,但"块与块之间"的关系被 ECB 原封不动地保留——图像大面积相同的区域、数据库里重复的记录、协议包里固定位置的字段,全部在密文里原样显形。

ECB 的失败揭示了一个原则性要求:语义安全——同一明文每次加密的结果应当不同,否则敌手只凭"这两处一样"就能获得信息。实现语义安全的标准办法是引入随机化的初始向量(IV)并把块串起来,这正好催生了 CBC 与 CTR 两种主流模式。

图:三种工作模式的结构对比

图:三种工作模式的结构对比

二、四种模式的取舍清单

维度 ECB CBC CTR GCM
块间结构 无链接,相同块同密文 密文链 + IV 计数器造密钥流 CTR 加密 + GHASH 认证
语义安全 有(IV 随机) 有(nonce 唯一) 有(nonce 唯一)
并行加密 不可(串行链)
随机访问解密 需先解前一块 可(跳到任意计数器)
附带完整性 无(需另加 HMAC) 无(需另加认证) 有,输出认证标签
现状 禁用于新设计 遗留系统与 TLS 旧版 作为 GCM 底层广泛使用 TLS 1.3 主力(AEAD)

CBC 的链条式设计比 ECB 高明,但有串行化的代价:块与块必须按序处理,多核 CPU 无用武之地。CTR 把分组密码当成"密钥流发生器"——用计数器逐块生成伪随机流再与明文异或——既恢复了并行性,又允许从任意字节处开始解密(视频拖动进度条的底气)。而它对 nonce 唯一性的要求是绝对命令:计数器重用一次,上面的减法实验就当场兑现,两份密文的异或直接等于两份明文的异或。

GCM 在 CTR 外面又包了一层 GHASH 认证,把"加密 + 完整性"合成一次调用,输出一个认证标签——这类组合叫 AEAD(带关联数据的认证加密)。TLS 从 1.2 起力推 AES-GCM,到 1.3 干脆只保留 AEAD 套件(AES-GCM 与 ChaCha20-Poly1305),把 3.5 节要讲的"加密与认证次序之争"直接用协议格式终结。

用 Python 复现随机数重用实验,亲眼看密钥流消掉:

import hashlib def ctr_keystream(key: bytes, nonce: bytes, n: int) -> bytes: """以 nonce+计数器为输入造密钥流(演示用,非标准 GCM)""" stream, ctr = b"", 0 while len(stream) < n: blk = hashlib.sha256(key + nonce + ctr.to_bytes(8, "big")).digest() stream += blk ctr += 1 return stream[:n] key, nonce = b"demo-key", b"0001" p1, p2 = b"MOVE FUNDS TO ACCOUNT A", b"MOVE FUNDS TO ACCOUNT B" c1 = bytes(a ^ b for a, b in zip(p1, ctr_keystream(key, nonce, len(p1)))) c2 = bytes(a ^ b for a, b in zip(p2, ctr_keystream(key, nonce, len(p2)))) # nonce 复用! diff = bytes(a ^ b for a, b in zip(c1, c2)) print("两个密文的异或 =", diff) # 密钥流成对消掉 print("两个明文的异或 =", bytes(a ^ b for a, b in zip(p1, p2))) print("两者相等,且只有 A/B 两字节不同——差异暴露无遗")

⚠️ 常见坑:随机数(IV/nonce)不当使用是实践中最高频的对称密码事故——重复的 GCM nonce 不仅泄露内容,还会泄露认证密钥使伪造成为可能。规则只有一条:同一密钥下,nonce 绝不重复;不确定时就换密钥。

模式选型的三个问答

问一:遗留系统还在用 CBC,要立刻换吗?若 CBC 之外有独立认证(先加密后 MAC),风险可控,按升级路线图走;若靠 MAC 后加密或干脆无认证,应提高优先级——填充预言机不会等你。

问二:磁盘加密为什么用 XTS 而不是 GCM?盘块的原地改写、随机访问与"不认证内容"的语义和 XTS 的设计完全匹配;完整性由文件系统与日志层另行负责,加密层只管机密性。

问三:CBC 的 IV 与 CTR 的 nonce 生成规则一样吗?不一样。CBC 的 IV 要不可预测(防首块被针对性选择),CTR 的 nonce 在同一密钥下绝不重复即可(可预测无妨,重复即灾难)。两者常被混用配置,这是模式事故里最隐蔽的一类。

边界补充:填充与流式边界

块模式还牵出一个不起眼却事故频出的角落:消息长度不是块长的整数倍怎么办。CBC 一类模式依赖显式填充(如 PKCS 系列:缺几字节就填几个"缺几"的数值),解密端必须先剥离填充再验内容,剥离逻辑就是填充预言机攻击的主战场;CTR 与流密码没有填充,但也因此要额外携带长度与认证标签,否则截断攻击无法被发现。工程建议一句话:填充满理由交给标准库,边界长度交给认证标签,永远不要手写这两段逻辑。

本节要点回顾

  • 模式决定语义安全:ECB 无链接、相同块同密文,结构泄露致其在新设计中禁用;
  • CBC 以 IV 与密文链实现语义安全但串行化,填充预言机风险使其退出 TLS 1.3;
  • CTR 用计数器造密钥流,并行与随机访问兼得,但 nonce 绝对不可重用;
  • GCM = CTR + GHASH,一次调用同时给出密文与认证标签(AEAD),是当前 TLS 主力;
  • 历史回响:流密码思想(逐位掩码)在 CTR 与 GCM 中借分组密码之壳复活。

模式是把分组密码"装进"真实消息的胶水。下一节看另一条平行演化的路线——天生逐位处理的流密码,以及 RC4 从不可一世到全网禁用的兴衰。


作者与出处
原作者: 灏天文库
来源:灏天文库
整理: 灏天文库整理
由灏天文库平台收录,内容或由平台用户上传,仅供学习交流
发布者: 作者: 灏天文库 转发
评论区 (0)
U