4. BLE 配对与绑定机制

好,咱们今天来聊聊 BLE 配对和绑定。这玩意儿,说白了就是让两个蓝牙设备互相认识、建立信任的过程。我刚开始做蓝牙项目时,总觉得配对就是点一下“连接”就完事了,结果产品在客户手里死活连不上,后来才发现是配对流程没走对。嗯,这里面的坑,咱们一个一个填。

4.1 配对流程:三步走

BLE 配对不是一锤子买卖,它分三个阶段。我习惯把它叫做“握手、验证、分钥匙”。

  1. Pairing Feature Exchange(配对特性交换):双方互相亮底牌——你支持什么 IO 能力?你用什么加密算法?说白了就是先看看对方能干啥。
  2. Pairing Authentication(配对认证):根据第一步的结果,选择具体的配对方式。这一步可能会弹窗让你输 PIN 码,或者让你确认数字。
  3. Transport Specific Key Distribution(传输特定密钥分发):认证通过后,双方交换加密密钥、身份解析密钥等。这些密钥存下来,下次连接就不用再配对了。

核心要点:配对是临时过程,绑定是持久化存储密钥。配对成功不一定绑定,但绑定一定先配对。

4.2 四种配对方式:选哪个?

配对方式的选择,取决于设备的 IO 能力。我做过一个智能门锁项目,客户要求“无屏无键盘”,那就只能用 Just Works。你想想看,一个锁上连个按钮都没有,怎么输 PIN 码?

配对方式 IO 能力要求 安全等级 典型场景
Just Works 无要求 低(无 MITM 保护) 传感器、灯泡
Passkey Entry 一方可输入,一方可显示 中 键盘、门锁
Numeric Comparison 双方都可显示和确认 高 手机配对、遥控器
OOB(Out of Band) 额外通信通道(NFC、二维码) 最高 支付设备、医疗设备

避坑指南:我曾经在一个项目中用了 Just Works,结果客户反馈“别人拿手机也能连我的灯”。后来加了 Passkey Entry,虽然用户体验差了点,但安全多了。记住:Just Works 不防中间人攻击。

4.3 绑定与密钥存储

绑定就是把配对过程中生成的密钥存到非易失性存储器里。下次两个设备再见面,直接加载密钥,跳过配对流程。

密钥存哪里?我一般建议存到 Flash 的专用分区里。注意几个要点:

  • LTK(Long Term Key):长期密钥,加密通信用的。
  • IRK(Identity Resolving Key):身份解析密钥,用来识别对方设备。
  • CSRK(Connection Signature Resolving Key):连接签名解析密钥,用于数据签名验证。

嗯,这里要注意:绑定信息是跟设备地址绑定的。如果设备地址变了(比如随机地址轮换),绑定就失效了。我踩过这个坑——产品出货后,用户手机重启就搜不到设备了,查了半天发现是地址类型没配对。

4.4 实战:实现 BLE 配对与绑定

咱们用 Nordic nRF5 SDK 举个例子。这段代码我调过很多次,基本是标准流程。

// 初始化配对参数
ble_gap_sec_params_t sec_params;
memset(&sec_params, 0, sizeof(sec_params));

sec_params.bond          = 1;                    // 使能绑定
sec_params.mitm          = 1;                    // 使能中间人保护
sec_params.lesc          = 1;                    // 使用 LE Secure Connections
sec_params.keypress      = 0;                    // 不使能按键通知
sec_params.io_caps       = BLE_GAP_IO_CAPS_DISPLAY_ONLY;  // 仅显示
sec_params.oob           = 0;                    // 不使用 OOB
sec_params.min_key_size  = 7;                    // 最小密钥长度
sec_params.max_key_size  = 16;                   // 最大密钥长度

// 发起安全请求
uint32_t err_code = sd_ble_gap_sec_params_reply(m_conn_handle, 
                                                BLE_GAP_SEC_STATUS_SUCCESS, 
                                                &sec_params, 
                                                NULL);
APP_ERROR_CHECK(err_code);

个人经验:我习惯把 io_caps 参数做成可配置的宏定义。这样同一个固件,通过修改宏就能切换配对方式,调试时特别方便。

绑定信息的存储,SDK 通常有现成的模块。比如 nRF5 的 peer_manager,它会自动管理绑定数据的读写。你只需要在初始化时调用:

// 初始化 Peer Manager
ret_code_t err_code = pm_init();
APP_ERROR_CHECK(err_code);

// 注册安全事件回调
err_code = pm_sec_manager_register(&sec_manager_cb);
APP_ERROR_CHECK(err_code);

回调函数里处理配对状态:

static void sec_manager_cb(pm_evt_t const * p_evt)
{
    switch (p_evt->evt_id)
    {
        case PM_EVT_CONN_SEC_SUCCEEDED:
            // 配对/绑定成功
            NRF_LOG_INFO("Pairing/Bonding succeeded");
            break;
            
        case PM_EVT_CONN_SEC_FAILED:
            // 配对/绑定失败
            NRF_LOG_ERROR("Pairing/Bonding failed, reason: %d", 
                          p_evt->params.conn_sec_failed.error);
            break;
            
        case PM_EVT_PEER_DATA_UPDATE_SUCCEEDED:
            // 绑定数据更新成功
            NRF_LOG_INFO("Bonding data updated");
            break;
            
        default:
            break;
    }
}

4.5 配对流程全景图

下面这张图,是我自己画的标准 BLE 配对流程。你看一眼,基本就能把整个流程串起来。

BLE 配对与绑定流程 阶段一:Pairing Feature Exchange Master 发送配对请求(包含 IO Capabilities、OOB Flag、AuthReq、Key Size) Slave 回复配对响应(同样包含上述参数) 双方确定配对方式:Just Works / Passkey Entry / Numeric Comparison / OOB 阶段二:Pairing Authentication 根据配对方式执行认证流程 Just Works:无用户交互,直接生成 TK Passkey Entry:一方显示 PIN,另一方输入 Numeric Comparison:双方显示数字并确认 阶段三:Transport Specific Key Distribution Master 分发 LTK、IRK、CSRK 给 Slave Slave 分发 LTK、IRK、CSRK 给 Master 双方将密钥存储到非易失性存储器(绑定完成) 注意:绑定成功后,下次连接直接加载密钥,无需重复配对

4.6 常见问题与调试技巧

调试配对问题,我一般用 nRF Connect 或者 LightBlue 抓日志。几个常见坑:

  • 配对请求无响应:检查从机是否调用了 sd_ble_gap_sec_params_reply。我遇到过从机没调用这个函数,主机一直等,超时断开。
  • 绑定信息丢失:检查 Flash 存储空间是否够用。绑定数据一般几百字节,但设备多了会占满。
  • 配对失败但没回调:检查安全回调函数是否注册了。嗯,这个我犯过两次,都是低级错误。

调试口诀:先看 IO 能力是否匹配,再看密钥长度是否一致,最后查 Flash 存储是否正常。

好了,BLE 配对与绑定这块,核心就是三步流程加四种方式。你只要把 IO 能力配对了,密钥存对了,基本不会出大问题。下次咱们聊 Wi-Fi AT 指令的配置,那个坑更多,哈哈。