安全机制

BLE SMP 过程详解:从配对请求到密钥分发的完整流程

发布于 2026-07-21 · 来自「嵌入式江湖」技术专栏

在 BLE 里谈到"配对""绑定""加密",真正的执行者其实是 SMP(Security Manager Protocol,安全管理协议)。上层 GAP 只负责"要不要配对"的触发,而双方怎么协商方法、怎么算密钥、怎么分发长期密钥,全在 SMP 这一层完成。

这篇文章把 SMP 的完整过程拆开讲:它在协议栈什么位置、一条配对从 Pairing Request 到密钥分发经历了哪些步骤、Legacy(传统配对)和 Secure Connections(安全连接)流程有何本质区别,以及六种关联模型在 SMP 里到底怎么落地。

一、SMP 在协议栈的位置

SMP 跑在 L2CAP 之上,使用固定的逻辑信道 CID = 0x0006(Security Manager Channel)。它不像普通业务数据走动态 CID,而是 BLE 专门预留的一条"安全专用通道"。

应用层
  └─ GAP(触发配对 / 维护绑定信息)
       └─ SMP(安全管理协议)── L2CAP 固定信道 0x0006
            └─ L2CAP
                 └─ Link Layer(HCI 之下)

要点: - SMP 只处理"安全相关"的 PDU,普通数据不走这条信道。 - 配对期间,这条信道上是排他的——不能夹着别的 L2CAP 流量。 - SMP 协商出来的密钥最终交给 LL 层去执行 链路加密(LE Encryption),加密本身不在 SMP 里做。

二、配对总览:四个阶段

一次完整配对(以绑定为例)大致分四个阶段:

  1. 阶段 1 — 配对特性交换(Pairing Feature Exchange) 双方互发 Pairing Request / Pairing Response,把能力、需求、要分发的密钥都摊开。
  2. 阶段 2 — 临时密钥 / DHKey 建立与认证
  3. Legacy:基于 TK 算 Confirm 互相验证,再算 STK(短期密钥)
  4. Secure Connections:基于 ECDH 的 DHKey 派生 LTK,并用 DHKey Check 互相验证。
  5. 阶段 3 — 链路加密 用阶段 2 得到的密钥(STK 或 LTK)对链路加密。
  6. 阶段 4 — 长期密钥分发(Key Distribution) 分发 LTK / IRK / CSRK 等,双方存好,完成"绑定",下次直接拿 LTK 加密。

三、阶段 1:Pairing Feature Exchange 字段详解

这是配对的"握手",双方交换的字段决定了后面走哪条路。核心字段:

字段 含义
IO Capability 输入输出能力:DisplayOnly / DisplayYesNo / KeyboardOnly / NoInputNoOutput / KeyboardDisplay
OOB 是否使用带外(NFC 等)数据
AuthReq 关键位域:Bonding 类型(2bit)、MITM(1bit)、SC(1bit)、Keypress(1bit) 等
Max Encryption Key Size 加密密钥长度,7~16 字节,默认 16
Initiator Key Distribution 发起方要分发哪些密钥:EncKey(LTK)/IdKey(IRK)/SignKey(CSRK)
Responder Key Distribution 响应方要分发哪些密钥

AuthReq 的几个位尤其重要,因为它们直接决定关联模型和是否抗中间人:

// AuthReq 字节位定义(Core Spec)
// bit0-1: Bonding Flags  00=不绑定 01=绑定(可接收) 10=绑定(专用) 11=保留
// bit2  : MITM          1=需要 MITM 保护
// bit3  : SC            (Secure Connections) 1=支持/要求 LESC
// bit4  : Keypress      1=支持 Passkey 按键通知
// bit5  : CT2           1=使用新签名算法

一个典型解析示例:

uint8_t auth_req = 0x0D;  // 二进制 0000 1101
int bonding   = auth_req & 0x03;        // = 01 -> 绑定
int mitm      = (auth_req >> 2) & 0x01; // = 1  -> 要求 MITM
int sc        = (auth_req >> 3) & 0x01; // = 1  -> 支持 Secure Connections
int keypress  = (auth_req >> 4) & 0x01; // = 0

四、关联模型是怎么选出来的

阶段 1 交换完,IO Capability + OOB + MITM + SC 共同决定双方用哪种 关联模型(Association Model)

