安全与加密:BLE配对绑定、Just Works、Passkey Entry、OOB、加密数据传输

说到 BLE 的安全,我估计不少朋友第一反应就是「配对」。嗯,配对确实是安全的第一步,但它远不止「连上就行」那么简单。我在做第一个 BLE 项目时,就踩过配对的坑——设备连上了,数据也传了,结果发现传输的内容完全是明文,随便拿个抓包工具就能看到。那感觉,就像把家门钥匙挂在门外一样。

所以这一章,我们来把 BLE 的安全机制彻底捋清楚。说白了,就是回答三个问题:怎么配对?怎么绑定?怎么加密传数据?

1. BLE 安全模型概览

BLE 的安全模型分三层:

  • 未加密通信:最原始的状态,谁都能监听。
  • 加密通信:通过配对协商出密钥,之后的数据都加密传输。
  • 绑定:把密钥存下来,下次连接直接复用,不用再配对。

你想想看,如果你的设备每次连接都要重新配对输入 PIN 码,用户体验得多糟糕。绑定就是为了解决这个问题的。

核心概念:配对是过程,绑定是结果。配对完成后如果双方都存储了密钥,就叫绑定。

2. 配对流程拆解

BLE 配对分三个阶段,我习惯叫它「三步走」:

  1. 配对特征交换:双方互相告诉对方「我支持什么安全等级、我有什么 IO 能力」。
  2. 短期密钥(STK)生成:根据双方能力,选择一种配对方法生成临时密钥。
  3. 传输密钥分发:用 STK 加密通道,把长期密钥(LTK)等分发给对方。

这里有个细节:IO 能力决定了配对方法的选择。比如你的手机有屏幕和键盘,你的智能手环只有一个小按钮,那它们就只能用 Just Works。

3. 三种配对方法详解

3.1 Just Works

Just Works,名字就告诉你「只管干,别问」。它不需要用户输入任何东西,也不需要确认任何数字。说白了,就是自动完成配对。

适用场景

  • 设备没有输入输出能力(比如传感器、灯泡)
  • 对安全性要求不高的场景

安全风险

  • 无法防止中间人攻击(MITM)
  • 因为不需要用户确认,攻击者可以悄悄配对

注意:Just Works 虽然叫「Just Works」,但它仍然会生成加密密钥,数据是加密传输的。只是它无法验证对方身份。我曾经在一个智能门锁项目里看到有人用 Just Works,吓得我赶紧建议改成了 Passkey Entry。

3.2 Passkey Entry

Passkey Entry 需要在一台设备上显示一个 6 位数字,在另一台设备上输入这个数字。双方都确认后,配对才完成。

工作流程

  1. 设备 A 生成一个 6 位随机数(比如 123456)
  2. 设备 A 显示这个数字
  3. 用户在设备 B 上输入 123456
  4. 双方确认数字一致,配对继续

为什么能防 MITM? 因为中间人无法同时伪造两端的显示和输入。你想想看,攻击者要同时控制你的手机屏幕和键盘,这几乎不可能。

我的经验:Passkey Entry 在 Android 上实现时,要注意处理输入超时。我记得有一次用户输入太慢,导致配对失败,后来我把超时时间从 30 秒改成了 60 秒,问题就解决了。

3.3 OOB(Out of Band)

OOB 的意思是「带外」,也就是通过 BLE 之外的通道交换配对信息。最常见的例子就是用 NFC 碰一碰完成配对。

优势

  • 安全性最高,因为攻击者要同时攻破 BLE 和 OOB 通道
  • 用户体验好,碰一下就行

实现方式

  • NFC:手机碰设备,交换密钥
  • 二维码:扫描设备上的二维码获取密钥
  • 声波:通过音频传输密钥(少见)

避坑指南:我曾经在 OOB 实现中犯过一个低级错误——把 OOB 数据直接明文写在二维码里。后来意识到,OOB 数据本身也应该加密,否则二维码被拍照就泄露了。

4. 加密数据传输

配对完成后,BLE 会使用 AES-128 加密所有后续数据。具体来说:

  • 加密算法:AES-128-CCM
  • 密钥长度:128 位
  • 加密范围:所有 ATT 协议数据单元(PDU)

在 Android 端,你不需要手动处理加密。只要配对成功,系统会自动加密通信。你只需要在连接时指定安全等级:

