2. OTAA激活流程详解:Join Request、Join Accept、AppKey与NwkKey的作用、Session密钥派生过程

好,咱们接着聊。上一节我讲了OTAA和ABP的区别,说白了就是「第一次见面怎么打招呼」的问题。这一节,咱们把OTAA的整个握手过程拆开来看。

OTAA的全称是Over-The-Air Activation,翻译过来就是「空中激活」。嗯,这个名字很形象——设备通过无线信号完成激活,不需要你拿着烧录器去现场。我在做智慧农业项目时,几百个传感器分布在几十亩大棚里,要是每个都手动激活,那得累死。OTAA就是为这种场景设计的。

2.1 整个流程一句话概括

设备发送Join Request → 服务器回复Join Accept → 双方各自派生出Session密钥 → 激活完成。

就这么简单?对,但细节都在密钥派生里。你想想看,如果只是发个请求、收个回复,那安全性怎么保证?所以LoRaWAN把重点放在了「如何安全地生成后续通信用的密钥」上。

2.2 Join Request:设备的第一声招呼

设备上电后,第一件事就是发Join Request。这个包长什么样?

| 字段 | 大小 | 说明 |
|------|------|------|
| AppEUI | 8字节 | 应用标识,相当于你的App ID |
| DevEUI | 8字节 | 设备标识,每个设备唯一 |
| DevNonce | 2字节 | 随机数,防重放攻击 |

这里有个关键点:Join Request是明文发送的。你可能会问:「明文?那安全性在哪?」别急,后面你就明白了。

DevNonce这个字段,我特别想强调一下。它是个随机数,每次Join Request都必须不同。服务器会记录所有用过的DevNonce,如果发现重复,直接拒绝。为什么?为了防止有人抓包重放。我曾经在调试一个项目时,发现设备反复入网失败,查了半天,原来是DevNonce生成函数写错了,每次复位都生成同一个值。嗯,这种坑踩过一次就记住了。

2.3 Join Accept:服务器的回应

服务器收到Join Request后,会验证DevNonce是否重复。如果没问题,就回复Join Accept。

| 字段 | 大小 | 说明 |
|------|------|------|
| JoinNonce | 3字节 | 服务器生成的随机数 |
| NetID | 3字节 | 网络标识 |
| DevAddr | 4字节 | 设备在网络中的短地址 |
| DLSettings | 1字节 | 下行参数设置 |
| RxDelay | 1字节 | 接收窗口延迟 |
| CFList | 可选 | 频道频率列表 |

注意,Join Accept是加密的。用什么加密?用AppKey或者NwkKey。这里有个版本差异:LoRaWAN 1.0.x用AppKey,1.1.x之后用NwkKey。我建议新项目直接用1.1.x,因为安全性更好。

加密后的Join Accept,设备收到后用自己的AppKey/NwkKey解密。解密成功,说明服务器是合法的。这就是双向认证——设备验证服务器,服务器也验证设备。

2.4 AppKey与NwkKey:到底谁管谁?

很多初学者搞不清AppKey和NwkKey的区别。我打个比方:

  • AppKey:相当于你家大门的钥匙。有了它,你就能进小区(网络层)。
  • NwkKey:相当于你房间的钥匙。有了它,你才能进自己的房间(应用层)。

在LoRaWAN 1.0.x中,只有一把钥匙——AppKey。它既负责网络层的安全,也负责应用层的安全。说白了就是一把钥匙开两把锁。

在LoRaWAN 1.1.x中,分成了两把钥匙:

  • NwkKey:用于网络层,派生NwkSKey
  • AppKey:用于应用层,派生AppSKey

这样做的好处是:即使网络层被攻破,应用层数据还是安全的。我在做城市照明项目时,客户要求应用数据必须端到端加密,网络层不能看到明文。这种场景就必须用1.1.x的分离密钥方案。

重要提醒:AppKey和NwkKey都是在设备出厂前预置的。它们存储在设备的安全元件中,不能通过无线方式读取。如果泄露,整个网络的安全就完蛋了。

2.5 Session密钥派生过程:这才是核心

好,现在设备收到了Join Accept,解密成功。接下来要做什么?派生Session密钥。

Session密钥有两把:

  • NwkSKey:网络会话密钥,用于MAC命令的加密和完整性校验
  • AppSKey:应用会话密钥,用于应用数据的加密

派生过程用的是AES-CMAC算法。具体公式如下:

NwkSKey = aes128_cmac(NwkKey, 0x01 | JoinNonce | NetID | DevNonce | pad16)
AppSKey = aes128_cmac(AppKey, 0x02 | JoinNonce | NetID | DevNonce | pad16)

注意看,输入参数里包含了:

  • JoinNonce:服务器给的随机数
  • NetID:网络标识
  • DevNonce:设备自己的随机数

这三个参数组合在一起,保证了每次激活生成的Session密钥都是唯一的。即使同一个设备再次激活,因为DevNonce变了,密钥也会变。

我举个例子:假设你的设备第一次激活时DevNonce=0x1234,第二次激活时DevNonce=0x5678。两次的JoinNonce也可能不同。这样派生的NwkSKey和AppSKey就完全不同。这就是所谓的「会话密钥」——一次会话一把钥匙。

我的经验:在调试时,可以用Wireshark抓包,然后手动计算Session密钥来验证。LoRaWAN官方文档附录里有计算示例,我建议你照着算一遍。算通了,你就真正理解了。

2.6 激活后的通信

Session密钥派生完成后,设备就可以用NwkSKey和AppSKey进行正常通信了。注意:

  • 上行数据用NwkSKey计算MIC(消息完整性校验码)
  • 应用数据用AppSKey加密
  • MAC命令用NwkSKey加密

直到设备断电或主动发起重新入网,这些Session密钥一直有效。如果设备掉电重启,Session密钥就丢了,需要重新走一遍OTAA流程。

避坑指南:我曾经遇到一个情况,设备频繁掉线重连,每次都要重新OTAA。后来发现是电源设计有问题,电压波动导致设备复位。每次复位后Session密钥丢失,又要重新激活。这不仅浪费流量,还增加了网络负担。所以,如果你的设备需要频繁OTAA,先检查电源稳定性。

2.7 小结

OTAA的核心就三点:

  1. Join Request:设备用明文打招呼,带上自己的身份和随机数
  2. Join Accept:服务器用预置密钥加密回复,设备解密验证
  3. 密钥派生:双方用相同的算法、相同的输入,各自算出Session密钥

整个过程不需要传输Session密钥本身,安全性由预置的AppKey/NwkKey保证。这就是OTAA的精髓——密钥不出门,安全有保障

下一节,我会讲ABP激活方式。到时候咱们对比着看,你就知道为什么OTAA是推荐方案了。