模型 适用场景 MITM 保护 仅 SC?
Just Works 双方都没输入/显示能力 ❌ 无
Numeric Comparison 双方都能显示 6 位数、都能确认 ✅ 有 ✅ 仅 SC
Passkey Entry 一方能输入 6 位 PIN ✅ 有
OOB 通过 NFC 等带外交换信息 ✅ 最强

选择逻辑(简化): - 若 OOB=1 → 用 OOB。 - 否则若 SC=1 且双方能力允许 → 优先 Numeric Comparison。 - 否则按 IO 能力矩阵查表选 Just Works / Passkey Entry。 - 若一方要求 MITM=1 但所选模型无 MITM 保护(典型:Just Works)→ 配对会因"Authentication Requirements"失败(Pairing Failed 原因码 0x03)。

五、Legacy Pairing(传统配对)过程

Legacy 基于一个 128 位的 TK(Temporary Key),但 TK 的熵受关联模型限制,这是它最大的软肋。

5.1 TK 的来源

  • Just WorksTK = 0(全零,等于没密钥)
  • Passkey EntryTK = 用户输入的 6 位 PIN(仅 20 bit 有效,其余补零)
  • OOB:TK 来自带外信道

5.2 确认值交换(防中间人,但 Just Works 形同虚设)

双方各自用 TK 和随机数计算 Confirm 并交换验证:

Confirm_M = c1(TK, Mrand, Pairing Request, Pairing Response, ...)
Confirm_S = c1(TK, Srand, Pairing Request, Pairing Response, ...)

互相发 Pairing Confirm,再发 Pairing Random 让对方重算核对。若一致,说明中间没人篡改 TK。

注意:Just Works 下 TK=0,中间人能算出同样的 Confirm,所以这个"验证"在 Just Works 下没有任何抗中间人作用

5.3 STK 计算与加密

STK = s1(TK, Srand, Mrand)

双方得到相同的 STK,用 STK 作为临时 LTK 对链路加密(阶段 3)。

5.4 密钥分发(阶段 4)

加密建立后,按阶段 1 协商的标志分发长期密钥: - Encryption Information (0x06):分发 LTK - Master Identification (0x07):分发 EDIV + Rand(LTK 的索引) - Identity Information (0x08):分发 IRK(用于 RPA 解析) - Identity Address Information (0x09):分发 Identity Address - Signing Information (0x0A):分发 CSRK(数据签名)

分发完成后,双方把 LTK/IRK 存进绑定库,绑定达成(绑定后下次重连如何直接用 LTK 加密,详见第九节)。

5.5 Legacy 的根本缺陷

  • TK 熵低(Just Works 为 0,Passkey 仅 20 bit),可被离线暴力破解。
  • 被动窃听者记录整次配对后,可算出 STK 并解密后续通信
  • 因此 Legacy 仅适合低风险场景,且 Just Works 完全没有保密性增益。

六、Secure Connections(安全连接)过程

SC 基于 ECDH(P-256 椭圆曲线),从根本上解决了 Legacy 的弱点。它不再有 STK,而是直接协商出长期 LTK。

6.1 公钥交换

双方互发 Pairing Public Key (0x0C),各自生成 ECDH 密钥对,算出共享的 DHKey(128 位)。DHKey 的保密性依赖椭圆曲线离散对数难题,远强于 TK。

6.2 认证(取决于关联模型)

  • Numeric Comparison:双方各自显示 6 位 ra/rb,用户确认一致。这是 SC 特有且同时具备 MITM 保护和数据保密性的模型。
  • Passkey Entry / OOB:同样基于 DHKey 派生,但走各自的验证方式。

6.3 用 f5 / f6 派生与校验

SC 定义了一组加密函数(基于 AES-CMAC):

MacKey || LTK = f5(DHKey, Na, Nb, ra, rb)   // 派生 MAC 密钥 和 长期密钥 LTK
AU_M = f6(MacKey, Ns, Nr, ...)              // 计算 DHKey Check 值

双方计算 DHKey Check (0x0D) 并互相验证:

DHKey Check 一致 -> 双方确认真的握过手、DHKey 未被篡改

6.4 直接加密 + 分发

SC 下没有 STK,链路直接用 f5 派生的 LTK 加密(阶段 3),随后阶段 4 分发 IRK/CSRK 等完成绑定。

