# AES IV 与 Nonce 重用：风险与防范

> 理解 IV 或 Nonce 重用为何会破坏 AES-CBC、CTR 和 GCM 的安全性，并学习安全生成、存储及并发分配方案。

Canonical: https://hsmkit.com/zh/guides/aes-iv-nonce-reuse/

Last updated: 2026-09-22

AES 的初始化向量（IV）或 Nonce 是公开元数据，不是第二把秘密密钥。它们的作用是保证重复执行加密时仍然安全。长度正确并不代表使用正确：不同模式对唯一性或不可预测性有不同要求，而重用 IV/Nonce 是现实系统中破坏性最强的加密错误之一。

## IV、Nonce 与计数器的区别

这些术语有交集，但安全要求不同：

- **IV** 用于初始化 CBC 等模式。CBC 要求随机、不可预测的 16 字节 IV；
- **Nonce** 即“只使用一次的数”。GCM 通常要求唯一的 12 字节 Nonce；
- **计数器**是按固定规则递增且不重复的结构化 Nonce，常用于 CTR 类模式。

这些值通常与密文一起保存。隐藏 IV 不能修复错误设计，而公开正确生成的 IV 也不会泄露密钥。

## 重用会破坏什么

| 模式 | 要求 | 重用后果 |
|---|---|---|
| CBC | 新的、不可预测的 16 字节 IV | 相同首分组产生相同首密文；可预测 IV 可被选择明文攻击利用 |
| CTR | 同一密钥下计数器流唯一 | 形成“两次一密”，异或密文会暴露明文异或值 |
| GCM | 同一密钥下 Nonce 唯一 | CTR 机密性失效，认证能力也可能被破坏 |
| OFB | 同一密钥下 IV 唯一 | 密钥流重复，暴露明文关系 |

### CTR 与 GCM：密钥流重复

CTR 加密计数器分组，再与明文进行 XOR。如果相同密钥和初始计数器被重用：

```text
C1 = P1 XOR S
C2 = P2 XOR S
C1 XOR C2 = P1 XOR P2
```

秘密密钥流 `S` 被抵消。只要某段明文具有固定结构或可被猜测，另一段明文就可能被恢复。GCM 内部使用 CTR，因此继承此问题；Nonce 重用还会在 GCM 认证计算中产生代数关系，风险不仅是明文泄露。

### CBC：首分组关系泄露

CBC 在加密前将第一个明文分组与 IV 进行 XOR。同一密钥下重用 IV，会泄露两条消息的首个明文分组是否相同。可预测 IV 还可能让能控制明文的攻击者测试分组关系。

CBC 的 IV 重用不等同于 GCM 的 Nonce 重用，但同样违反模式的安全前提，必须作为缺陷处理。

## 安全生成方案

### CBC 随机 IV

每次加密生成 16 个随机字节：

```text
IV = CSPRNG(16 字节)
密文 = IV || AES-CBC(密钥, IV, 填充后明文)
```

使用操作系统或密码库提供的安全随机源。不要使用 `Math.random()`、时间戳、UUID 文本、明文哈希或固定配置值。

### GCM 随机 Nonce

多数应用可使用密码学安全随机源生成 12 字节 Nonce。随着同一密钥下消息数量增长，碰撞概率会上升，因此应设定每把密钥的消息上限，并在风险达到不可接受水平前轮换密钥。

在分布式系统中，仅使用随机数并不等于完成设计。多个进程、虚拟机快照恢复或随机状态异常都可能造成重复。

### GCM 结构化 Nonce

高吞吐系统可以组合唯一实例前缀与单调递增计数器：

```text
Nonce = 32位实例ID || 64位消息计数器
```

实例 ID 不能冲突，密钥有效期间计数器不能归零。系统应在确认加密成功前持久化计数器，或给不同工作节点分配互不重叠的计数区间。

## 存储与密文封装

密文封装应明确每个字段：

```text
版本 || 密钥ID || Nonce || 密文 || 认证标签
```

Nonce 应与加密记录一起保存。若版本、密钥 ID、租户或业务上下文不允许被修改，可以将其作为 GCM 附加认证数据（AAD）。

数据库可以对 `(key_id, nonce)` 建立唯一约束作为纵深防御。它不能替代正确分配机制，但能把灾难性的密码学故障转换为可观察的写入错误。

## 常见生产故障

### 配置中使用固定 IV

固定 IV 让测试结果可重复，却会破坏生产安全。测试代码可以注入确定性生成器，生产环境必须使用真实安全生成器。

### 服务重启后计数器归零

内存计数器在服务重启后从零开始。如果密钥没有更换，旧 Nonce 会再次出现。应持久化计数器、重启时轮换密钥，或分配持久化计数区间。

### 多个写入节点共享密钥

两台服务器可能生成相同计数值。应使用实例前缀划分 Nonce 空间或集中分配，并保证密钥关联数据存在期间实例 ID 不被重用。

### 加密请求重试

重试可能用同一个 Nonce 加密不同明文。幂等请求应直接返回之前完整的密文结果；重新执行加密时必须分配新 Nonce。

### Nonce 被截断或转换

把 Nonce 转成整数、删除前导零或截断 UUID 会改变长度与唯一性。应定义固定二进制表示，并测试编码往返一致性。

## 检测与响应

如果系统不记录 Nonce 与密钥 ID，事后很难从密文本身发现重复。应记录非敏感封装元数据、监控唯一约束，并在故障切换演练中审计计数器。

确认 GCM Nonce 重用后：

1. 停止使用受影响密钥继续加密；
2. 生成新密钥并修复 Nonce 分配；
3. 找出共享相同密钥与 Nonce 的全部记录；
4. 将这些记录的机密性和真实性视为已受损；
5. 只对可信明文使用新密钥重新加密。

仅为未来消息生成新 Nonce，不能修复已经受影响的记录。

## 实现检查清单

- 先确定模式，再确定 IV 规则；
- CBC 使用 16 字节安全随机 IV；
- GCM 使用唯一 12 字节 Nonce，除非库明确要求其他格式；
- 唯一性范围始终与具体密钥绑定；
- Nonce 和密钥 ID 与密文一起存储；
- 定义密钥轮换与每密钥消息上限；
- 测试重启、重试、并发、备份恢复和容灾切换。

模式选择可参考 [AES-GCM 与 AES-CBC 对比](/zh/guides/aes-gcm-vs-cbc/)。也可以使用 [AES 加密工具](/aes-encryption)配合非敏感测试向量观察 IV 行为。
