3、LoRaWAN安全机制:Join过程、AppKey与NwkKey、会话密钥派生

聊LoRaWAN安全,很多人第一反应就是“AES加密”。但说实话,加密只是最后一步。真正决定安全性的,是设备怎么入网、密钥怎么来的。我早年调试一个水表项目时,就因为Join过程没走对,结果设备死活发不出数据——后来发现是会话密钥根本没派生成功。嗯,今天我们就来把这套机制彻底捋清楚。

3.1 为什么需要Join过程?

LoRaWAN设备不是插电就能用的。它必须先跟网络服务器“握手”,拿到一组临时密钥,才能开始通信。这个握手过程,就叫Join。

你想想看,如果每个设备都用同一个固定密钥加密,那一旦密钥泄露,整个网络就完了。所以LoRaWAN设计了一套动态密钥派生机制:每次入网都会生成新的会话密钥,即使这次被破解,下次入网又会换一套。

核心思想: 长期密钥(AppKey/NwkKey)只用于入网认证,不用于数据加密。数据加密用的是每次入网临时派生的会话密钥(AppSKey/NwkSKey)。

3.2 两个长期密钥:AppKey与NwkKey

在LoRaWAN 1.0.x版本中,只有一个根密钥叫AppKey。但从1.1版本开始,为了安全分离,拆成了两个:NwkKey和AppKey。

密钥名称 长度 用途 存储位置
NwkKey 128位(AES-128) 网络层密钥,用于Join过程完整性校验、派生NwkSKey 设备 + 网络服务器
AppKey 128位(AES-128) 应用层密钥,用于派生AppSKey 设备 + 应用服务器

我个人习惯把NwkKey理解为“网络身份证”,AppKey理解为“应用保险箱钥匙”。两者分开后,即使网络服务器被攻破,攻击者也拿不到AppKey,应用数据依然是安全的。

注意: 在LoRaWAN 1.0.x中,只有一个AppKey,同时承担网络和应用的双重职责。升级到1.1后,务必确认你的网络服务器和应用服务器是否支持双密钥模式。我曾经见过一个项目,服务器端还是1.0的固件,设备端烧了1.1的密钥,结果Join永远失败。

3.3 Join过程的完整流程

Join过程说白了就是一次“密钥交换握手”。设备发送Join Request,服务器回复Join Accept,然后双方各自派生出会话密钥。

LoRaWAN Join 过程流程图 LoRaWAN 设备 网络服务器 应用服务器 ① Join Request (DevEUI + AppEUI + DevNonce) ② 验证MIC ③ 生成Join Accept (AppNonce + 网络参数 + MIC) ④ Join Accept ⑤ 设备派生 AppSKey + NwkSKey ⑥ 服务器派生 AppSKey + NwkSKey ⑦ 分发AppSKey ⑧ 安全数据通信(使用AppSKey加密 + NwkSKey校验) 关键点:DevNonce必须唯一,否则服务器会拒绝Join Request,防止重放攻击

3.3.1 第一步:设备发送Join Request

设备上电后,会组装一个Join Request消息,包含以下字段:

  • DevEUI:设备唯一标识,类似MAC地址,全球唯一。
  • AppEUI(或JoinEUI):应用标识,告诉服务器“我想加入哪个应用”。
  • DevNonce:一个随机数,用于防止重放攻击。

整个消息会使用NwkKey计算一个MIC(消息完整性校验码),附在末尾。服务器收到后,会用同样的NwkKey重新计算MIC,如果对不上,直接丢弃。

避坑指南: DevNonce必须保证每次入网都不一样。我曾经遇到一个客户,设备代码里DevNonce生成逻辑写错了,每次上电都生成同一个值。结果服务器认为这是重放攻击,直接拒绝入网。折腾了两天才找到原因。

3.3.2 第二步:服务器验证并回复Join Accept

网络服务器收到Join Request后,会做三件事:

  1. 验证MIC是否正确(用NwkKey)。
  2. 检查DevNonce是否重复(防止重放)。
  3. 生成一个AppNonce(服务器随机数),用于后续密钥派生。

然后服务器组装Join Accept消息,包含AppNonce、网络参数(如频段、速率等),同样用NwkKey计算MIC。这个消息会加密后发给设备。

3.3.3 第三步:双方派生会话密钥

设备收到Join Accept后,用同样的NwkKey解密并验证MIC。验证通过后,双方就可以用相同的算法派生出会话密钥了。

3.4 会话密钥派生:AppSKey与NwkSKey

派生算法用的是AES-128的CMAC模式。说白了,就是把AppNonce、DevNonce等参数拼在一起,用NwkKey或AppKey做AES加密,输出就是会话密钥。

具体公式如下(LoRaWAN 1.1版本):

// NwkSKey 派生(使用 NwkKey)
NwkSKey = aes128_cmac(NwkKey, 0x01 || AppNonce || DevNonce || ...)

// AppSKey 派生(使用 AppKey)
AppSKey = aes128_cmac(AppKey, 0x02 || AppNonce || DevNonce || ...)

这里有个细节:0x01和0x02是区分符,确保NwkSKey和AppSKey不会算出相同的值。我当年第一次看协议时,还纳闷为什么加个固定字节——后来自己写代码时才发现,不加这个区分符,两个密钥可能一模一样,那就出大问题了。

会话密钥 长度 用途 派生源
NwkSKey 128位 网络层:校验数据包完整性(MIC)、加密MAC命令 NwkKey
AppSKey 128位 应用层:加密/解密应用数据(传感器数据、控制指令等) AppKey

重要原则: NwkSKey只存在于设备和网络服务器之间,AppSKey只存在于设备和应用服务器之间。网络服务器不应该知道AppSKey,应用服务器也不应该知道NwkSKey。这样即使一方被攻破,另一方数据依然安全。

3.5 密钥的生命周期管理

会话密钥不是永久的。每次设备重新入网(重新Join),都会生成新的会话密钥。旧的会话密钥立即失效。

但长期密钥(NwkKey和AppKey)是设备出厂时烧录的,理论上终身不变。所以:

  • 出厂阶段:在安全环境中将NwkKey和AppKey写入设备。
  • 运行阶段:设备每次入网,动态派生会话密钥。
  • 密钥更新:如果需要更换长期密钥,只能通过物理接触设备(比如用串口重新烧录)。

安全提醒: 千万不要把NwkKey或AppKey通过无线方式传输!我见过一个方案,为了“方便管理”,在设备入网后通过下行命令把AppKey读回来——这等于把保险箱密码写在门上。正确的做法是:长期密钥只在出厂时写入,之后永远不出设备。

3.6 小结:一张图看懂密钥体系

最后,我用一张图总结整个LoRaWAN密钥体系的关系:

LoRaWAN 密钥体系总览 NwkKey(长期) 网络根密钥 AppKey(长期) 应用根密钥 NwkSKey(会话) 网络会话密钥 AppSKey(会话) 应用会话密钥 Join过程派生 (每次入网重新生成) MIC校验 + MAC命令加密 应用数据加解密

嗯,到这里,LoRaWAN的Join过程和密钥体系就讲完了。记住一句话:长期密钥保安全,会话密钥保通信。下次你调试设备入网失败时,先检查DevNonce是否重复,再检查MIC计算是否正确——这两个坑,我踩过不止一次。