// 设置安全等级为加密 + MITM 保护
bluetoothGatt.requestMtu(512)
bluetoothGatt.requestConnectionPriority(
    BluetoothGatt.CONNECTION_PRIORITY_HIGH
)

// 启动安全连接
bluetoothDevice.createBond()

但要注意,createBond() 只是发起配对请求,具体用什么配对方法,由系统根据双方 IO 能力决定。

5. Android 上的配对实践

在 Android 上处理 BLE 配对,有几个关键点:

5.1 监听配对状态

// 注册广播接收器监听配对状态
val filter = IntentFilter(BluetoothDevice.ACTION_BOND_STATE_CHANGED)
registerReceiver(bondStateReceiver, filter)

private val bondStateReceiver = object : BroadcastReceiver() {
    override fun onReceive(context: Context, intent: Intent) {
        val device = intent.getParcelableExtra<BluetoothDevice>(
            BluetoothDevice.EXTRA_DEVICE
        )
        val bondState = intent.getIntExtra(
            BluetoothDevice.EXTRA_BOND_STATE,
            BluetoothDevice.BOND_NONE
        )
        
        when (bondState) {
            BluetoothDevice.BOND_BONDING -> {
                Log.d("BLE", "配对中...")
            }
            BluetoothDevice.BOND_BONDED -> {
                Log.d("BLE", "配对成功")
                // 可以开始加密通信了
            }
            BluetoothDevice.BOND_NONE -> {
                Log.d("BLE", "未配对")
            }
        }
    }
}

5.2 处理 Passkey Entry 输入

当系统选择 Passkey Entry 时,你需要监听 ACTION_PAIRING_REQUEST

val filter = IntentFilter(BluetoothDevice.ACTION_PAIRING_REQUEST)
registerReceiver(pairingRequestReceiver, filter)

private val pairingRequestReceiver = object : BroadcastReceiver() {
    override fun onReceive(context: Context, intent: Intent) {
        val device = intent.getParcelableExtra<BluetoothDevice>(
            BluetoothDevice.EXTRA_DEVICE
        )
        val pairingVariant = intent.getIntExtra(
            BluetoothDevice.EXTRA_PAIRING_VARIANT,
            BluetoothDevice.PAIRING_VARIANT_PIN
        )
        
        if (pairingVariant == BluetoothDevice.PAIRING_VARIANT_PASSKEY_CONFIRMATION) {
            // 显示 6 位数字让用户确认
            val passkey = intent.getIntExtra(
                BluetoothDevice.EXTRA_PAIRING_KEY,
                0
            )
            // 在 UI 上显示 passkey,让用户确认
            showPasskeyDialog(passkey)
        }
    }
}

小技巧:如果你想让设备强制使用 Passkey Entry,可以在 GATT 服务中声明 IO 能力为 KeyboardOnlyDisplayYesNo。但要注意,这需要设备端也做相应配置。

6. 知识体系图

下面这张图总结了 BLE 安全的核心逻辑:

BLE 安全与加密知识体系 BLE 安全模型三层 未加密通信 加密通信 绑定(存储密钥) 三种配对方法 Just Works Passkey Entry OOB(带外) 配对完成后,使用 AES-128-CCM 加密所有 ATT 数据

7. 常见问题与避坑

问题 原因 解决方案
配对成功后数据还是明文 没有启用加密连接 检查 GATT 连接是否设置了 requestMtucreateBond
Passkey Entry 输入后配对失败 超时或数字不匹配 延长超时时间,确保两端数字一致
OOB 配对不触发 OOB 数据格式不对 检查 OOB 数据长度和哈希算法是否匹配
绑定后重连仍需配对 密钥未正确存储 检查设备端是否支持绑定,Android 端是否调用了 setDeviceKey

重要提醒:不要为了省事而跳过配对。我曾经见过一个项目,为了「用户体验」直接跳过配对,结果数据被隔壁房间的人用抓包工具全看光了。安全不是可选项,是必选项。

8. 总结

BLE 安全说白了就是三件事:选对配对方法、处理好绑定、确保数据加密。Just Works 最方便但最不安全,Passkey Entry 适合有输入输出的设备,OOB 最安全但实现复杂。

我个人建议:能用 Passkey Entry 就别用 Just Works,能用 OOB 就别用 Passkey Entry。安全等级每提升一级,你的用户数据就多一层保护。

嗯,这一章的内容就到这里。记住,安全不是功能,是底线。


公众号:蓝海资料掘金营,微信 deep3321