02-密钥体系与AES-CMAC实现


4.2 密钥体系与 AES-CMAC 实现

本节摘要:LoRaWAN 用一把根密钥(AppKey)派生三把会话密钥,通信过程用 AES-128 加密与 AES-CMAC 做完整性校验。本节讲清密钥分工、上下行密钥方向的区别,并给出一段可移植的 AES-CMAC 实现要点。位置:4.1 完成了入网,本节把入网派生的密钥用起来。

四把密钥的分工表

密钥 谁持有 用途 生命周期
AppKey 设备 + 应用服务器 Join 报文 MIC、派生会话密钥 长期(出厂预置)
NwkSKey 设备 + 网络服务器 数据帧 MIC、网络层载荷加解密(FPort=0) 会话期(入网时派生)
AppSKey 设备 + 应用服务器 应用载荷(FPort=1-223)AES-128 加解密 会话期(入网时派生)
多播密钥(可选) 设备 + 网络服务器 多播帧 MIC 与加解密 多播会话期

关键点:上行与下行使用不同方向的密钥——LoRaWAN 1.0.x 用同一把 NwkSKey 的不同方向参数,1.1 起拆成 FNwkSIntKey、SNwkSIntKey、NwkSEncKey 三把。实现时要严格按版本参数表派生,混用会直接导致 MIC 校验失败。

AES-CMAC 的实现要点

MIC 是 AES-CMAC 的截断值(取前 4 字节)。AES-CMAC 是 OMAC1 的 AES 版本,核心三步:

/* 伪代码:AES-CMAC 主流程(K0 为子密钥,X 为状态块) */ /* 1. 由主密钥 K 生成子密钥 K1、K2(用 L = AES_K(0) 左移 + 常数异或) */ /* 2. 对消息分组:完整分组用 M_i ⊕ X ⊕ K1,最后不完整分组补 0x80... 后用 K2 */ /* 3. 最后一轮 X 再用 AES_K 加密一次,输出前 4 字节即 MIC */ ​

实现要点三条:子密钥生成必须正确(K1/K2 的移位与 0x87 常数异或,错一位全错);消息长度为零时也要走"不完整分组"分支(补 0x80 用 K2);MIC 校验必须常数时间比较(防时序侧信道)。社区实现(如 LoRaMac-node 的 aes.c)可直接移植,但要注意端序:LoRaWAN 的字节序是 Little-Endian 处理 + 按字节传输,移植时最容易踩的就是字节序反了导致"本机验不过自己发的包"。

会话期密钥的刷新机制

会话密钥随每次 OTAA 刷新,因此设备应周期性重入网(常见做法:每 24–48 小时一次,或按网络服务器的会话超时配置),把长期运行的密钥暴露窗口缩短。ABP(预置密钥)没有入网流程,密钥固定——虽然省了入网开销,但密钥泄露后无法轮换,且没有 DevNonce 防重放机制(靠帧计数补),生产环境强烈建议用 OTAA。

一段可运行的 MIC 验证流程

入网与数据帧的 MIC 验证逻辑一致,可以在开发机上先跑通,再移植到 MCU。验证流程:

输入:payload(MHDR+MACPayload 字节)、key(NwkSKey 或 AppKey)、 msgType(JoinRequest/JoinAccept/DataUp/DataDown)、direction(上行/下行) 步骤: 1. 按 LoRaWAN 规范拼接 B0 块:0x49 || 0x00 || 0x00 || 0x00 || 0x00 || msgType || direction || DevAddr(4B) || FCnt(4B) || 0x00 || len 2. 用 AES-CMAC(key, B0 || payload) 生成 16 字节 MAC 3. 取前 4 字节与帧内 MIC 比较(常数时间比较) 4. 相等 → 校验通过;不等 → 丢弃帧 ​

B0 块是 MIC 计算的隐藏难点:它把消息类型、方向、地址、帧计数都编进认证范围,任何一位错了 MIC 就失败——这也是"为什么明明密钥对却验不过"时最该检查的地方。开发时把 B0 块的 16 字节逐字节打出来与规范示例对照,能省一半排错时间。

密钥存储的硬件化

