CMAC 是什么
CMAC(Cipher-based MAC)是用分组密码构造的消息认证码,规范见 NIST SP 800-38B 与 RFC 4493(AES-CMAC)。它把密钥与分组密码结合,对消息生成定长认证标签,用来判断消息是否被篡改、是否来自持有密钥的一方。与 HMAC 相比,CMAC 不依赖哈希函数,只需要一个分组密码实现(通常是 AES),在已有 AES 硬件引擎的设备与受限终端上更容易落地,因此常见于物联网与卡类协议。
基本信息
| 标准 | NIST SP 800-38B / RFC 4493(AES-CMAC) |
|---|---|
| 基础算法 | AES-128/192/256,也可基于 3DES 等其他分组密码 |
| 标签长度 | 等于分组长度,AES 下为 16 字节,可按协议截断 |
| 密钥要求 | 认证密钥应与加密密钥分离 |
| 与 HMAC 的关系 | 两者都做完整性校验,算法与标签互不兼容 |
典型使用场景
- 物联网设备固件、指令与遥测报文的完整性校验:设备算力有限,AES 引擎常已内置硬件加速
- 金融与卡类协议的报文认证:部分行业规范直接指定 CMAC 作为认证方式
- 无线与工业控制协议中信令帧的完整性保护
- 与加密组合成「先加密再认证」的方案,实现机密性与完整性的分离设计
- 替代存在变长拼接缺陷的 CBC-MAC,成本几乎没有增加
算法计算与示例
算法计算与示例
# Python(cryptography 库):计算 AES-CMAC 标签
from cryptography.hazmat.primitives import cmac
from cryptography.hazmat.primitives.ciphers import algorithms
c = cmac.CMAC(algorithms.AES(key)) # key 为 16 / 24 / 32 字节
c.update(b'header') # 可多次 update 分段喂入
c.update(b'payload')
tag = c.finalize() # 输出 16 字节标签
c.verify(tag) # 校验,失败时抛异常
# 命令行(OpenSSL 3.x):
# openssl mac -macopt cipher:AES-256-CBC -macopt hexkey:<32 字节 hex> -in msg.bin CMACCMAC 与 HMAC 对比
| 对比项 | CMAC | HMAC |
|---|---|---|
| 依赖原语 | 分组密码(AES) | 哈希函数(SHA-2) |
| 标签长度 | 等于分组长度,AES 下 16 字节 | 等于哈希输出长度,SHA-256 为 32 字节 |
| 硬件成本 | 复用已有 AES 引擎 | 需要哈希核或软件实现 |
| 标准出处 | NIST SP 800-38B、RFC 4493 | FIPS 198-1、RFC 2104 |
| 典型场景 | 受限设备、卡类协议 | 网络与 Web 协议、通用服务 |
标签截断在受限协议中有先例(例如只保留 8 字节),但截断会直接降低伪造难度:短于 64 位时需要额外的安全论证与明确的使用边界,不要只看「能验证通过」。