3. AppKey的职责:加密应用数据,保护你的业务载荷
好,我们进入正题。AppKey 到底是干什么用的?
简单说,AppKey 就是用来加密你的业务数据的。终端设备采集的温度、湿度、开关状态、GPS 坐标——这些真正有价值的信息,全靠 AppKey 来保护。
我刚开始接触 LoRaWAN 时,有个误区:以为所有密钥都是用来做入网认证的。后来被一个现场问题狠狠教育了一回——设备入网成功,数据也能上报,但服务器那边解析出来的 payload 全是乱码。查了半天,发现是 AppKey 和 NwkKey 搞混了。嗯,从那以后我才真正理解了这两个密钥的分工。
3.1 AppKey 到底加密了什么?
AppKey 只负责一件事:加密 FRMPayload。也就是应用层的数据载荷。
看一个 LoRaWAN 数据帧的结构你就明白了:
+----------------+----------------+----------------+----------------+
| MHDR (1B) | MACPayload | MIC (4B) | FRMPayload |
+----------------+----------------+----------------+----------------+
| FHDR | FPort | AppPayload |
| (7-22B)| (1B) | (N bytes) |
+--------+-------+----------------+
↑
AppKey 加密范围
注意看,FPort 是关键。当 FPort = 0 时,FRMPayload 用 NwkSKey 加密(这是网络层数据)。当 FPort = 1..223 时,FRMPayload 才用 AppSKey 加密——而 AppSKey 正是由 AppKey 派生出来的。
核心结论:AppKey 不直接加密数据,它通过派生 AppSKey 来加密应用层数据。FPort 决定了用哪个密钥。
3.2 AppKey 的派生链路
我个人习惯把这条链路记成「三跳」:
- AppKey(根密钥,写入设备 & 服务器)
- ↓ 派生 ↓(基于 DevEUI + JoinEUI + DevNonce)
- AppSKey(会话密钥,每次入网重新生成)
- ↓ 加密 ↓(AES-128 CTR 模式)
- FRMPayload(密文,在空口传输)
你想想看,为什么 LoRaWAN 不直接用 AppKey 加密数据?
因为安全设计的一个基本原则:根密钥不出设备。AppKey 只在 OTAA 入网时参与一次派生计算,之后就「退休」了。后续的数据加密全部由 AppSKey 完成。这样即使 AppSKey 被临时破解,也不会泄露根密钥。
3.3 我在项目中踩过的坑
讲个真实案例。去年有个智能水表项目,客户反馈:设备上线后数据偶尔能解析,偶尔全是乱码。
我远程抓包一看,发现一个规律:
- FPort = 1 时,数据正常
- FPort = 2 时,数据乱码
这就奇怪了。同一个设备,同一个 AppKey,为什么不同端口表现不一样?
查了三天,最后发现是固件里的一个 bug:FPort = 2 时,工程师手误把 NwkSKey 传给了加密函数。而 NwkSKey 和 AppSKey 虽然都是 128 位 AES 密钥,但内容完全不同——用 NwkSKey 去解 AppSKey 加密的数据,当然乱码。
避坑指南:我曾经在多个项目里看到类似的错误。建议你在代码里显式区分两个密钥变量名,比如 app_skey 和 nwk_skey,不要用 skey1、skey2 这种模糊命名。
3.4 AppKey 与 NwkKey 的职责对比
为了让你看得更清楚,我整理了一张对比表:
| 对比维度 | AppKey | NwkKey |
|---|---|---|
| 主要职责 | 派生 AppSKey,加密应用数据 | 派生 NwkSKey,加密网络控制数据 |
| 加密对象 | FRMPayload(FPort ≠ 0) | MAC 命令、FPort = 0 的 FRMPayload |
| 派生时机 | OTAA 入网时 | OTAA 入网时 |
| 生命周期 | 设备生命周期内不变 | 设备生命周期内不变 |
| 会话密钥 | AppSKey(每次入网不同) | NwkSKey + FNwkSIntKey + SNwkSIntKey |
| 谁持有 | 终端 + 应用服务器 | 终端 + 网络服务器 |
说白了,AppKey 管「业务」,NwkKey 管「网络」。一个负责你的数据内容安全,一个负责网络通信本身的安全。
3.5 加密过程实战演示
我们来看一段伪代码,模拟 AppSKey 加密 FRMPayload 的过程:
// 输入参数
AppKey = 0x2B7E151628AED2A6ABF7158809CF4F3C
DevEUI = 0x0102030405060708
JoinEUI = 0x0000000000000000
DevNonce = 0x1234
// 1. 派生 AppSKey
AppSKey = aes128_cmac(AppKey, 0x02 || JoinEUI || DevNonce || DevEUI || pad16)
// 2. 准备加密参数
Dir = 0x00 // 上行
FCnt = 0x0001
ConfFCnt = 0x0000
TxDr = 0x00
TxCh = 0x00
// 3. 生成 AES 密钥流
AES_CTR_Keystream = aes128_encrypt(AppSKey, 0x01 || 0x00 || Dir || DevEUI || FCnt || 0x00 || 0x00)
// 4. 加密明文 payload
plaintext = [0x01, 0x02, 0x03, 0x04] // 温度数据
ciphertext = plaintext XOR AES_CTR_Keystream[0:4]
注意看第 1 步的 0x02——这是 LoRaWAN 1.1 规范里规定的 AppSKey 派生标识符。NwkSKey 的派生标识符是 0x01。这个细节,我曾经在代码审查时抓到过三次错误。
小技巧:调试时,你可以用已知的 AppKey 和 DevNonce 手动计算一次 AppSKey,然后跟设备上报的密文做对比。如果解密后得到的是 0x00 或乱码,大概率是密钥派生参数传错了。
3.6 一张图看懂 AppKey 的职责
下面这张 SVG 图,把 AppKey 在整个数据加密链路中的位置画清楚了:
3.7 关于 AppKey 的几个关键认知
最后,我想强调几个容易被忽略的点:
- AppKey 不参与空中传输——它只存在于终端和服务器本地。如果有人告诉你「抓包能抓到 AppKey」,那一定是骗人的。
- AppKey 长度固定为 128 位(16 字节)。LoRaWAN 1.0.x 和 1.1 版本都是这个长度,没有例外。
- AppKey 丢失 = 数据永久不可读。我见过一个项目,设备量产时把 AppKey 写错了,结果 2000 台设备的数据全部无法解析。最后只能逐个设备返厂重写。
- AppKey 和 NwkKey 可以相同吗?技术上可以,但绝对不要这么做。安全设计的基本原则就是「职责分离」——两个密钥用同一个值,等于把业务数据和网络控制数据放在同一个保险柜里。
一句话总结:AppKey 是你的业务数据的「看门人」。它不直接露面,但通过派生 AppSKey,确保你的温度、湿度、电量等业务载荷在空口传输时,只有合法的应用服务器能读懂。