AES 模式怎么选:ECB 为什么不能用、CBC 缺了什么、GCM 好在哪

· 约 6 分钟 🔒 AES 加解密

选模式这件事,看上去像是在一堆缩写里挑一个,实际上是在决定「加密到底保护了什么」。选错的后果不是性能差一点,而是加密做了等于没做

先给结论:

新代码用 GCM。老系统迁不动用 CBC 加 HMAC。任何情况下不要用 ECB。

下面是理由。

模式对照

模式需要 IV需要填充可并行防篡改结论
ECB❌ 泄漏明文结构
CBC是(16 字节)仅解密⚠️ 必须外加 MAC
CFB仅解密已被 CTR 取代
OFB已被 CTR 取代
CTR是(nonce)⚠️ nonce 绝不能重用
GCM是(12 字节 nonce)✅ 是✅ 首选

注意「防篡改」那一列——只有 GCM 是勾。这一列就是选型的分水岭。

ECB:把明文的结构原样印在密文上

ECB 的做法是把明文切成 16 字节一组,每组独立加密。结果就是:相同的明文分组必然产生相同的密文分组。

最有名的演示是拿 ECB 加密一张位图,加密后企鹅的轮廓依然清晰可见。但别被「那是图片」误导,在业务数据上后果一样严重:

等值关联泄漏。 数据库里加密存储手机号,相同号码的密文也相同。攻击者不用解密就能:统计哪个号码出现得最多、把不同表里的同一个人关联起来、拿常见值做字典比对。

分组重排攻击。 密文分组彼此独立,可以剪切、复制、调换顺序。攻击者能从记录 A 剪一段密文贴到记录 B,拼出一条从未被加密过、但你的系统会正常解密的数据。转账金额、权限等级这类固定位置的字段尤其危险。

结构泄漏。 固定格式的数据里,哪些字段相同、哪些重复出现,都直接写在密文的模式里。

唯一可以接受的场景:明文总长不超过 16 字节,且每次的值都不重复(比如加密一个随机 token)。此时 ECB 和 CBC 没有实质差别。除此之外没有理由用它。

CBC:解决了图案问题,没解决篡改问题

CBC 让每个明文分组先和前一个密文分组异或再加密,第一个分组用 IV 顶上。这样相同的明文分组不再产生相同的密文,ECB 的图案问题解决了。

但 CBC 只提供保密性,它无法告诉你密文有没有被改过。这带来两类攻击:

比特翻转

翻转密文第 N 个分组的某一比特,解密后第 N+1 个分组的对应比特会跟着翻转。攻击者不需要密钥,只要猜到明文的结构,就能定向改动它。明文里有 "role":"user" 这种东西时,这不是理论问题。

padding oracle

只要系统在「填充错误」和「其它错误」上表现出任何差异——不同的错误码、不同的报错文案、甚至只是不同的响应时间——攻击者就能一个字节一个字节地把密文解出来,全程不需要密钥

这是 Vaudenay 在 2002 年提出的攻击,二十多年来在 TLS、ASP.NET 和各类 Web 框架上反复出现,是最顽固的一类实现漏洞。

所以 CBC 必须配 MAC

顺序很重要:先加密,再对密文算 MAC(Encrypt-then-MAC)。验证 MAC 失败就直接拒绝,根本不要进入解密流程——这样 padding oracle 就没有可以观察的差异了。

反过来(先算 MAC 再加密,或者对明文算 MAC)仍然会漏出 oracle。另外加密密钥和 MAC 密钥要分开,不要复用同一把。

既然都要额外加一层,不如直接用 GCM。

CTR 和 GCM:nonce 是唯一的红线

CTR 把「nonce + 计数器」加密成密钥流,再和明文异或。好处很实在:不需要填充、加解密都能并行、能跳到任意位置解密。

代价是它对 nonce 重用零容忍

同一把密钥 + 同一个 nonce
  → 完全相同的密钥流
  → 密文A ⊕ 密文B = 明文A ⊕ 明文B

密钥在这里被彻底绕过了。两段明文异或的结果,在很多情况下可以直接还原出原文。

GCM 下重用更严重一档:除了上面这条,还会泄漏 GHASH 的认证子密钥 H。拿到 H 之后,攻击者可以对任意密文伪造出合法的认证标签——完整性保护整个失效。这就是所谓的 forbidden attack。

怎么保证 nonce 不重复

  • 每次用 12 字节的密码学安全随机数 —— GCM 的推荐做法,也是最不容易做错的
  • 持久化递增计数器 —— 理论上更好,但要保证进程重启、多实例部署、数据库回滚都不会让它倒退,工程上比随机数难做对得多
  • 同一把密钥不要接近 2³² 次加密 —— 用随机 nonce 时,超出这个量级碰撞概率就不可忽略了,该轮换密钥

