4、加密与认证:AES-CCM加密、签名与数据完整性、白名单与地址解析
好,咱们进入BLE安全机制里最核心的一块。加密、签名、还有地址处理。说实话,很多开发者觉得BLE安全就是配个对、设个密码,其实远不止这些。你想想看,一个蓝牙门锁,如果加密没做好,别人在空气里抓包就能把门打开,那还得了?
我个人习惯把BLE安全分成三层来看:链路层加密保证数据不被偷看,签名保证数据没被篡改,地址策略保证设备不被跟踪。这三层缺一不可。今天咱们就把它们一个一个说透。
核心要点:BLE安全不是单一技术,而是加密(AES-CCM)、完整性(CMAC签名)、隐私(白名单+地址解析)三者的组合拳。缺一个,你的产品就可能被攻破。
4.1 AES-CCM 加密:BLE的看家本领
AES-CCM,全称是 Advanced Encryption Standard - Counter with CBC-MAC。名字挺长,但说白了就是两件事:用CTR模式加密数据,用CBC-MAC算一个校验码。两个合在一起,既保证了机密性,又保证了完整性。
我记得第一次在项目里实现BLE加密时,踩了一个坑——Nonce(随机数)的构造。BLE协议对Nonce的格式有严格规定:8字节的IV(初始化向量)加上4字节的分组计数器,再加上1字节的标志位。如果你随便填,加密出来的数据对方根本解不开。
实战经验:Nonce 在 BLE 里绝对不能重复使用。同一个密钥下,Nonce 重复 = 加密被破解。我习惯在每次连接建立时重新生成 IV,并且用硬件随机数发生器来保证真随机。
AES-CCM 在 BLE 里的工作流程是这样的:
- 密钥协商:配对过程中,双方通过 ECDH 或 Just Works 等方式协商出 LTK(长期密钥)。
- Nonce 生成:主设备生成 IV,从设备确认。双方各自维护分组计数器。
- 加密传输:每个数据包用 CTR 模式加密,同时计算 MIC(消息完整性校验码)。
- 接收验证:接收方用同样的密钥和 Nonce 解密,并验证 MIC。MIC 对不上,直接丢弃。
这里有个关键点——MIC 的长度。BLE 4.0/4.1 固定用 4 字节 MIC,但从 4.2 开始支持 8 字节、12 字节甚至 16 字节。长度越长,安全性越高,但开销也越大。我个人建议,如果你的产品对功耗不敏感,尽量用 8 字节 MIC。4 字节 MIC 在理论上存在碰撞风险,虽然概率很低,但安全设计讲究的是「纵深防御」。
| MIC 长度 | 安全性等级 | 额外开销 | 推荐场景 |
|---|---|---|---|
| 4 字节 | 基础 | 低 | 低功耗传感器、Beacon |
| 8 字节 | 中等 | 中 | 智能家居、可穿戴设备 |
| 12 字节 | 高 | 较高 | 医疗设备、工业控制 |
| 16 字节 | 最高 | 高 | 金融支付、高安全场景 |
4.2 签名与数据完整性:别让数据在路上被改
加密能防止数据被偷看,但防不了数据被篡改。你想想看,攻击者虽然看不懂加密数据,但他可以把加密数据包整个替换掉,或者重放一个旧的数据包。这时候就需要签名来保证完整性。
BLE 里用的签名算法是 AES-CMAC,本质上就是用 AES 算法算一个消息认证码。和加密不同的是,签名不需要 Nonce,也不需要分组计数器。它只依赖一个共享密钥——CSRK(连接签名解析密钥)。
我在项目中遇到过这样一个场景:一个蓝牙锁具,用户用手机开锁。如果只加密不签名,攻击者可以录制一次开锁的加密数据包,然后在用户离开后重放。加了签名之后,每个数据包都带有一个基于数据内容和 CSRK 算出来的签名值,重放的数据包签名对不上,锁具直接忽略。
注意:签名和加密是两个独立的安全机制。你可以只加密不签名,也可以只签名不加密,还可以两者都用。但我的建议是——永远不要只签名不加密。签名本身会暴露数据的某些特征,攻击者可以通过签名值推断数据内容。加密 + 签名才是完整方案。
签名的计算过程其实不复杂:
// 伪代码:AES-CMAC 签名计算
function calculateCMAC(key, data):
// 1. 用密钥生成子密钥 K1 和 K2
K1 = generateSubkey(key, 0x87)
K2 = generateSubkey(key, 0x1B)
// 2. 对数据进行填充(如果需要)
paddedData = pad(data)
// 3. 分块处理,每块 16 字节
for each block in paddedData:
if block is last block:
if block length == 16:
block = block XOR K1
else:
block = block XOR K2
cipher = AES_ECB_encrypt(key, block XOR previousCipher)
previousCipher = cipher
// 4. 取前 4 或 8 字节作为签名
return cipher[0:signatureLength]
嗯,这里要注意:签名长度在 BLE 里通常取 4 字节或 8 字节。4 字节签名用于低功耗场景,8 字节用于高安全场景。和 MIC 一样,签名越长越安全,但功耗和带宽开销也越大。
4.3 白名单与地址解析:让设备学会「隐身」
好,加密和签名解决了数据的安全问题,但还有一个容易被忽视的问题——设备隐私。你想想看,如果蓝牙设备每次广播都用固定的 MAC 地址,攻击者就可以跟踪这个设备的移动轨迹。你今天带着蓝牙耳机去公司、回家、去健身房,全被记录下来了。
BLE 的解决方案是私有地址。设备定期更换自己的 MAC 地址,让攻击者无法关联。但问题来了——如果地址一直在变,合法的连接设备怎么认出它?
这就是白名单 + 地址解析要解决的问题。
白名单,说白了就是一张「允许连接我」的设备列表。每个设备有一个固定的身份标识(Identity Address),白名单里存的就是这些身份地址。当设备使用私有地址时,接收方用 IRK(身份解析密钥)来解析这个私有地址,看它是否对应白名单里的某个设备。
地址解析的过程是这样的:
- 设备 A 用 IRK 和随机数生成一个可解析的私有地址。
- 设备 B 收到这个地址后,用自己存储的 IRK 列表逐个尝试解析。
- 如果某个 IRK 能成功解析出地址,说明设备 A 在白名单里。
- 如果所有 IRK 都解析失败,设备 B 直接忽略这个连接请求。
关键点:白名单和地址解析是配合使用的。白名单决定「谁可以连我」,地址解析决定「你是谁」。没有地址解析,白名单就形同虚设——因为设备地址一直在变,你根本不知道谁在连你。
我曾经在一个智能门锁项目里吃过亏。当时只配了白名单,没启用地址解析。结果发现,门锁每次广播时,手机都能连上,但门锁根本不知道是谁在连——因为手机每次都用不同的私有地址。后来加上地址解析,问题才解决。
白名单的配置方式有两种:
| 配置方式 | 说明 | 适用场景 |
|---|---|---|
| 静态白名单 | 在固件中预置设备列表 | 固定配对设备,如遥控器、传感器 |
| 动态白名单 | 通过配对过程动态添加 | 手机 App 配对后自动加入 |
我个人更推荐动态白名单。静态白名单虽然简单,但一旦设备需要更换,就得升级固件,用户体验很差。动态白名单配合配对流程,用户只需要在 App 上点一下「配对」,设备就自动加入白名单,方便得多。
最后说一个容易被忽略的细节——地址刷新周期。BLE 协议规定,私有地址至少每 15 分钟更换一次。但实际项目中,我建议根据场景调整:
- 高隐私场景(如医疗设备):每 1-2 分钟更换一次。
- 普通场景(如智能家居):每 15 分钟更换一次。
- 低功耗场景(如 Beacon):每 30-60 分钟更换一次,甚至不更换(使用不可解析私有地址)。