关键区别一句话:Legacy 用 STK 临时加密再分发 LTK;SC 直接用协商出的 LTK 加密,STK 这层被取消,安全性和效率都更好。

七、完整命令序列对照

下面两条是典型流程的 PDU 顺序(省略 LL 层加密命令):

Legacy(Just Works)

<SMP> Pairing Request
<SMP> Pairing Response
<SMP> Pairing Confirm   (M->S)
<SMP> Pairing Confirm   (S->M)
<SMP> Pairing Random    (M->S)
<SMP> Pairing Random    (S->M)
<LL>  Encryption Request (用 STK)
<SMP> Encryption Information (LTK)
<SMP> Master Identification (EDIV+Rand)
<SMP> Identity Information (IRK)
<SMP> Identity Address Information

Secure Connections(Numeric Comparison)

<SMP> Pairing Request
<SMP> Pairing Response
<SMP> Pairing Public Key (M->S)
<SMP> Pairing Public Key (S->M)
<UI>  双方显示 6 位数,用户确认
<SMP> Pairing Confirm (M->S)
<SMP> Pairing Confirm (S->M)
<SMP> Pairing Random  (M->S)
<SMP> Pairing Random  (S->M)
<SMP> Pairing DHKey Check (M->S)
<SMP> Pairing DHKey Check (S->M)
<LL>  Encryption Request (用 LTK)
<SMP> Identity Information (IRK) / Signing Information (CSRK)

八、常见 Pairing Failed 原因码

配对失败时对方会回 Pairing Failed (0x05),附带原因码,调试时很有用:

原因码 含义
0x01 Passkey Entry Failed(PIN 输错)
0x02 OOB Not Available
0x03 Authentication Requirements(MITM 需求无法满足)
0x04 Confirm Value Failed(Confirm 校验不过,疑似中间人)
0x05 Pairing Not Supported
0x06 Encryption Key Size(密钥长度不达标)
0x07 Command Not Supported
0x08 Unspecified Reason
0x09 Repeated Attempts(频繁重试,被限流)
0x0A Invalid Parameters
0x0B DHKey Check Failed(SC 校验失败)
0x0C Numeric Comparison Failed(数字比对不一致)

九、BOND 绑定与重连过程

前面几节把"配对"讲完了。但配对只是这一次协商密钥、建立加密;真正让"下次连上就直接加密、不用重新配对"的,是 绑定(Bonding)

9.1 绑定到底是什么

  • 配对(Pairing):执行 SMP 流程,协商出密钥、建立加密。配对完密钥可以立刻丢弃。
  • 绑定(Bonding) = 配对成功 双方把长期密钥持久化存进"绑定库(Bonding / Security Database)"。

也就是说: - 配对不一定绑定(AuthReq 的 Bonding Flag = 00 时,配对完即丢,下次还得重新配对)。 - 绑定一定先经历配对。 只有设置了 Bonding Flag(01/10)且阶段 4 密钥分发完成,双方才真正"绑定"。

9.2 绑定后双方各自存了什么

以中心(如手机)与外围设备(如耳机)为例,各自存一份对端的安全信息: - LTK(长期密钥):后续直接用来加密的密钥 - EDIV + Rand:LTK 的索引 / 查找键(Legacy 用;SC 下 EDIV=Rand=0)。Rand 是 64 位随机数、EDIV 是 16 位,二者组合用来在绑定库里定位这条 LTK;它不参与密钥派生,作用更像"数据库主键"而非密码学里的 salt(盐)。 - IRK(Identity Resolving Key):用来解析对方的可解析随机地址 RPA - CSRK(Connection Signature Resolving Key):数据签名用(较少用到) - Identity Address:对方的"真身"地址(Public 或 Static Random),用于把变来变去的 RPA 映射到具体设备 - 角色标记:本端是 Initiator 还是 Responder(决定持有哪部分密钥)

密钥分发的"方向性"来自阶段 1:Initiator Key Distribution 决定发起方要收哪些密钥,Responder Key Distribution 决定响应方要收哪些。常见是手机(Initiator)向设备要 LTK/IRK,设备(Responder)也常向手机要 IRK,以便反向识别。

9.3 下次连接时的重连流程(不再配对)