最危险的写法是把 nonce 写死成常量。 这在老代码里出奇地常见,通常是因为「加密时要传个 IV,随便填一个先跑通」,然后就上线了。

GCM 的三样东西缺一不可

密文、nonce认证标签(16 字节)——三样都要传给解密方。nonce 和 tag 都不需要保密,但都不能丢。对接时漏传 tag 是常见问题。

CFB 和 OFB:了解就行

它们当年的价值是「不需要填充」,密文和明文等长。但 CTR 同样不需要填充,而且加解密都能并行、支持随机访问,在多核 CPU 上快得多。

演进线很清楚:

CFB / OFB  →  CTR  →  GCM
(免填充)    (可并行)  (加完整性)

越往后越没有理由退回去。

DES 和 3DES:只用来读历史数据

DES 的密钥只有 56 位。1998 年就被专用硬件在几天内破解,今天用云上算力是小时级的事。

3DES 把有效密钥提到 112 位,抗暴力破解没问题,但分组长度只有 64 位。按生日界,同一把密钥加密约 32 GB 数据后就会出现分组碰撞,可据此恢复明文——这就是 2016 年的 Sweet32。NIST 在 SP 800-131A Rev.2 中已将 3DES 的加密用途列为 disallowed。

AES 的分组是 128 位,同样的碰撞门槛远在任何现实数据量之外。

本站工具保留 DES 和 3DES,是为了帮你把历史数据解出来再用 AES 重新加密,不是鼓励在新代码里用。

决策树

需要防篡改吗?(数据经过不可信链路 / 存在不可信的地方)
├─ 是(绝大多数场景)
│   ├─ 环境和对接方都支持 GCM? → ✅ GCM,nonce 每次随机 12 字节
│   └─ 不支持              → CBC + HMAC-SHA256(先加密再算 MAC,双密钥)
└─ 否(罕见,且你确定)
    ├─ 明文 ≤ 16 字节且不重复 → ECB 勉强可以
    └─ 其它                  → CBC,IV 每次随机

动手看看差别

AES 加解密支持全部六种模式,可以直接观察它们的行为差异:

  • ECB 的图案泄漏:把 AAAAAAAAAAAAAAAA 重复三遍加密,会看到密文里出现三段一模一样的分组
  • CBC 的 IV 效应:同样的明文和密钥,换个 IV,密文完全不同
  • GCM 的完整性:把密文改动一个字符再解密,会直接验证失败,而不是吐出一段乱码

工具在浏览器本地计算,密钥和明文都不会离开设备。AES 三种密钥长度均通过 FIPS-197 官方测试向量验证,并与 OpenSSL 逐字节交叉核对过。

如果两端结果对不上,模式只是四个变量之一,另外三个(密钥编码、IV 来源、填充方式)见同样的密钥和明文,为什么各家在线 AES 工具算出来不一样

❓ 常见问题

一句话说,我到底该选哪个模式?

新写的代码选 GCM,改不动的老系统用 CBC,任何情况下别用 ECBGCM 为什么是默认答案:它在保密性之外还提供完整性校验——密文被改一个比特,解密时就会验证失败并明确报错,而不是安静地吐出一段错误明文。这一条是 CBC 完全不具备的。什么时候只能退回 CBC:(1) 对接方的老系统只支持 CBC;(2) 运行环境的库没有 GCM 实现。这种情况下必须额外加一层 HMAC,按「先加密再对密文算 MAC」的顺序,否则你的系统只有保密没有防篡改。ECB 的适用范围:明文总长不超过一个分组(16 字节)且每次不重复,比如加密一个随机 token。超出这个范围一律不要用。

ECB 到底有什么问题?我的数据又不是图片

问题不在图片,在于「相同明文分组产生相同密文分组」这条性质本身。用位图演示只是因为它最直观——ECB 加密一张企鹅图,加密后轮廓仍然清晰可辨。但在业务数据上,后果一样严重:(1) 等值关联泄漏——数据库里加密存储的身份证号或手机号字段,相同的值密文也相同,攻击者不用解密就能统计频次、把不同表的记录关联起来,甚至用已知的常见值做字典比对;(2) 分组重排攻击——密文分组彼此独立,攻击者可以剪切、复制、调换分组顺序,拼出一条从未被加密过、但能被你的系统正常解密的记录。比如把「转账金额」那一段密文换成从另一条记录里剪来的密文;(3) 结构泄漏——固定格式的数据(JSON、定长记录)里哪些字段相同、哪些字段重复出现,都会直接暴露在密文的模式里。

