1. 项目概述与背景
在嵌入式安全和物联网设备身份认证的实践中,一个核心的挑战是如何在资源受限的终端(比如一枚小小的NFC标签)上实现高强度的安全功能。直接在这些设备上存储和处理密钥是极其危险的,一旦被物理攻击或侧信道分析,整个安全体系就会崩塌。因此,业界普遍采用“安全元件”(Secure Element, SE)的架构,将最核心的密钥管理和加密运算交给一个专门的、经过安全认证的硬件芯片来处理。今天要深入探讨的,就是恩智浦(NXP)生态中一个非常经典的组合:MIFARE SAM AV3安全模块与NTAG 22x DNA系列NFC标签。
简单来说,NTAG 22x DNA标签内置了AES-128加密引擎,支持双向认证和生成防篡改的安全消息,但它自己并不“知道”密钥。密钥被安全地存储在独立的MIFARE SAM AV3芯片里。当读卡器需要验证标签真伪或校验其发出的数据时,读卡器端的SAM AV3会代替标签执行复杂的加密解密计算。这样,标签只需进行最基本的通信和存储,而密钥始终处于SAM AV3的硬件保护之下,从未暴露在通信链路或读卡器主控的普通内存中。这种设计完美平衡了成本、功耗与安全性,非常适合门禁卡、产品防伪标签、需要离线验证的物流跟踪等场景。
我接触过不少项目,从简单的门禁到复杂的供应链溯源,只要涉及到对NFC标签进行真伪鉴别或数据来源认证,最终几乎都会走到这条技术路径上。官方应用笔记(AN13762)给出了基础流程,但在实际开发和调试中,有大量的细节和“坑”是文档不会明说的。接下来,我将结合自己的实操经验,为你彻底拆解如何利用MIFARE SAM AV3为NTAG 22x DNA实现认证和SUN验证,不仅告诉你“怎么做”,更重点解释“为什么这么做”,以及过程中有哪些必须注意的关键点。
2. 核心组件与安全模型解析
在动手敲代码或发送APDU命令之前,我们必须先理解这场“安全双人舞”中两位主角的角色与能力边界。这决定了后续所有流程的设计。
2.1 MIFARE SAM AV3:可编程的安全卫士
MIFARE SAM AV3本质上是一个微型的安全协处理器。你可以把它想象成一个高度保险的、只能通过特定指令(APDU命令)访问的“黑盒子”。它的核心价值在于:
- 安全存储:内部有一个密钥库(Key Store),可以注入多种类型的对称密钥(如AES-128)和非对称密钥。密钥一旦注入,就无法以明文形式读取(除非特殊配置),从而杜绝了软件层面的窃取。
- 硬件加密:内置了加密算法引擎(如AES、3DES)。当需要通过
SAM_Encipher或SAM_Decipher等命令执行加密解密时,密钥数据不会离开芯片的安全边界,运算也在芯片内完成,极大降低了旁路攻击的风险。 - 密钥多样性:支持密钥派生(Diversification)。这意味着你可以将一个主密钥(Master Key)注入SAM,然后根据每个标签的唯一标识符(如UID)动态派生出不同的子密钥。即使某个标签的子密钥泄露,也不会危及主密钥和其他标签的安全。
在NTAG 22x DNA的应用中,SAM AV3扮演着认证方(Verifier)和MAC生成/校验方的角色。它持有用于认证和生成CMAC的密钥,并执行所有相关的加密运算。
2.2 NTAG 22x DNA:具备安全能力的智能标签
NTAG 22x DNA(如NTAG 223 DNA, NTAG 224 DNA)是支持NFC Forum Type 2标准的标签芯片,但其特殊之处在于集成了AES-128加密引擎,并支持以下安全功能:
- 相互认证(Mutual Authentication):基于AES-128的挑战-响应协议,确保标签和读卡器(背后的SAM)相互确认对方拥有正确的密钥。
- 安全唯一NDEF(SUN):标签可以生成一个包含其UID、递增计数器(NFC_CTR)和基于这两者计算出的CMAC的NDEF消息。任何读取者都可以获取该消息,但只有拥有正确密钥的验证者(SAM)才能校验CMAC的有效性,从而确认消息确实来自该标签且未被篡改。
关键限制:NTAG DNA标签不支持安全的密钥更新机制。这意味着用于认证和SUN的密钥(AES_Key_x)必须在标签生产阶段,在一个安全的环境中被一次性写入。之后便无法远程更改。这强调了初始密钥注入环节的极端重要性。
2.3 安全交互模型与密钥流
整个安全体系的基石是一个共享的AES-128密钥。其生命周期如下:
- 密钥生成与注入(安全环境):密钥在安全环境中生成,并被同时注入到:
- MIFARE SAM AV3的指定密钥槽(Key Entry)中。
- NTAG 22x DNA标签的
AES_Key_x存储区(通过WRITE命令,此过程密钥以明文传输,故需绝对安全)。
- 在线认证流程:
- 读卡器向标签发起认证命令。
- 标签生成随机数
RndB,用密钥加密后发送给读卡器。 - 读卡器将加密的
RndB传给SAM AV3解密,得到明文RndB。 - SAM AV3生成随机数
RndA,并将RndA和RndB'(RndB移位后)加密,结果由读卡器发给标签。 - 标签解密后验证
RndB',并加密RndA'(RndA移位后)返回。 - 读卡器将加密的
RndA'传给SAM AV3解密并验证,完成双向认证。
- 离线SUN验证流程:
- 任何NFC读卡器(无需SAM)都可以读取标签的SUN消息(一个包含UID、计数器和CMAC的文本)。
- 验证者(拥有SAM AV3的终端)获取该SUN消息,从中提取UID和计数器。
- 验证者要求SAM AV3使用相同的密钥,对提取出的UID和计数器计算CMAC。
- 比较SAM计算出的CMAC与SUN消息中的CMAC是否一致,一致则证明标签真实且数据新鲜。
注意:认证过程是交互式的、在线的,需要标签参与。而SUN验证可以是离线的,验证者只需要拿到SUN消息的副本即可,这为供应链查验等场景提供了极大便利。
3. 密钥生命周期管理:从SAM到标签
这是整个系统搭建中最需要谨慎对待的环节。密钥一旦泄露,所有安全措施形同虚设。
3.1 在MIFARE SAM AV3中配置密钥条目
根据应用笔记,用于NTAG 22x DNA的密钥必须是AES-128类型,且密钥类(KeyClass)必须设置为OfflineCrypto。这一点至关重要。
为什么是OfflineCrypto而不是PICC?
PICC密钥类是专为MIFARE经典协议(如MIFARE DESFire)的本地安全通信(Secure Messaging)设计的。NTAG DNA虽然也使用AES,但其安全通信帧格式与MIFARE原生协议不同,SAM AV3的硬件逻辑无法直接处理。OfflineCrypto密钥类则是将SAM AV3当作一个纯粹的加密/解密协处理器来使用。读卡器主控(Host)通过APDU命令将待处理的数据发送给SAM,SAM用指定的密钥处理后再将结果返回。这种方式更灵活,可以适配NTAG DNA自定义的认证数据格式。
使用SAM_ChangeKeyEntry命令注入密钥。命令格式复杂,需要正确设置密钥数据、密钥类型(AES-128)、密钥类(OfflineCrypto)以及访问控制条件(如KeyVAEK)。一个常见的设置是将KeyVAEK设为0xFE,这意味着后续使用此密钥时,无需主机对SAM进行身份验证,简化了流程。
// 示例:向SAM AV3的密钥槽0x01注入全零的AES-128密钥(仅示意,非完整APDU) 80 C1 01 FF 40 [KeyData...] [KeySettings...] FEFE其中,KeySettings字段需要包含OfflineCrypto的标识和相应的访问控制位。
3.2 将密钥安全注入NTAG 22x DNA
这是高风险操作!因为NTAG DNA的WRITE命令对AES_Key_x区域的写入是以明文形式进行的。这意味着在注入过程中,密钥会暴露在标签、读卡器和主机之间的通信线上。
必须遵循的实践:
- 安全环境:密钥注入必须在受控的、物理安全的生产或初始化环境中进行,例如在产线的安全工位上。
- 一次性编程:通常与标签的个人化(写入UID、初始化数据)流程结合,完成后即脱离安全环境。
- 密钥派生考虑:如果使用密钥派生,则注入到每个标签的
AES_Key_x应该是根据标签UID和主密钥派生出的差异化密钥,而不是主密钥本身。这样即使某个标签被破解,也不会波及其他标签。
从SAM AV3导出密钥(如需):如果生产系统使用SAM AV3作为密钥源,需要将密钥写入标签,则必须先将密钥从SAM中导出。这需要事先在配置密钥条目时,启用ExtSET字节的位3(允许导出)。
- 明文导出:使用
SAM_DumpSecretKey命令。这要求导出操作本身也在安全环境中进行,因为密钥会以明文返回给主机。 - 加密导出(更安全):可以配置密钥条目,要求提供 diversification input 才能导出(启用
ExtSET位4)。这样导出的密钥是经过派生的,即使导出过程被监听,攻击者得到的也不是主密钥。
// 示例:从SAM密钥槽0x01导出密钥(明文) > 80 D6 00 00 02 01 00 00 // SAM_DumpSecretKey 命令,针对密钥槽0x01 < [16字节密钥明文] 90 00 // SAM返回密钥和成功状态获取密钥明文后,再通过标准的WRITE命令序列,将其写入NTAG标签的AES_Key_x配置页。
实操心得:在实际产线中,我们通常会使用一台专用的、离线的高安全级别工控机,连接SAM AV3和标签编程器。工控机上的初始化软件在安全启动后,从加密的配置文件中或通过HSM获取主密钥,注入SAM,然后为每个标签派生并写入差异化密钥。整个过程中,内存中的密钥明文会在操作完成后立即清零。绝对要避免在联网的普通PC上进行此操作。
4. NTAG 224 DNA双向认证的逐步实现与剖析
认证流程是理解整个安全握手的关键。我们以NTAG 224 DNA为例,详细拆解每一步,并解释其背后的密码学原理和实现细节。
4.1 认证协议原理(简化三步握手)
NTAG DNA的相互认证基于一个改良的三步挑战-响应协议,其核心目标是让双方(Tag和SAM)在不泄露密钥的前提下,向对方证明自己拥有相同的密钥。
- 挑战(Challenge):读卡器(代表SAM)向标签发送
AUTHENTICATE命令(0x1A)。 - 响应与挑战(Response & Challenge):标签生成随机数
RndB,用共享密钥Kx加密得到E(Kx, RndB),并将其发送给读卡器。同时,RndB也是标签对读卡器的挑战。 - 响应(Response):读卡器将
E(Kx, RndB)交给SAM解密得到RndB。SAM生成自己的随机数RndA,并将RndA和RndB'(RndB循环左移一个字节)拼接后加密,得到E(Kx, RndA || RndB'),发送给标签。标签解密后验证RndB'是否正确,如果正确,则相信读卡器(SAM)拥有密钥。接着,标签将RndA'(RndA循环左移一个字节)加密后E(Kx, RndA')返回。读卡器将其交给SAM解密并验证RndA',完成对标签的验证。
4.2 基于SAM AV3的命令流实现
以下表格和详解还原了应用笔记中的流程,并加入了关键注释:
| 步骤 | 操作方向 | APDU命令/数据 | 说明与内部解析 |
|---|---|---|---|
| 1 | 主机 -> SAM | 80 01 00 00 02 01 00 | ActivateOfflineKey:激活SAM中密钥槽0x01的密钥,准备用于后续加解密。 |
| 2 | SAM -> 主机 | 90 00 | SAM响应操作成功。 |
| 3 | 主机 -> 标签 | 1A 00 | 向NTAG 224 DNA发送**AUTHENTICATE**命令(0x1A),0x00是参数。 |
| 4 | 标签 -> 主机 | AF+[E(Kx, RndB)] | 标签返回状态0xAF(需要更多数据)和加密的16字节随机数RndB。 |
| 5 | 主机 -> SAM | 80 71 00 00 10+[全零IV] | LoadIV:为SAM的AES算法设置初始向量(IV)。NTAG DNA认证使用ECB模式,但SAM的OfflineCrypto功能可能需要显式设置IV,这里设为全零。 |
| 6 | SAM -> 主机 | 90 00 | 成功。 |
| 7 | 主机 -> SAM | 80 0D 00 00 10+[E(Kx, RndB)]+00 | SAM_Decipher_Offline_Data:命令SAM用激活的密钥解密收到的E(Kx, RndB)。末尾的00是填充指示。 |
| 8 | SAM -> 主机 | [RndB明文]+90 00 | SAM返回解密得到的16字节RndB。至此,读卡器端获得了标签的挑战。 |
| 9 | 主机 -> SAM | 80 84 00 00 10 | GetRandom:请求SAM生成一个16字节的随机数,作为读卡器对标签的挑战RndA。 |
| 10 | SAM -> 主机 | [RndA明文]+90 00 | SAM返回生成的RndA。 |
| 11 | 主机 -> SAM | 80 71 00 00 10+[全零IV] | 再次**LoadIV**,为加密操作准备。 |
| 12 | SAM -> 主机 | 90 00 | 成功。 |
| 13 | 主机 -> SAM | 80 0E 00 00 20+[RndA]+[RndB']+D2 00 | SAM_Encipher_Offline_Data:命令SAM加密数据。待加密数据是RndA (16字节)和RndB' (16字节)的拼接(共32字节)。RndB'是RndB循环左移一个字节。末尾的D2 00是填充相关参数。 |
| 14 | SAM -> 主机 | `[E(Kx, RndA | |
| 15 | 主机 -> 标签 | AF+ `[E(Kx, RndA | |
| 16 | 标签 -> 主机 | 00+[E(Kx, RndA')] | 标签返回状态0x00(成功)和加密的RndA'(RndA循环左移一个字节)。 |
| 17 | 主机 -> SAM | 80 71 00 00 10+[全零IV] | 再次**LoadIV**,为最后一步解密准备。 |
| 18 | SAM -> 主机 | 90 00 | 成功。 |
| 19 | 主机 -> SAM | 80 0D 00 00 10+[E(Kx, RndA')]+00 | SAM_Decipher_Offline_Data:命令SAM解密E(Kx, RndA')。 |
| 20 | SAM -> 主机 | [RndA'明文]+90 00 | SAM返回解密得到的RndA'。主机需要验证此RndA'是否等于步骤10中获得的RndA经过循环左移一个字节后的结果。如果一致,则双向认证成功。 |
关键点解析:
- 随机数(Nonce)的作用:
RndA和RndB确保了每次认证会话都是唯一的,防止重放攻击(Replay Attack)。 - 移位操作(RndB‘, RndA’):这是一个简单的“变形”,用于在响应中证明对方确实正确解密了挑战随机数。它比直接返回原随机数多了一层保护。
- SAM的桥梁角色:整个过程中,主机(读卡器MCU)只是命令的转发者和数据的搬运工。它从未接触过密钥
Kx,也从未进行实际的AES运算。所有加解密都在SAM内部完成,这是硬件安全元件的核心价值体现。
5. 安全唯一NDEF(SUN)消息的离线验证实战
SUN验证是NTAG DNA一个非常强大的功能,它允许标签发布一个可公开读取但不可伪造的数据块。验证者可以在离线状态下,仅凭这个数据块和本地的SAM AV3,就能确认数据的真实性和新鲜性。
5.1 SUN消息的构成与获取
一个完整的SUN消息是一个39字节的ASCII字符串,格式为:[UID(14字节ASCII)]+x+[NFC_CTR(6字节ASCII)]+x+[CMAC(16字节ASCII)]。
- UID:标签的7字节唯一标识符,转换为14位十六进制ASCII字符。
- NFC_CTR:一个3字节的递增计数器,每完成一次成功的认证或特定操作后递增,转换为6位十六进制ASCII字符。它保证了消息的新鲜性(Freshness),防止旧消息被重放。
- CMAC:基于AES-CMAC算法,使用密钥
Kx对UID和NFC_CTR(均为二进制格式)计算得到的16字节消息认证码,转换为32位十六进制ASCII字符。
获取SUN消息有两种主要方式:
- 通过NDEF读取:如果标签配置了NDEF镜像功能,SUN消息会作为一个NDEF记录存储。任何标准的NFC手机或读卡器都可以像读取普通网址一样读取到它,例如:
nxp.com/data=04B4BF4A031090x000005x2F5D760654E91A4B。这种方式对读取设备要求最低,最通用。 - 直接读取内存页:通过
READ或FAST_READ命令,读取标签内存中配置的SUN镜像区域。这需要读取设备知道镜像的具体位置(通过读取配置页0x39获得)。
5.2 使用SAM AV3进行验证的步骤
验证的核心是重新计算CMAC并比对。以下是基于直接读内存方式的步骤分解:
定位并读取SUN消息:
- 发送
READ命令读取配置页0x39,获取MIRROR_PAGE等参数,确定SUN数据存放的起始页。 - 使用
FAST_READ命令从起始页开始读取足够长度的数据(通常10页,44字节),确保覆盖完整的39字节SUN ASCII字符串。 - 从返回的字节流中解析出ASCII字符串。
- 发送
解析SUN消息:
- 将39字节ASCII字符串按
x字符分割成三部分。 - 将第一部分(14字符)和第二部分(6字符)从ASCII十六进制表示转换回二进制数据。例如,ASCII
"30344234424634"转换为二进制0x04, 0xB4, 0xBF, 0x4A, ...。
- 将39字节ASCII字符串按
使用SAM AV3计算CMAC:
- 发送
ActivateOfflineKey命令激活SAM中对应的密钥。 - 准备待计算的数据:将解析出的二进制
UID和NFC_CTR拼接起来。 - 发送
SAM_GenerateMAC命令给SAM。该命令会使用激活的密钥,对输入数据计算AES-CMAC。 - SAM返回计算得到的16字节CMAC。
- 发送
比对与验证:
- 将SAM返回的二进制CMAC,转换为32位的十六进制ASCII字符串。
- 将此字符串与SUN消息中解析出的第三部分(CMAC ASCII)进行逐字符比较。
- 如果完全一致,则验证通过,证明该SUN消息确实是由拥有正确密钥
Kx的标签在计数器值为NFC_CTR时生成的。
// 示例:SAM_GenerateMAC 命令调用(数据为示意) // 假设 UID (hex) = 04B4BF4A031090, NFC_CTR (hex) = 000005 // 拼接后数据: 04 B4 BF 4A 03 10 90 00 00 05 > 80 7C 00 80 0A [04 B4 BF 4A 03 10 90 00 00 05] // 80 7C 是 GenerateMAC, 0A是数据长度 < [2F 5D 76 06 54 E9 1A 4B ...] 90 00 // SAM返回16字节CMAC将返回的2F5D760654E91A4B...与SUN消息中的CMAC字段比较即可。
注意事项:
NFC_CTR的单调递增性是防重放的关键。在验证系统中,验证者应该记录每个UID最后一次验证成功的NFC_CTR值。当收到一个新的SUN消息时,其NFC_CTR必须大于上次记录的值,否则应视为重放攻击而拒绝。这就需要验证端有一个简单的状态存储(数据库或文件)。
6. 开发调试与生产中的常见问题与解决思路
在实际项目中,从原型验证到批量生产,会遇到各种各样的问题。这里分享一些典型的坑和排查经验。
6.1 认证失败(Authentication Failed)
这是最常见的问题,通常表现为标签返回错误状态码(非0xAF或0x00),或者SAM返回6A80(错误数据)、6A88(密钥未找到)等。
排查清单:
- 密钥一致性:这是首要怀疑对象。确认注入到NTAG标签
AES_Key_x区域的密钥,与SAM AV3密钥槽中存储的密钥完全一致(包括是原始密钥还是派生密钥)。一个字节的差异都会导致失败。建议在安全环境下,用调试工具分别读出两者(SAM需配置为可导出)进行比对。 - 密钥类(KeyClass):确认SAM中的密钥类设置为
OfflineCrypto,而不是PICC或其他。这是最容易被忽略的配置错误。 - 密钥槽编号:在
ActivateOfflineKey等命令中,引用的密钥槽编号(如0x01)必须与密钥实际存储的槽位一致。 - 随机数处理:确保在主机代码中正确地对
RndB进行了循环左移一位的操作以得到RndB',并在最后对SAM返回的RndA'进行验证(与本地RndA移位后对比)。移位操作错误是常见的逻辑Bug。 - 数据拼接与填充:在调用
SAM_Encipher_Offline_Data加密RndA||RndB'时,确保32字节数据拼接的顺序和内容正确。同时注意APDU命令中关于填充(Padding)的参数(如示例中的D200),必须与SAM的期望模式匹配。通常对于AES-ECB,可能需要指定无填充或特定的填充方式。 - SAM状态:确保在发送一系列命令前,SAM处于正确的会话状态。例如,某些配置下可能需要先通过
SAM_AuthenticateHost命令验证主机身份,才能使用密钥。示例中设置KeyVAEK=0xFE就是为了绕过主机认证。
6.2 SUN验证不通过(CMAC Mismatch)
当计算出的CMAC与标签提供的CMAC不一致时。
排查清单:
- 数据源:确认你用于计算CMAC的
UID和NFC_CTR二进制数据,与标签SUN消息中的ASCII字符串转换结果完全一致。特别注意字节序(Endianness)。NTAG通常使用大端序(MSB first)。在代码中转换ASCII hex字符串到字节数组时,要确保每两个字符转换成一个字节的顺序正确。 - 计数器值:确认你读取的
NFC_CTR是有效的。如果标签从未进行过认证或触发计数器递增的操作,NFC_CTR可能为全零或初始值,这本身是有效的,但如果你期望一个更大的值,就需要检查标签状态。 - 密钥问题:同认证失败,确保SAM中用于生成MAC的密钥与标签中用于生成SUN的密钥一致。
- CMAC算法:确认SAM的
SAM_GenerateMAC命令使用的是AES-CMAC算法,并且输入数据就是UID和NFC_CTR的二进制拼接,没有添加额外的头尾、长度或分隔符。有些加密库在计算CMAC时可能会自动处理数据填充,但SAM的GenerateMAC命令通常要求输入数据已经是块对齐的,或者通过参数指定填充方式。 - SUN消息截取:如果通过读内存方式获取SUN,确保读取的起始地址和长度正确,完整地拿到了39字节ASCII字符串,没有多读或少读,也没有包含内存中的其他无关数据。
6.3 性能与稳定性考量
- 时序问题:在资源受限的嵌入式读卡器上,主机MCU在SAM和NFC射频芯片之间来回转发命令和数据,可能涉及中断、缓冲区管理。要确保命令发送和响应接收的时序正确,避免数据覆盖或丢失。特别是认证流程步骤多,要做好超时处理和错误重试机制。
- SAM并发访问:如果系统需要快速连续处理多个标签,要管理好对SAM的访问。通常SAM是串行处理命令的。可以考虑设计一个简单的命令队列,或者确保同一时间只有一个认证/SUN验证流程在进行。
- 电源与复位:确保SAM AV3的供电稳定。异常断电或复位可能导致密钥激活状态丢失,需要重新发送
ActivateOfflineKey命令。
6.4 生产测试建议
- 自动化测试脚本:编写覆盖全流程的自动化测试脚本,包括:密钥注入验证、双向认证测试、SUN读写与验证测试。对每个生产出来的标签或读卡器模块都跑一遍,确保功能完好。
- 抽样破坏性测试:定期抽样,尝试用错误的密钥进行认证和SUN验证,确保系统能正确拒绝。
- 计数器溢出测试:测试
NFC_CTR从0xFFFFFE递增到0xFFFFFF再回到0x000000时的行为,确保验证逻辑能正确处理回绕。
最后,务必反复阅读NXP官方的最新版数据手册(Datasheet)和应用笔记(Application Note)。芯片的细微行为、命令的特定参数可能会因版本而异。官方文档始终是最权威的参考。将MIFARE SAM AV3与NTAG 22x DNA结合,为嵌入式系统提供了一个性价比极高的硬件安全解决方案。理解其原理,仔细处理密钥,严格遵循流程,就能构建出坚固可靠的身份认证与数据验证体系。