本节摘要: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 校验失败。
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 验证逻辑一致,可以在开发机上先跑通,再移植到 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 烧录后抹掉调试口,生产固件不做可读回显。
要点回顾:AppKey 管入网、NwkSKey 管网络层、AppSKey 管应用层;AES-CMAC 生成 MIC,注意子密钥、空消息分支与常数时间比较;会话密钥靠周期重入网轮换。下一节看帧计数器这道重放防线怎么把好最后一道关。
量产时的密钥管理是安全账本里最容易被跳过的环节。规范的流程是:产线生成——每一台设备在产线上生成唯一的 AppKey(随机源必须真随机,常见做法是用射频 RSSI 采样或硬件随机数),烧录进设备的独立 Flash 区,同时登记进应用服务器;抹除调试口——烧录完成后关闭调试接口(读保护),防止固件与密钥被读出;交付链路——密钥从产线到应用服务器的传递走加密通道,不走明文邮件或聊天工具。
| 环节 | 动作 | 常见失误 |
|---|---|---|
| 产线 | 生成并烧录 AppKey | 用固定密钥量产,全网一把钥匙 |
| 登记 | 密钥入库并与设备绑定 | 台账与实物对不上 |
| 保护 | 开读保护、抹调试口 | 调试口留着,密钥可被读出 |
| 交付 | 加密通道移交密钥 | 明文邮件传递 |
最严重的量产事故是"全网共用一把 AppKey":一旦任何一台设备被拆解读出密钥,整个网络的安全边界随之崩溃。AppKey 必须每设备唯一,这是底线。
现场最常见的密钥类故障是"换了一个 NS 就全部失联":设备侧密钥是出厂烧录的 AppKey,NS 侧登记的 AppKey 不一致,入网必然失败。排错顺序:先在 NS 后台核对设备 EUI 与 AppKey 登记 → 再用抓包工具验 JoinRequest 的 MIC → 若 MIC 失败则确认设备固件里的密钥区是否被覆盖(产线烧录覆盖了出厂默认值,或开发板的默认密钥没被量产流程替换)。密钥类问题几乎都是"两边对不上",先对台账再动设备。
小结与速查:四把密钥——AppKey 管入网、NwkSKey 管网络层、AppSKey 管应用层、组密钥管多播;AES-CMAC 生成 MIC,B0 块把类型/方向/地址/计数编入认证;比较要常数时间;会话密钥靠周期重入网轮换;密钥存储要读保护、独立 Flash 页。