基础概念

BLE 设备地址 BD_ADDR 科普:Public、Random 与 RPA 隐私机制

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

你有没有想过:手机的蓝牙地址为什么每次扫描看到的都不一样?为什么 iPhone 明明是同一台设备,在 nRF Connect 里却隔一段时间就换个"身份"?这背后就是 BLE 的设备地址 —— BD_ADDR 在起作用。这篇文章把 BD_ADDR 的来龙去脉、地址类型、隐私机制彻底讲清楚。

一、BD_ADDR 是什么

BD_ADDR 全称 Bluetooth Device Address,是蓝牙设备的物理地址,长度 48 位(6 字节),作用相当于以太网世界里的 MAC 地址 —— 用来在链路层唯一标识一台设备。

在空口(over the air)和大多数协议栈 API 里,它按 小端序(little-endian) 传输和存储:字节顺序是反的。比如你在手机上看到显示为:

AA:BB:CC:DD:EE:FF

在抓包工具(如 Wireshark / nRF Sniffer)的原始字节里,它实际是:

FF EE DD CC BB AA

这个字节序问题是新手抓包时最常见的困惑来源之一,务必记住"显示是大端、空口是小端"。

二、两大类地址:Public 与 Random

BLE 地址不是只有一种。链路层的广播包和连接请求里,除了 48 位地址值,还有一个 TxAdd / RxAdd 标志位,用来区分这是"公有地址"还是"随机地址"。所以判断一个地址属于哪类,光看地址值不够,必须结合这个标志位。

1. Public Address(公有地址)

  • 由 IEEE 统一分配,结构和以太网 MAC 一样:
  • 高 24 位 = OUI(Organizationally Unique Identifier,厂商唯一标识),需向 IEEE 注册购买。
  • 低 24 位 = 厂商自行分配的设备编号。
  • 全球唯一、终生不变,烧录在芯片里(或产测时写入)。
  • 优点:稳定可追溯;缺点:永久暴露身份,可被用来追踪用户。

2. Random Address(随机地址)

随机地址不需要向 IEEE 花钱申请,靠地址最高两个 bit(MSB)再细分成三种。判断规则看地址最高字节的 bit7、bit6:

最高 2 bit 类型 说明
11 Static Random(静态随机) 上电时确定,一个上电周期内不变
00 Non-Resolvable Private(不可解析私有) 纯随机,不可被反推身份
10 Resolvable Private(可解析私有,RPA) 隐私核心,可被授权方解析

三、三种随机地址详解

Static Random Address(静态随机地址)

  • 最高 2 bit 固定为 11,其余 46 bit 随机(不能全 0 也不能全 1)。
  • 一旦生成,在本次上电周期内保持不变;设备可以选择重启后换一个,也可以固化到 flash 里长期不变。
  • 大量没有 Public 地址的国产 BLE 模组、开发板用的就是它 —— 省掉了买 OUI 的成本。
  • 注意:如果重启就换地址,会导致已配对设备找不到它,所以做产品时通常把它固化下来。

Non-Resolvable Private Address(不可解析私有地址)

  • 最高 2 bit 为 00,其余随机。
  • 定期更换、且任何人都无法把它反推回真实身份(包括曾经配对过的设备)。
  • 适合只想广播、完全不想被关联的场景,实际用得较少。

Resolvable Private Address(RPA,可解析私有地址)—— 隐私的关键

这是理解"手机蓝牙地址一直在变"的核心。

  • 最高 2 bit 为 10
  • 每隔一段时间(默认约 15 分钟,由 GAP 的 TGAP(private_addr_int) 定义)自动更换一次。
  • 对陌生人来说,它就是一串会变的随机数,无法追踪;但对曾经配对/绑定过的设备,可以用一把共享密钥把它"解析"出来,从而认出"这还是原来那台设备"。

RPA 的结构(48 bit 拆成两半):

| prand (24 bit) | hash (24 bit) |
   高 24 位          低 24 位
  • prand:随机数,最高 2 bit 固定 10
  • hash = ah(IRK, prand):用设备的 IRK(Identity Resolving Key,身份解析密钥) 对 prand 做一个加密函数 ah(基于 AES-128)算出来的值。