CBC 已经加了 IV,为什么还说它不安全?

CBC 提供的是保密性,不是完整性——它不能告诉你密文有没有被人改过具体的攻击面有两个:(1) 比特翻转——攻击者翻转密文第 N 个分组的某一比特,解密后第 N+1 个分组的对应比特会跟着翻转。如果明文里有 "role":"user" 这样的结构,攻击者不需要知道密钥就能定向改动它;(2) padding oracle——只要系统在填充错误和其它错误上表现不同(不同的报错、不同的响应时间),攻击者就能一个字节一个字节地把密文解出来,完全不需要密钥。这是 Vaudenay 在 2002 年提出的攻击,之后在 TLS、ASP.NET、各类框架上反复出现。结论:CBC 必须搭配 MAC 使用,且顺序是先加密、再对密文算 MAC(Encrypt-then-MAC)。反过来做仍然会漏出 padding oracle。既然都要加一层,不如直接用 GCM——它本来就是这么设计的。

nonce 重用有多严重?CTR 和 GCM 一样吗?

都是灾难,但 GCM 更严重一档CTR 下重用:CTR 把 nonce 加计数器加密成密钥流,再和明文异或。同一个 nonce 加同一把密钥会产生完全相同的密钥流,两段密文异或就等于两段明文异或——密钥被彻底绕过,明文在很多情况下可以直接还原出来。GCM 下重用:除了上面这一条,还会额外泄漏 GHASH 的认证子密钥 H。拿到 H 之后攻击者可以对任意密文伪造出合法的认证标签,也就是说完整性保护整个失效了,这就是所谓的 forbidden attack。实务上怎么保证不重用:(1) 每次加密用 12 字节的密码学安全随机数,这是 GCM 的推荐做法;(2) 或者用一个持久化的递增计数器,但要保证进程重启、多实例部署、数据库回滚都不会让它倒退——比随机数难做对得多;(3) 同一把密钥用随机 nonce 时,加密次数不宜接近 2 的 32 次方,超出后碰撞概率不再可忽略,该轮换密钥了。最危险的写法是把 nonce 写死成常量,这在老代码里出奇地常见。

DES 和 3DES 还能用吗?

新系统一律不要用,老系统尽快迁移DES:密钥只有 56 位,1998 年就被专用硬件在几天内暴力破解,如今用云上的算力是小时级的事情。它的存在价值只剩下解读历史数据。3DES:三重 DES 把有效密钥提到 112 位,抗暴力破解没问题,但它有个绕不过去的结构缺陷——分组长度只有 64 位。按生日界,同一把密钥加密的数据量接近 2 的 32 次方个分组(约 32 GB)时就会出现分组碰撞,可以据此恢复明文,这就是 2016 年的 Sweet32 攻击。NIST 在 SP 800-131A Rev.2 里已经把 3DES 的加密用途列为 disallowed。AES 相比之下:分组 128 位,同样的碰撞门槛远在任何现实数据量之外。如果你在维护一个还在用 3DES 的系统,本站工具保留了 DES 和 3DES 的实现,就是为了帮你把历史数据解出来、再用 AES 重新加密,而不是鼓励你在新代码里用它。

CFB、OFB 这两个模式还有必要了解吗?

了解即可,新代码基本用不到它们的共同点:都把分组密码变成流密码,明文多长密文就多长,不需要填充——这是当年它们存在的主要理由(省掉填充意味着密文不会变长,对定长字段和低带宽链路有意义)。各自的特点:(1) CFB——密文反馈,加密不能并行、解密可以并行,出错会传播到下一个分组;(2) OFB——输出反馈,密钥流和明文无关,可以预先算好,但完全不能并行,且一旦 IV 重用,问题和 CTR 一样严重。为什么被 CTR 取代:CTR 同样不需要填充,但加密解密都能并行,还能直接跳到任意位置解密(随机访问),在现代多核 CPU 上快得多。为什么 CTR 又被 GCM 取代:GCM 就是 CTR 加上一个 GHASH 认证,几乎不增加成本就补上了完整性。所以这条演进线是:CFB / OFB → CTR → GCM,越往后越没有理由退回去。

🔒 打开 AES 加解密 AES/DES/3DES·ECB/CBC/CFB/OFB/CTR/GCM·PKCS7 填充·UTF-8/Hex/Base64 密钥·本地运行不上传

📖 同一工具的其他教程

🔗 相关阅读

全部教程 →