在 BLE 里谈到"配对""绑定""加密",真正的执行者其实是 SMP(Security Manager Protocol,安全管理协议)。上层 GAP 只负责"要不要配对"的触发,而双方怎么协商方法、怎么算密钥、怎么分发长期密钥,全在 SMP 这一层完成。
这篇文章把 SMP 的完整过程拆开讲:它在协议栈什么位置、一条配对从 Pairing Request 到密钥分发经历了哪些步骤、Legacy(传统配对)和 Secure Connections(安全连接)流程有何本质区别,以及六种关联模型在 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 里做。
一次完整配对(以绑定为例)大致分四个阶段:
Pairing Request / Pairing Response,把能力、需求、要分发的密钥都摊开。Confirm 互相验证,再算 STK(短期密钥)。DHKey Check 互相验证。这是配对的"握手",双方交换的字段决定了后面走哪条路。核心字段:
| 字段 | 含义 |
|---|---|
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 基于一个 128 位的 TK(Temporary Key),但 TK 的熵受关联模型限制,这是它最大的软肋。
TK = 0(全零,等于没密钥)TK = 用户输入的 6 位 PIN(仅 20 bit 有效,其余补零)双方各自用 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 下没有任何抗中间人作用。
STK = s1(TK, Srand, Mrand)
双方得到相同的 STK,用 STK 作为临时 LTK 对链路加密(阶段 3)。
加密建立后,按阶段 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 加密,详见第九节)。
SC 基于 ECDH(P-256 椭圆曲线),从根本上解决了 Legacy 的弱点。它不再有 STK,而是直接协商出长期 LTK。
双方互发 Pairing Public Key (0x0C),各自生成 ECDH 密钥对,算出共享的 DHKey(128 位)。DHKey 的保密性依赖椭圆曲线离散对数难题,远强于 TK。
ra/rb,用户确认一致。这是 SC 特有且同时具备 MITM 保护和数据保密性的模型。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 未被篡改
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 (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(数字比对不一致) |
前面几节把"配对"讲完了。但配对只是这一次协商密钥、建立加密;真正让"下次连上就直接加密、不用重新配对"的,是 绑定(Bonding)。
也就是说:
- 配对不一定绑定(AuthReq 的 Bonding Flag = 00 时,配对完即丢,下次还得重新配对)。
- 绑定一定先经历配对。
只有设置了 Bonding Flag(01/10)且阶段 4 密钥分发完成,双方才真正"绑定"。
以中心(如手机)与外围设备(如耳机)为例,各自存一份对端的安全信息:
- 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,以便反向识别。
绑定的价值就在这里——已有 LTK,直接加密,整套 SMP 配对全部跳过。典型重连:
LL 连接建立(含交换 Feature)
<SMP> Security Request(从设备可主动发,提示"我需要加密")
<LL> LL Encryption Request(携带已存的 LTK / EDIV+Rand)
<LL> LL Start Encryption
<LL> 链路进入加密态
与首次配对的关键区别:
- 没有 Pairing Request/Response、Pairing Confirm/Random、DHKey Check 等 SMP 配对命令。
- 直接用存储的 LTK 作为加密密钥,走 LL 层的 LE Encryption 流程。
- 阶段 4 的密钥分发整体跳过——LTK 早就存好了。
所以"绑定过的设备秒连且自动加密",本质就是这条轻量重连路径。
如果对方用的是可解析随机地址(RPA),每次广播地址都变。重连时本端怎么做:
ah(IRK, prand) 与对端 RPA 的 hash 比对。这正是"耳机地址天天变,手机还能秒连"的原因——手机靠 IRK 在后台把变化后的地址"认"回原来的身份。
LL Encryption Request;首次连接则由 GAP 触发 SMP 配对。LL Encryption 失败。SC=1:否则会回退到 Legacy,安全性降级。SMP 是 BLE 安全的"执行引擎":先用 Pairing Request/Response 摊开能力与需求,再按关联模型在 Legacy(TK→STK)或 Secure Connections(ECDH→LTK)两条路径上完成认证,最后分发长期密钥达成绑定。新项目一律优先 Secure Connections + Numeric Comparison,传统 Legacy 仅在兼容老设备时不得已而用。理解了 SMP 的命令序列,抓包看配对失败、定位 Pairing Failed 原因码就不再是黑盒。