会话密钥落在 MCU Flash 里有两个风险:读保护不足(调试接口开着,固件被读出后密钥直接泄露)与Flash 磨损(频繁重入网改写密钥区)。成熟做法:密钥区放到独立 Flash 页并开启读保护;有硬件安全单元(SE)的 MCU 把密钥放入 SE 或 TEE;没有硬件支持时,至少把 AppKey 烧录后抹掉调试口,生产固件不做可读回显。

易错点补充

  • B0 块长度字段算错:len 是 MACPayload 长度,不含 MHDR 与 MIC,算错一位全错;
  • 多播帧与单播帧用同一密钥派生路径:多播密钥独立于会话密钥,处理路径要分开;
  • 密钥区与代码区共用 Flash 页:固件升级可能覆盖密钥区,分区设计要预留并做升级后的密钥保留策略。

易错点清单

  • 用同一密钥处理上下行方向:方向参数(msgType + direction)参与派生,方向搞错则 MIC 全灭;
  • 把 AppSKey 当 NwkSKey 用:两把密钥的派生输入相同但用途不同,混用既破坏安全也必然解析失败;
  • MIC 比较用 strcmp:必须常数时间比较,长度固定 4 字节,用固定循环或专用函数;
  • 字节序没测就上量:先在开发板上做"自收自发"回环测试验证 MIC 一致性,再接入网络。

要点回顾:AppKey 管入网、NwkSKey 管网络层、AppSKey 管应用层;AES-CMAC 生成 MIC,注意子密钥、空消息分支与常数时间比较;会话密钥靠周期重入网轮换。下一节看帧计数器这道重放防线怎么把好最后一道关。

密钥派生与固件烧录流程

量产时的密钥管理是安全账本里最容易被跳过的环节。规范的流程是:产线生成——每一台设备在产线上生成唯一的 AppKey(随机源必须真随机,常见做法是用射频 RSSI 采样或硬件随机数),烧录进设备的独立 Flash 区,同时登记进应用服务器;抹除调试口——烧录完成后关闭调试接口(读保护),防止固件与密钥被读出;交付链路——密钥从产线到应用服务器的传递走加密通道,不走明文邮件或聊天工具。

环节 动作 常见失误
产线 生成并烧录 AppKey 用固定密钥量产,全网一把钥匙
登记 密钥入库并与设备绑定 台账与实物对不上
保护 开读保护、抹调试口 调试口留着,密钥可被读出
交付 加密通道移交密钥 明文邮件传递

最严重的量产事故是"全网共用一把 AppKey":一旦任何一台设备被拆解读出密钥,整个网络的安全边界随之崩溃。AppKey 必须每设备唯一,这是底线。

会话密钥的现场排错

现场最常见的密钥类故障是"换了一个 NS 就全部失联":设备侧密钥是出厂烧录的 AppKey,NS 侧登记的 AppKey 不一致,入网必然失败。排错顺序:先在 NS 后台核对设备 EUI 与 AppKey 登记 → 再用抓包工具验 JoinRequest 的 MIC → 若 MIC 失败则确认设备固件里的密钥区是否被覆盖(产线烧录覆盖了出厂默认值,或开发板的默认密钥没被量产流程替换)。密钥类问题几乎都是"两边对不上",先对台账再动设备。

易错点补充

  • 日志里打印密钥:调试日志禁止输出密钥明文,用掩码或干脆不打印;
  • 固件升级覆盖密钥区:OTA 升级要避开密钥区,或升级后密钥区保留策略明确;
  • 密钥派生参数写死版本:LoRaWAN 1.0.x 与 1.1 的派生参数不同,协议栈版本与密钥派生版本要一致。

本节速查

小结与速查:四把密钥——AppKey 管入网、NwkSKey 管网络层、AppSKey 管应用层、组密钥管多播;AES-CMAC 生成 MIC,B0 块把类型/方向/地址/计数编入认证;比较要常数时间;会话密钥靠周期重入网轮换;密钥存储要读保护、独立 Flash 页。

作者与出处
原作者:灏天文库
来源:平台策划编纂
整理人: 灏天文库整理
由灏天文库平台收录,内容或由平台用户上传,仅供学习交流
发布者: 作者: 灏天文库 转发
评论区 (0)
U