← 返回文章列表

AES 加密实操:模式、IV 与认证加密的区别

加密避坑

AES 只是“一块”算法

AES 每次只处理 16 字节的分组。要让它能加密任意长度的数据,需要工作模式(GCM、CBC 等)把分组串起来。所以只说“我用 AES 加密”是不够的,还必须说明模式和参数。

模式对比

模式是否认证IV 要求建议
GCM是(AEAD)12 字节随机,绝不可复用首选,兼顾保密与防篡改
CBC否16 字节随机需自行加 HMAC 才能防篡改
ECB否无不要用:相同明文块产生相同密文块

IV(初始向量)为什么不能复用

GCM 用 IV 与计数器生成密钥流。同一个密钥下重复使用 IV,会让两段密文的密钥流重合,攻击者异或即可还原明文,同时认证标签也会失效。实践中每次加密都随机生成 IV,并把它与密文一起存储(IV 无需保密)。

正确的加密流程

  1. 随机生成 12 字节 IV;
  2. 用 AES-GCM 加密,得到密文与认证标签;
  3. 把 IV + 密文 + 标签 一起存储或传输(注意不要把 IV 也加密);
  4. 解密时必须验证标签,验证失败就直接报错,绝不使用部分明文。

密钥从哪来

用户口令不能直接当密钥——它的熵远低于 128 位。应通过 KDF(PBKDF2、scrypt、Argon2)从口令派生密钥,并设置足够的工作因子;或直接用 CSPRNG 生成随机密钥,再讨论如何安全保存。

常见问题

IV 和盐是一回事吗?不是:IV 用于加密过程、保证同一明文每次密文不同;盐用于派生密钥、保证相同口令得到不同密钥。加密后还要哈希吗?AEAD 模式已内建完整性校验,无需再叠加哈希;用 CBC 时才需要额外做 MAC。

存储格式建议

// 让 IV 与标签和密文一起存,格式自带版本号与算法标识
{
  "v": 1,              // 版本号,便于日后升级算法
  "alg": "AES-GCM",
  "iv": "base64...",   // 12 字节随机
  "ct": "base64...",   // 密文
  "tag": "base64..."   // 认证标签(部分库与密文合并输出)
}

密钥怎么保存

  1. 服务端密钥放在密钥管理服务或环境变量中,不要写进代码与仓库;
  2. 需要长期保存数据时用信封加密:由 KMS 里的主密钥保护每条数据的数据密钥;
  3. 客户端加密场景,密钥交给密码管理器或系统钥匙串,不要放 localStorage;
  4. 提前规划轮换:算法升级或密钥疑似泄露时要能平滑迁移——这正是加版本号的原因。

实战案例:三个易错点

  1. “同一明文每次密文不同,是不是坏了?”没坏。CBC/GCM 每次使用随机 IV,密文自然不同;解密时要带上同样的 IV。
  2. “改了密文一个字节,解密直接报错”:这是 GCM 等认证加密的正常行为——它检测到篡改并拒绝解密,比 CBC 只返回乱码更安全。
  3. “生产环境用 ECB 被安全评审打回”:ECB 不用 IV,相同明文块产生相同密文,会泄露图片与数据结构,敏感场景应禁用。

常见问题(FAQ)

密钥怎么安全地传给别人?密钥与密文分开、走不同通道传递(如密钥平台、线下),不要放在同一条消息里。IV 要一起保存吗?要,IV 不是秘密,但解密必须用到,通常随密文一起存。AES-GCM 的 tag 是什么?它是完整性校验值,用于验证密文未被篡改;缺了它就无法校验完整性。密钥泄漏了怎么办?立即轮换密钥并重新加密数据——对称加密没有“改密码”这种便利。

动手试试:对称加密、哈希计算

nonce 复用的后果

  • GCM 下灾难性:同一密钥下 nonce 复用会让攻击者恢复认证密钥并伪造消息,这比单纯的明文泄露更严重;
  • CBC 下可推断明文:相同 IV 与相同明文前缀会得到相同密文块,从而暴露内容结构(ECB 的“企鹅图”就是极端例子);
  • 生成方式:优先用随机 96 位 nonce 并限制单密钥加密次数,或改用计数器式 nonce 并确保不会回绕;
  • 密钥轮换:接近次数上限时必须换密钥,不要无限制地用同一密钥加密。