生成:设备用自己的 IRK + 新随机数 prand 算出 hash,拼成新 RPA 广播出去。

解析:接收方(曾绑定过、手里存有对方 IRK)把收到的 prand 用自己保存的 IRK 重新算一遍 hash,若结果和收到的 hash 一致 → 确认"就是这台设备"。手机里存了多个已配对设备的 IRK,就逐个尝试解析(这就是所谓的 resolving list)。

IRK 是在配对(bonding)阶段作为身份信息交换的。所以只有绑定过的双方才能互相解析对方的 RPA —— 这正是隐私与可用性的平衡点。

四、为什么要搞这么复杂?—— 隐私保护

早期蓝牙用固定 Public 地址,带来严重的隐私追踪问题:商场、地铁只要部署一堆蓝牙扫描器,就能凭固定地址记录"某台设备(某个人)几点出现在哪里"。

RPA 机制让设备对陌生人呈现为不断变化的随机地址,杜绝了被动追踪;同时通过 IRK 让"自己人"仍能认出彼此。这就是为什么:

  • iPhone / Android 手机默认使用 RPA,你每次扫描看到的地址都不同。
  • 但你的蓝牙耳机、手表(已和手机绑定)依然能自动重连 —— 因为手机能解析它们的 RPA。

五、Identity Address(身份地址)

绑定之后,协议栈内部会用一个稳定的 Identity Address(身份地址) 来标识对端,它可以是对方的 Public 地址,或对方的 Static Random 地址。RPA 只是"对外的马甲",Identity Address 才是"内部档案里的真名"。开发时若要长期记住一台设备,应存它的 Identity Address,而不是随时会变的 RPA。

六、实战:怎么查看和判断

手机 App(nRF Connect / LightBlue):扫描列表里每个设备旁通常标注了地址类型(Public / Random)。看到地址频繁变化的,多半是 RPA。

代码里判断类型(伪代码,以最高字节 addr[5](大端显示的第一字节)为例):

// addr[] 按空口小端存储,addr[5] 是显示时的最高字节
uint8_t msb = addr[5];
uint8_t top2 = msb >> 6;   // 取最高 2 bit

if (is_public) {
    // TxAdd/RxAdd 标志位指示为 public
    type = "Public";
} else {
    switch (top2) {
        case 0b11: type = "Static Random"; break;
        case 0b00: type = "Non-Resolvable Private"; break;
        case 0b10: type = "Resolvable Private (RPA)"; break;
        default:   type = "Reserved/Invalid"; break;
    }
}

抓包:nRF Sniffer + Wireshark 里,btle 层能直接解出 Advertising Address 和其类型;注意原始字节是小端。

七、常见坑提醒

  1. 字节序:显示大端、空口小端,抓包对不上时先怀疑这里。
  2. 地址类型要连标志位一起看:不能只凭地址值猜 Public 还是 Random,TxAdd/RxAdd 才是权威。
  3. 别拿 RPA 当设备唯一标识存库:它会变,重连后就对不上了;要存就存 Identity Address。
  4. Static Random 不等于永久不变:规范只保证一个上电周期内不变,重启是否换取决于实现。
  5. RPA 解析依赖 IRK:没配对过就拿不到对方 IRK,也就无法解析 —— 这是设计如此,不是 bug。
  6. 国产模组多用 Static Random:没买 OUI 很正常,做量产要注意把地址固化,避免每次上电都变导致配对丢失。
  7. RPA 更新周期:默认约 15 分钟,规范允许调整;周期太短会增加重连开销,太长则削弱隐私效果。

一句话总结

BD_ADDR 是 BLE 设备的 48 位"身份证",分 Public(IEEE 分配、永久唯一)和 Random(Static / Non-Resolvable / Resolvable)两大类。其中 RPA(可解析私有地址) 用 IRK 在"对陌生人隐身"和"对自己人可认"之间取得平衡,是现代蓝牙隐私保护的基石 —— 这就是你手机蓝牙地址总在变、耳机却仍能自动回连的原因。

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