本文聚焦 3GPP TS 33.501 §6.9《Security handling in mobility》中最容易被忽视、却直接决定前向安全(forward security)是否成立的子机制——NG-RAN 切换时的接入层(AS)密钥再派生。我们将逐条拆解 NH 链、NCC 计数器、水平(Horizontal)与垂直(Vertical)派生,并落到 Xn/N2 两条切换信令路径上的密钥交付。
主认证完成时,UE 与核心网共享的只是 NAS 层密钥 KAMF,而空口上的 RRC 加密/完整性、用户面加密用的是 AS 层密钥 KgNB(也称 KNgRAN)。KgNB 由 KAMF 派生后,下发到 UE 当前驻留的那一个 gNB。
问题随之而来:UE 每发生一次切换,密钥就从源 gNB 流向目标 gNB。如果每次切换都用"同一个 KgNB"或者用源 gNB 能预测的方式生成新密钥,那么一个被攻破的源 gNB 就能推算出此后所有目标 gNB 的密钥——这叫前向安全被打破。
3GPP 的解法是设计一条"单向派生链":每跳切换都产生一个新的 KgNB*,并且通过 NH(Next Hop)链保证已离开的 gNB 无法推算未来的密钥。TS 33.501 §6.9.2.1 把这套机制定义在 AS 安全上下文里,是我们今天的主角。
在进入切换密钥之前,必须先看清整条密钥树的根与枝。所有派生都基于 TS 33.220 Annex B.2 定义的 HMAC-SHA256 KDF:KDF(Key, S) = HMAC-SHA256(Key, FC ‖ P0‖L0 ‖ P1‖L1 ‖ …)。
flowchart TD
K["永久密钥 K<br/>(USIM / UDM)"] --> AKA["主认证 5G AKA / EAP-AKA'"]
AKA --> KAUSF["KAUSF"]
KAUSF --> KSEAF["KSEAF"]
KSEAF --> KAMF["KAMF<br/>(NAS 级, AMF 持有)"]
KAMF -->|"Annex A.6 FC=0x6E<br/>AccessType ‖ SUPI"| KgNB["KgNB / KNgRAN<br/>(NCC = 0, 锚点)"]
KAMF -->|"Annex A.7 FC=0x6F<br/>SYNC"| NH["NH 链 NH₁ .. NHₙ<br/>(NCC = 1 .. n)"]
KgNB -->|水平派生| KH["KgNB*<sub>水平</sub><br/>= KDF(KgNB, PCI, ARFCN-DL, SSB-off)"]
NH -->|垂直派生| KV["KgNB*<sub>垂直</sub><br/>= KDF(NH[n], PCI, ARFCN-DL, SSB-off)"]
KH --> AS["目标 gNB 新 KgNB<br/>→ K<sub>RRCenc/int</sub>, K<sub>UPenc/int</sub>, K<sub>MAC</sub>"]
KV --> AS
要点:KgNB 与 NH 是并列的两条派生支,都以 KAMF 为根。KgNB 自身对应 NCC=0,是水平派生的锚;NH 链对应 NCC=1,2,…,是垂直派生的素材。
NCC(Next hop Chaining Count)是一个计数器,记录 NH 链被推进了多少次。它的真正作用是让 UE 和网络对一个"用过的密钥"达成共识:
KgNB 是初始锚点(来自 KAMF 直派生),尚未使用任何 NH。UE 侧也维护一个 NH 数组与 NCC 指针。当收到切换命令中的 NCC 时,UE 用该 NCC 决定用"水平锚"还是"第 n 个 NH"去派生 KgNB*。UE 与目标 gNB 必须用相同的 NCC,否则 SRB1 的第一条 RRCReconfigurationComplete 就会因完整性校验失败而丢弃。
NH 链是一条由 AMF 维护的单向哈希链(TS 33.501 §6.9.2.1.1):
KAMF 作为 KDF 密钥、用一个同步输入派生;KAMF;NH[n] 也无法反推 NH[n-1],更无法前推 NH[n+1](因为推进需要 KAMF,而 KAMF 从不下发给 gNB)。flowchart LR
KAMF["KAMF (仅 AMF/UE 知晓)"] --> D0["派生 KgNB, NCC=0"]
KAMF --> D1["NH₁, NCC=1"]
D1 --> D2["NH₂, NCC=2"]
D2 --> D3["NH₃, NCC=3"]
D3 --> Dn["NHₙ, NCC=n ..."]
style KAMF fill:#ffe4e1
注意一个关键的"投放窗口":AMF 不会一次性把整条 NH 链发给 gNB。它只在初始上下文建立和每次 N2 切换 / Path Switch 成功时,把"下一个可用的 NH + 它的 NCC"投递给当前服务 gNB。这意味着 gNB 手里的 NH 是"配额制"的——这正是下一节前向安全漏洞的来源。
TS 33.501 §6.9.2.3.2 规定了源 gNB 在 Xn 切换时的派生方式选择规则:
若源 gNB 当前 NCC 已等于其持有的"最高可用 NH 的 NCC"(即没有更新的 NH 在手),执行水平派生;否则执行垂直派生。
直观理解:手里有没用过的 NH 就用 NH(垂直),没有就只能在自己 KgNB 上接着磨(水平)。
flowchart TD
S(["源 gNB 决定切换"]) --> Q{"当前 NCC < 已持有 NH 的 NCC?<br/>(还有没用的 NH)"}
Q -->|是| V["垂直派生 VERTICAL"]
Q -->|否| H["水平派生 HORIZONTAL"]
V --> V1["KgNB* = KDF(NH[n], PCI, ARFCN-DL, SSB-off)"]
V1 --> V2["NCC 置为 n<br/>提供完整前向安全"]
H --> H1["KgNB* = KDF(KgNB, PCI, ARFCN-DL, SSB-off)"]
H1 --> H2["NCC 不变<br/>仅一跳前向安全"]
V2 --> F(["派生目标 AS 算法密钥"])
H2 --> F
KgNB* 的派生函数(对应 LTE TS 33.401 的 KeNB* 派生)输入参数如下表。请注意 5G NR 相较 LTE 新增了第三个输入 P2——SSB 子载波偏移,这是 NR 灵活参数集下的必要扩展:在 NR 中,PCI + 下行频点在某些部署下不足以唯一锚定一个 SSB,必须再绑定 SSB 相对 Point A 的子载波偏移,否则会出现"两小区复用同一密钥"的歧义。
| 输入参数 | 含义 | 长度 | 来源 / 说明 |
|---|---|---|---|
| KEY(密钥输入) | 派生用的根密钥 | 256 bit | 水平=KgNB;垂直=NH[n] |
| FC | 函数码(区分用途) | 1 octet | 见 TS 33.501 Annex A |
| P0 | 目标小区 PCI | 2 octet | Physical Cell ID,绑定目标物理小区 |
| P1 | 目标下行载频 ARFCN-DL | 2 octet | 绑定目标频点 |
| P2 | SSB subcarrier offset | — | 5G NR 新增,绑定 SSB 频率位置 |
对照:LTE(TS 33.401)
KeNB*派生只有 PCI + EARFCN-DL 两个输入,FC=0x19;5G 增补 P2,并把体系搬到 TS 33.501 Annex A 与 §6.9.2。KAMF→KgNB(FC=0x6E)和NH派生(FC=0x6F)是另外两个独立 KDF。
| 维度 | 水平派生 Horizontal | 垂直派生 Vertical |
|---|---|---|
| 密钥输入 | 当前 KgNB |
NH[n] |
| 前向安全 | ❌ 仅"一跳"——源 gNB 仍可推算下一跳 | ✅ 完整——源 gNB 不知未来 NH |
| 谁负责选择 | 源 gNB(Xn)/ AMF(N2) | 同左 |
| NCC 变化 | 不变 | +1 |
| 触发条件 | 手里没有更新 NH | 手里有未用 NH |
| 被攻破风险 | 源 gNB 被攻破→后续链失守 | 即使源 gNB 被攻破也安全 |
Xn 切换是 gNB 间直接完成、不经过 AMF 参与密钥派生决策的路径,因此 NH 的"配额"问题在这里最为突出。
sequenceDiagram
participant UE
participant SgNB as 源gNB (持 KgNB, NCC=n)
participant TgNB as 目标gNB
participant AMF
Note over UE,AMF: 初始: AMF 已下发 KgNB(NCC=0) + 若干 NH(NCC=1..k)
SgNB->>SgNB: 判定 H/V: 是否还有未用 NH?
SgNB->>TgNB: XnAP Handover Request (含 KgNB*, NCC)
Note over SgNB,TgNB: 水平: KgNB*=KDF(KgNB,PCI,ARFCN,SSBoff)<br/>垂直: KgNB*=KDF(NH[n],PCI,ARFCN,SSBoff)
TgNB->>SgNB: XnAP Handover Request Acknowledge
SgNB->>UE: RRCReconfiguration (含 NCC, 目标 PCI/SSB-offset)
UE->>UE: 按 NCC 派生同一 KgNB*
UE->>TgNB: RRCReconfigurationComplete (SRB1 受新密钥完整性保护)
TgNB->>AMF: NGAP Path Switch Request (上报当前 NCC)
AMF->>AMF: 若 NCC 接近耗尽, 推进 NH 链, 生成新 NH
AMF->>TgNB: Path Switch Request Acknowledge (含下一可用 NH, NCC+1)
关键信令单元逐条解析:
KgNB* 和对应 NCC 直接传给目标 gNB。注意:这条消息里传的是已经派生完成的密钥,而不是 NH——因为 AMF 不在场,源 gNB 不能把 KAMF 或 NH 链递交给目标。KgNB*。N2 切换由 AMF 居中转发,密钥派生决策回到 AMF 手中。AMF 持有 KAMF 和整条 NH 链,因此N2 切换强制走垂直派生,前向安全由 AMF 的信任级别兜底。
sequenceDiagram
participant UE
participant SgNB as 源gNB
participant AMF
participant TgNB as 目标gNB
SgNB->>AMF: NGAP Handover Required (含 NCC_current)
AMF->>AMF: 推进 NH 链取下一 NH (NCC+1)<br/>KgNB*=KDF(NH, PCI, ARFCN-DL, SSB-off) [垂直]
AMF->>TgNB: NGAP Handover Request (含 KgNB*, NCC)
TgNB->>AMF: NGAP Handover Request Acknowledge
AMF->>SgNB: NGAP Handover Command (含给 UE 的 NCC)
SgNB->>UE: RRCReconfiguration (含 NCC)
UE->>TgNB: 完成切换, 派生同一 KgNB*
差异要点:N2 路径里源 gNB 永远不知道目标 gNB 的 KgNB*,这是它与 Xn 路径的根本安全差别。代价是多一次 AMF 往返时延,所以高速移动/密集小区场景仍优先 Xn。
Linköping 大学与 ETH Zürich 的形式化分析(WISEC'21、SECRYPT'23)揭示了一个标准本身允许、但实现须警惕的窗口:当 gNB 手里的 NH 配额耗尽、又来不及通过 Path Switch 补货时,连续的 Xn 切换会强制退化为水平派生链。此时若某一跳的源 gNB 被攻破(如伪基站/被入侵的 gNB),攻击者即可推算该跳之后所有水平派生的 KgNB*,前向安全在链上彻底失效。
flowchart TD
A["正常: 垂直派生链<br/>NCC=1,2,3..."] --> B{"NH 配额耗尽?"}
B -->|否| A
B -->|是, PathSwitch 未及时补货| C["退化为水平链<br/>KgNB → KgNB* → KgNB** ..."]
C --> D["某跳源 gNB 被攻破"]
D --> E["攻击者推算后续所有 KgNB*<br/>前向安全失效 ❌"]
E --> F["缓解: AMF 主动预投多 NH<br/>或缩短 PathSwitch 补货周期"]
工程缓解:让 AMF 在初始上下文建立和每次 Path Switch 时一次预投多个 NH(而非单个),把垂直派生的"续航里程"拉长;或在 NG-RAN 配置上限制连续 Xn 跳数,超出则强制走 N2。
在 NGAP/XnAP/5G-RRC 抓包中,关键识别点:
Security Key(即 KgNB,NCC=0),标志着 NH 链起点。New Security Key Indication / 下一个 NH,是补货证据。UESecurityCapabilities 与安全上下文,但密钥本身经 NDS/IP(TLS)保护,明文抓包看不到 KgNB* 字节,只能靠 NCC 字段与流程时序推断。reconfigurationWithSync 中的目标 PCI、ARFCN、SSB-offset;NCC 通常体现在 AS 安全上下文相关字段。| 现象 | 可能根因 | 排错方向 |
|---|---|---|
| UE 切换后 SRB1 第一条消息完整性失败、立即触发 RRC 重建 | UE 与目标 gNB NCC 不一致,或 SSB-offset 输入不匹配 | 核对切换命令里的 NCC/SSB-offset;检查 gNB 派生实现是否遗漏 P2 |
| 频繁水平派生、安全审计告警 | AMF 投递 NH 配额不足、Path Switch 过频失败 | 检查 AMF NH 链推进逻辑与预投策略 |
| Xn 切换成功但 N2 切换失败 | AMF 推进 NH 链时 NCC 计算与 UE 不同步 | 比对 AMF/UE 维护的 NCC 指针 |
| 跨 SA/NSA、NR↔E-UTRA 切换后密钥错乱 | KeNB↔KgNB 桥接(N26/inter-RAT)的密钥映射函数用错 | 检查 TS 33.501 §6.9.3 inter-RAT 密钥派生 |
Q1:水平派生和垂直派生到底差在哪?
A:差在"派生用谁的密钥"。水平用源 gNB 自己的 KgNB 继续 KDF,源 gNB 能预测结果,所以被攻破后危及下一跳;垂直用 AMF 投递的 NH,源 gNB 不知道未来 NH,被攻破也无害。安全强度上垂直 ≫ 水平。
Q2:NCC=0 意味着什么?为什么初始就是 0?
A:NCC=0 表示当前 AS 密钥是 KAMF 直接派生的初始 KgNB,尚未消耗任何 NH。把它定为 0 是为了与"水平锚点"语义统一——只要 NCC 没动,就说明走的是水平路径。
Q3:Xn 切换为什么会出现前向安全被打破?标准不是已经设计了 NH 链吗? A:标准设计了 NH 链,但没有强制 gNB 在每跳都用垂直派生。当 gNB 手里 NH 配额耗尽、Path Switch 又没及时补货时,§6.9.2.3.2 允许它退化为水平派生以维持切换可用性。这是"可用性优先于安全"的工程权衡,也正是形式化分析批评的焦点。
Q4:UE 怎么知道该用第几个 NH?
A:靠切换命令(RRCReconfiguration)里携带的 NCC。UE 本地维护 NH 数组,收到 NCC 后取对应 NH(若 NCC 未变则用水平锚),再配合目标 PCI/ARFCN/SSB-offset 派生出与目标 gNB 一致的 KgNB*。
Q5:N2 切换会退化成水平派生吗?
A:正常情况下不会。N2 切换由 AMF 居中,AMF 总能用自己掌握的 KAMF 推进 NH 链、取下一 NH 做垂直派生并把 KgNB* 下发目标。除非 AMF 实现有缺陷,否则 N2 路径天然具备完整前向安全,这也是安全敏感场景倾向 N2 的原因。
本文由「51学通信」技术团队出品。51学通信由通信行业18年老兵爱卫生(微信ID: gprshome201101)维护,聚焦5G/6G核心网、3GPP标准解读、通信前沿技术深度分析。 出版作品有《5G核心网原理与实践》《IMS原理与实践》等4本通信专业书籍。知识星球「51学通信」提供深度技术答疑和行业洞察。
[!tip] 51学通信知识星球 星主:爱卫生(微信:gprshome201101)——公众号「51学通信」创办者,19 年通信行业运维与讲师经验,著有《GPRS网络信令实例详解》《EPC原理与实践》《5G核心网原理与实践》三本纸质书(各大电商平台有售)。 星球定位:深耕 5G 核心网、IMS、NFV/AI、云原生等 ICT 领域,主打运维岗位与信令分析。 当前规模:5200 篇主题 / 990 个回答,198 元/年。 加入福利:加入星球后请加爱卫生微信,拉您进知识星球年度陪伴与答疑群——不限量回复您的各种问题,另有群内分享。 AI 加持:51学通信知识星球已作为 skill 接入 WorkBuddy,使用 deepseek-4-flash 在海量星球帖子中精准找贴、找视频、找 FAQ 和案例,做总结、找文档和 PPT 等场景全覆盖。
