安全与加密: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 能力为 KeyboardOnly 或 DisplayYesNo。但要注意,这需要设备端也做相应配置。

6. 知识体系图

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

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

7. 常见问题与避坑

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

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

8. 总结

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

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

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


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