绑定的价值就在这里——已有 LTK,直接加密,整套 SMP 配对全部跳过。典型重连:

LL 连接建立(含交换 Feature)
<SMP> Security Request(从设备可主动发,提示"我需要加密")
<LL>  LL Encryption Request(携带已存的 LTK / EDIV+Rand)
<LL>  LL Start Encryption
<LL>  链路进入加密态

与首次配对的关键区别: - 没有 Pairing Request/ResponsePairing Confirm/RandomDHKey Check 等 SMP 配对命令。 - 直接用存储的 LTK 作为加密密钥,走 LL 层的 LE Encryption 流程。 - 阶段 4 的密钥分发整体跳过——LTK 早就存好了。

所以"绑定过的设备秒连且自动加密",本质就是这条轻量重连路径。

9.4 用 IRK 解析 RPA,认出老朋友

如果对方用的是可解析随机地址(RPA),每次广播地址都变。重连时本端怎么做:

  1. 拿到对端当前的 RPA(来自广播包或连接请求)。
  2. 用本地存的每个已绑定设备的 IRK,计算 ah(IRK, prand) 与对端 RPA 的 hash 比对。
  3. 命中哪一个,就说明"这是那个老朋友",对应到它的 Identity Address
  4. 取出该身份下的 LTK,发起加密。

这正是"耳机地址天天变,手机还能秒连"的原因——手机靠 IRK 在后台把变化后的地址"认"回原来的身份。

9.5 绑定管理

  • 删除绑定:任一方删除对端条目后,下次连接因 LTK 缺失,会重新走完整配对流程。
  • 多绑定槽:中心(手机)通常能绑很多外围设备(几十上百);外围设备(耳机/传感器)能绑的中心数受厂商绑定槽位限制,常见几个到几十个不等。
  • 谁发起加密:重连时由已持有 LTK 的一方(通常是曾做 Initiator 的中心)发起 LL Encryption Request;首次连接则由 GAP 触发 SMP 配对。

9.6 重连场景的坑

  • LTK 存错位置 / 重启被清空:设备每次重启都丢了绑定库,表现就是"每次都要重新配对",体验极差——绑定信息必须落在掉电不丢的存储区。
  • 用了 RPA 却没存 IRK:重连时无法解析变化后的地址,只能当新设备重新配对。
  • EDIV/Rand 与 LTK 不配对(Legacy):LTK 必须和正确的 EDIV/Rand 配套,存混了会导致 LL Encryption 失败。
  • 一端删绑定另一端没删:会出现"手机以为还绑着、设备已不认",连接能建立但加密失败,需双端都清理。

十、实战坑提醒

  1. Just Works 不保密:Legacy 下 Just Works 的 TK=0,被动窃听者可解密。敏感场景务必用带 MITM 的模型或上 SC。
  2. 密钥分发标志要双向一致:Initiator/Responder Key Distribution 协商不一致会导致配对失败或绑定不完整。
  3. Max Key Size 取双方最小值:一方只支持 7 字节会让整条链路降到 7 字节强度。
  4. 配对期间别发业务数据:SMP 信道在配对时是排他的,夹塞会出错。
  5. SC 需要双方都声明 SC=1:否则会回退到 Legacy,安全性降级。
  6. 绑定库要持久化:LTK/IRK 存错位置(比如每次重启清空),就会导致"每次都要重新配对"。
  7. 安卓/iOS 行为差异:iOS 对 MITM/SC 要求较严格,某些只支持 Just Works 的设备在 iOS 上可能配对失败;Android 相对宽松。
  8. DHKey Check Failed 多半是随机数/实现 bug:SC 下若 f5/f6 哪一步字节序或参数传错,会在这个阶段暴露。

十一、一句话总结

SMP 是 BLE 安全的"执行引擎":先用 Pairing Request/Response 摊开能力与需求,再按关联模型在 Legacy(TK→STK)或 Secure Connections(ECDH→LTK)两条路径上完成认证,最后分发长期密钥达成绑定。新项目一律优先 Secure Connections + Numeric Comparison,传统 Legacy 仅在兼容老设备时不得已而用。理解了 SMP 的命令序列,抓包看配对失败、定位 Pairing Failed 原因码就不再是黑盒。

更多 BLE / 鸿蒙星闪 / 芯片选型 / MCU 实战,持续更新