CCM 认证加密模式:NIST SP 800-38C 与 CTR-CBC-MAC 的构造原理
title: "CCM 认证加密模式:NIST SP 800-38C 与 CTR-CBC-MAC 的构造原理" slug: "ccm-authenticated-encryption-nist-sp-800-38c" excerpt: "CCM(Counter with CBC-MAC)是一种将计数器模式与 CBC-MAC 结合的双通道认证加密方案,被 NIST 标准化为 SP 800-38C,广泛运用于 IEEE 802.11i (WPA2)、IPSec 和 TLS 等协议。本文从数学结构、格式化处理、安全性和工程实现四个维度深度解析 CCM 的构造原理,并与 AES-GCM 进行对比。" category: algorithm tags: - CCM - 认证加密 - AEAD - NIST - SP 800-38C
概述
CCM(Counter with Cipher Block Chaining-Message Authentication Code,计数器模式结合 CBC-MAC)是一种双通道认证加密(Authenticated Encryption with Associated Data,AEAD)方案。它将 CTR 模式用于机密性保护,将 CBC-MAC 用于完整性认证,通过巧妙的格式化处理将两者组合为一个统一的认证加密原语。
CCM 由 Rogaway、Shrimpton 等人于 2002 年提出,2004 年被 NIST 标准化为 SP 800-38C,是早期 AEAD 方案中最具代表性的标准之一。它与 AES-GCM(SP 800-38D)同为 NIST 推荐的主流认证加密模式,但两者在设计哲学上有本质差异:CCM 采用"先 MAC 后加密"(encrypt-then-MAC 变体),而 GCM 采用"边加密边认证"(GHASH 并行计算)的策略。
设计动机与结构
2.1 为什么需要 CCM?
传统加密方案面临一个核心矛盾:加密保证机密性,MAC 保证完整性,但两者独立使用时顺序不当会导致安全漏洞。
例如:
- MAC-then-Encrypt:攻击者可篡改密文,因解密在前可能触发错误信息
- Encrypt-then-MAC:若 MAC 验证失败,解密结果不可信,但可以提前拒绝
- Encrypt-and-MAC:两个操作独立执行,存在并行优势但需证明安全性
2.2 CCM 的双通道架构
明文 P ─┬─► CBC-MAC ──► 认证标签 T
│
└─► CTR 加密 ──► 密文 C
关联数据 A ──────────────────► CBC-MAC(仅认证,不加密)CCM 的核心思想是复用同一个密钥,通过两次不同的处理流程:
- CBC-MAC 通道:对关联数据 A 和明文 P 计算认证标签 T
- CTR 通道:使用计数器模式加密明文 P 和标签 T
数学构造
3.1 基本符号
设:
K:128/192/256 位密钥N:96 位Nonce(每次加密唯一)A:关联数据(Associated Data,可选,长度≤2^64-1)P:明文 Payload(长度≤2^64-1)C:密文T:认证标签(长度 4-16 字节,通常为 16)
3.2 格式化函数 B(N, A, P)
CCM 首先构造一个格式化的初始块序列 $B_0, B_1, \ldots, B_r$:
B₀ 块(控制块):
B₀ = | Flags | N_A | Lenc | Length(P) |其中:
Flags(1 字节):
q-1,其中 q 为 N 的长度字节数(N=96bit 时 q=12,q-1=11=0xB)
- Bit 3-5:a,关联数据标志(a=0 表示无 A,a>0 表示有 A)
- Bit 6-7:保留 0
N_A(q 字节):Nonce NLenc(1 字节):L-1,L 为长度字段长度字节数Length(P):2L 字节,明文的十六进制长度
- 若关联数据 A 非空:
- 明文块:
总块数:$r + 1 = \lceil |A|/16 \rceil + \lceil |P|/16 \rceil + 2$(含关联数据和明文各补齐后)
3.3 CBC-MAC 计算
初始化:$Y_0 = E_K(B_0)$
迭代计算:$Y_i = E_K(B_i \oplus Y_{i-1})$,$i = 1, 2, \ldots, r$
最终认证标签:$T = MSB_{Tlen}(Y_r)$,其中 $Tlen$ 为认证标签长度(通常 16 字节)
关键点:CBC-MAC 使用零 IV(即 $B_0$ 作为第一个输入),与标准 CBC-MAC 不同。这是 CCM 的特有设计。
3.4 CTR 加密
计数器初始化: $$Ctr_0 = B_0 \oplus (0 || 0 || \ldots || 0 || 1)$$ 即把 $B_0$ 的最低有效字节加 1(模拟计数器初始值)。
计数器链: $$Ctr_{j+1} = Ctr_j + 1 \pmod{2^{128}}$$
加密过程:
- 计算 keystream:$S_j = E_K(Ctr_j)$,$j = 0, 1, \ldots, m$
- 密文:$C = P \oplus MSB_{|P|}(S_1 || S_2 || \ldots || S_m)$
- 认证标签密文:$T^* = T \oplus MSB_{Tlen}(S_0)$
3.5 完整流程图
┌──────────────────────────────────────┐
│ Format Block │
N, A, P ───────►│ (Flags, Nonce, Lengths) │
└──────────────┬───────────────────────┘
│
┌──────────────▼───────────────────────┐
│ CBC-MAC Channel │
│ Y₀ = Eₖ(B₀) │
│ Yᵢ = Eₖ(Bᵢ ⊕ Yᵢ₋₁) │
│ T = MSBₜₗₑₙ(Yᵣ) │
└──────────────┬───────────────────────┘
│
┌──────────────▼───────────────────────┐
│ CTR Channel │
│ Ctr₀ = B₀ + 1 │
│ Sⱼ = Eₖ(Ctrⱼ) │
│ C = P ⊕ MSB(|P|)(S₁||S₂||...||Sₘ) │
│ T* = T ⊕ MSB(Tlen)(S₀) │
└──────────────┬───────────────────────┘
│
Output: (C, T*)安全性分析
4.1 安全模型
CCM 在随机预言机模型下被证明达到 IND-CCA2(不可区分性-选择密文攻击)安全,前提是底层分组密码是强伪随机置换(PRP)。
核心安全定理(Rogaway et al.): 若 $E_K$ 是安全分组密码,则 CCM 构造的 AEAD 方案满足: $$Adv^{AEAD}_{CCM}(A) \leq Adv^{PRP}_E(A') + \frac{q(q-1)}{2 \cdot 2^{128}} + \frac{1}{2^{Tlen/2}}$$
其中:
- $q$:加密查询次数
- $Tlen$:认证标签长度(字节)
4.2 Nonce 重用安全边界
CCM 的安全性依赖于Nonce 的唯一性。如果同一个 $(K, N)$ 对重复使用:
- CBC-MAC 通道:可能泄露分组密码的输出
- CTR 通道:Keystream 重用,导致明文恢复
- 每个 $(K, N)$ 对最多使用一次
- Nonce 必须全局唯一(建议使用 96 位随机值,碰撞概率可忽略)
- 认证标签长度至少 8 字节(推荐 16 字节)
4.3 与 GCM 的安全对比
| 安全属性 | CCM (SP 800-38C) | GCM (SP 800-38D) |
|---|---|---|
| 认证强度 | IND-CCA2 | IND-CCA2 |
| 并行化 | CBC-MAC 串行,CTR 可并行 | GHASH 和 CTR 均可并行 |
| 性能(硬件) | 中等 | 高(Galois域乘法可硬件加速) |
| 性能(软件) | 较低(CBC-MAC 串行依赖) | 高(SIMD 友好) |
| 错误容忍 | 任何部分损坏即失败 | 同左 |
| 旁路安全 | 需注意时序攻击 | 需注意时序攻击 |
工程实现
5.1 Python 实现示例
from cryptography.hazmat.primitives.ciphers.aead import AESCCM
import os
def ccm_encrypt(key: bytes, nonce: bytes, plaintext: bytes, aad: bytes = b'') -> tuple:
"""
CCM 加密实现(使用 cryptography 库)
Args:
key: 16/24/32 字节密钥
nonce: 7-13 字节 Nonce
plaintext: 明文
aad: 关联数据
Returns:
(ciphertext_with_tag,) - 密文+认证标签拼接
"""
aesccm = AESCCM(key, tag_length=16)
ciphertext = aesccm.encrypt(nonce, plaintext, aad)
return ciphertext
def ccm_decrypt(key: bytes, nonce: bytes, ciphertext: bytes, aad: bytes = b'') -> bytes:
"""
CCM 解密实现
Args:
key: 16/24/32 字节密钥
nonce: 7-13 字节 Nonce
ciphertext: 密文+标签(aesccm.encrypt 返回的完整输出)
aad: 关联数据
Returns:
plaintext(解密失败时抛出 InvalidTag 异常)
"""
aesccm = AESCCM(key, tag_length=16)
try:
plaintext = aesccm.decrypt(nonce, ciphertext, aad)
return plaintext
except Exception as e:
raise ValueError(f"Authentication failed: {e}")
# 使用示例
key = os.urandom(16) # 128-bit key
nonce = os.urandom(12) # 96-bit nonce
aad = b"Associated Data"
plaintext = b"Hello, CCM!"
ciphertext = ccm_encrypt(key, nonce, plaintext, aad)
print(f"Ciphertext (hex): {ciphertext.hex()}")
try:
decrypted = ccm_decrypt(key, nonce, ciphertext, aad)
print(f"Decrypted: {decrypted.decode()}")
except ValueError as e:
print(f"Decrypt failed: {e}")
### 5.2 与 OpenSSL 的交互
> **注意**:标准 `openssl enc` 命令不支持 CCM 模式(AEAD 模式)。如需通过 OpenSSL CLI 测试,建议使用 Tongsuo 或专门的密码学工具。
以下示例展示如何使用 Python 调用 OpenSSL 底层 API 进行 CCM 加密:查看系统支持的 CCM 算法
openssl list -cipher-algorithms | grep -i ccm输出: id-aes128-ccm, id-aes192-ccm, id-aes256-ccm
``5.3 常见实现陷阱
- Nonce 重用:最严重的安全漏洞,一旦 Nonce 重复,攻击者可恢复明文和标签
- 标签长度不足:少于 8 字节的标签容易被暴力破解
- 关联数据未验证:AAD 不参与加密但参与 MAC 计算,篡改 AAD 会导致验证失败
- 长度编码错误:P 和 A 的长度字段必须是原始长度(未补齐前的字节数)
应用场景
6.1 协议中的 CCM
协议 CCM 用途 备注 IEEE 802.11i (WPA2) 无线帧加密 与 TKIP 并存,CCMP 基于 CCM IPSec (ESP) 载荷加密 可通过 AH/ESP 选择 CCM TLS 1.2 密码套件 TLS_RSA_WITH_AES_128_CCM_8` 等 DTLS 1.2 数据包加密 UDP 场景下的 TLS 替代 SSH 某些实现 OpenSSH 支持 CCM 作为候选模式
6.2 CCM vs GCM 选型建议
选择 CCM 的场景:
- 资源受限设备(CCM 实现复杂度较低)
- 需要与 WPA2/IPSec 等传统协议兼容
- 数据量较小(CBC-MAC 串行开销可接受)
选择 GCM 的场景:
- 高性能要求(并行计算、硬件加速)
- 大数据量传输
- 新系统设计,无历史包袱
参考标准
- NIST SP 800-38C: Recommendation for Block Cipher Modes of Operation: The CCM Mode for Authentication and Confidentiality
- URL: https://csrc.nist.gov/pubs/sp/800/38/c/final
- IEEE 802.11i-2004: Wireless LAN Security (CCMP)
- RFC 3610: Counter with CBC-MAC (CCM)
- RFC 5116: An Interface and Algorithms for Authenticated Encryption
总结
CCM 作为最早标准化的 AEAD 方案之一,其双通道设计(CBC-MAC + CTR)为认证加密提供了清晰的理论框架。尽管在性能上不及 GCM 的并行化架构,CCM 仍在 WiFi(WPA2)、IPSec 等传统协议中占据重要地位。理解 CCM 的数学构造不仅有助于实现正确的加密方案,也为学习更现代的 AEAD 模式(如 GCM、Poly1305-AES)奠定了坚实基础。
关键要点:
- CCM = CBC-MAC(认证)+ CTR(加密),通过格式化块 B₀ 连接
- Nonce 必须唯一,否则安全性彻底崩溃
- 认证标签 T 也参与加密,防止标签篡改
- 实现时严格遵循 NIST SP 800-38C 的格式规范
*相关实践:SM4-GCM 认证加密模式详解*