首页
社区
课程
招聘
[原创] Android PIN 解锁全链路:Gatekeeper · TEE · KeyMint 与 FBE 密钥解封
发表于: 2026-10-9 11:38 1470

[原创] Android PIN 解锁全链路:Gatekeeper · TEE · KeyMint 与 FBE 密钥解封

2026-10-9 11:38
1470

Android PIN 解锁全链路:Gatekeeper · TEE · KeyMint 与 FBE 密钥解封

一句话:你按下的 6 位 PIN,从来没被"存起来",也从来没被"解密出来比对过"——它只留下一个带硬件根密钥的指纹;真正解密 /data 的密钥,是认证通过后由 TEE 的签名凭证换来的。

适合谁读:想搞清楚"输一次 PIN → 用户数据可访问"中间到底发生了什么的人(系统/安全/驱动/测试都适用)。
怎么读:先看 §1 的 图① 总览建立地图,再按 §2 → §3 → §4 读 图② 注册 / 图③ 校验 / 图④ 密钥解封;§5–§6 讲"东西存在哪"和"防护边界在哪",§7 是一页速查,§8 是附录。
适用版本:Android 9+(FBE + synthetic password;14/15 同构)。文中路径/文件名属 AOSP 实现细节,已标注置信度。


0. 术语速查

术语 含义
REE Rich Execution Environment,普通 Android 系统侧(含内核)
TEE Trusted Execution Environment,可信安全世界
HUK Hardware Unique Key,每芯片唯一密钥,熔在 OTP 里、不出硬件
LSKF Lock Screen Knowledge Factor,锁屏知识因子(PIN / 图案 / 密码)
verifier / handle gatekeeper password handle = {version, salt, H},其中 H = KDF(HUK, salt, PIN)
AuthToken TEE 签名的认证凭证,用来向 KeyMint 证明"该用户已通过认证"
SP Synthetic Password,随机主密钥;被凭据派生出的密钥加密后存盘
CE / DE Credential Encrypted / Device Encrypted 存储(CE 在解锁后才可访问)
RPMB Replay Protected Memory Block,防回滚存储(本项目里放节流状态)

1. 舞台与角色

组件 所在侧 职责
锁屏 UI REE 收 PIN、显示失败与倒计时
LockSettingsService REE 编排校验流程、管理 SP 凭据
gatekeeperd / Gatekeeper HAL REE 把 enroll / verify 请求转进 TEE
Gatekeeper TA TEE 唯一持有比对逻辑,读写 RPMB 节流状态
KeyMint TA TEE 校验 AuthToken,授权派生 / 释放 CE 密钥
RPMB 硬件 错误计数、冷却状态、per-user secure_user_id
OTP HUK 硬件 KDF 的硬件根密钥,只在硬件内部参与运算
硬件加密引擎 SoC 片内 用 CE 密钥解密 /data

图① 总览:一次 PIN 解锁走的四段路(后文 §2–§4 分别展开)

sequenceDiagram
    actor User as 用户
    participant UI as 锁屏UI【REE】
    participant LSS as LockSettingsService【REE】
    participant HAL as Gatekeeper HAL / gatekeeperd【REE】
    participant TA as Gatekeeper TA【TEE】
    participant KM as KeyMint TA【TEE】
    participant HW as RPMB + 凭据存储

    Note over User,HW: 图① 总览:注册(§2) → 校验(§3) → 换密钥(§4)
    User->>UI: ①-1 输入PIN
    UI->>LSS: ①-2 提交校验
    LSS->>HAL: ①-3 verify(challenge, handle, PIN)
    HAL->>TA: ①-4 切入安全世界
    TA->>HW: ①-5 读节流状态 + 取 verifier(handle)
    TA->>TA: ①-6 重算 H′ 并与 H 比对
    alt 比对失败
        TA-->>UI: ①-7 失败 + 冷却时长(更新 RPMB 计数)
    else 比对成功
        TA->>KM: ①-8 出示 AuthToken(TEE 签名)
        KM-->>LSS: ①-9 授权派生 / 释放 CE 密钥
        LSS-->>UI: ①-10 挂载 CE 目录,解锁完成
    end

2. 凭据注册(Enroll):PIN 只留下"指纹"

核心结论:系统从不保存 PIN——既不存明文,也不存"能解出 PIN 的密文"。注册时 TEE 只吐出一个 verifier:

H = KDF(HUK, salt, PIN)        # salt 随机,HUK 在熔丝里
handle = {version, salt, H}     # 这个 handle 才落盘

PIN 通常只有 4–6 位(熵极低),所以任何可逆保存的方案都等于泄露 PIN;把 HUK 放进熔丝、只保存不可逆的 H,才能保证"拿到落盘数据也无法离线爆破"。

图② Enroll:落盘的只有 verifier 与加密后的 SP

sequenceDiagram
    actor User as 用户
    participant LSS as LockSettingsService【REE】
    participant HAL as Gatekeeper HAL【REE】
    participant TA as Gatekeeper TA【TEE】
    participant OTP as OTP熔丝(HUK)
    participant RPMB as RPMB安全存储
    participant SP as SP凭据存储【REE /data,DE】
    participant KM as KeyMint TA【TEE】

    Note over User,KM: 图② Enroll:只有 verifier 与加密后的 SP 落盘,PIN 明文不落盘
    User->>LSS: ②-1 设置PIN(首次或修改)
    LSS->>HAL: ②-2 enroll(PIN)
    HAL->>TA: ②-3 切入安全世界
    TA->>OTP: ②-4 生成随机 salt,计算 H = KDF(HUK, salt, PIN)
    OTP-->>TA: ②-5 返回 H(摘要不出安全边界)
    TA->>RPMB: ②-6 写入 per-user secure_user_id(供节流绑定)
    TA-->>LSS: ②-7 返回 password handle = {version, salt, H}
    LSS->>SP: ②-8 写入 spblob/*.pwd(含 handle 与 escrow)
    LSS->>KM: ②-9 请求 auth-bound 密钥(用于包装 SP)
    KM-->>LSS: ②-10 返回包装密钥
    LSS->>SP: ②-11 写入 spblob/*.spblob(加密后的 SP)

3. PIN 校验(Verify):重算 verifier 再比对

最容易搞错的一点:校验不是"用 PIN 派生密钥去解密某段密文,再拿解密结果和原始 PIN 对比"。既没有可比对的明文,也没有可解的 PIN 密文。

真实过程只有两步(都在 TEE 内):

  1. 用 handle 里的 salt + 候选 PIN,重算 H′ = KDF(HUK, salt, PIN′);
  2. 与 handle 里的 H 做常数时间比对——这是 verifier ↔ verifier,不是明文比对。

而且节流是前置的:先把错误计数 +1 落盘到 RPMB,再开始比对——这样即使攻击者在比对前杀掉进程,也无法绕过计数。

图③ Verify:节流前置 + 重算比对(失败与冷却都在这张图里)

sequenceDiagram
    actor User as 用户
    participant UI as 锁屏UI【REE】
    participant LSS as LockSettingsService【REE】
    participant HAL as Gatekeeper HAL【REE】
    participant TEE_Drv as TEE驱动【REE内核】
    participant TA as Gatekeeper TA【TEE】
    participant RPMB as RPMB安全存储
    participant OTP as OTP熔丝(HUK)

    Note over User,OTP: 图③ Verify:先节流、再比对
    User->>UI: ③-1 输入PIN码
    UI->>LSS: ③-2 提交PIN校验请求
    LSS->>HAL: ③-3 调用Gatekeeper HAL verify接口
    HAL->>TEE_Drv: ③-4 发送PIN + 校验请求到TEE
    TEE_Drv->>TA: ③-5 切换到安全世界,传入PIN
    TA->>RPMB: ③-6 读取连续错误计数 & 冷却状态
    RPMB-->>TA: ③-7 返回错误次数、剩余锁定时间

    alt 处于冷却锁定期(③-8 – ③-12)
        TA-->>TEE_Drv: ③-8 返回失败+等待时长
        TEE_Drv-->>HAL: ③-9 透传结果
        HAL-->>LSS: ③-10 返回校验失败
        LSS-->>UI: ③-11 通知锁屏展示倒计时
        UI-->>User: ③-12 提示等待,禁止重试
    else 未锁定,可尝试校验(③-13 – ③-16)
        TA->>RPMB: ③-13 错误计数 +=1,写入RPMB(提前落盘防绕过)
        RPMB-->>TA: ③-14 写入完成确认
        TA->>OTP: ③-15 用 HUK+salt 对候选PIN重算KDF,与存储的 verifier 比对
        Note over OTP: HUK不读出总线,仅在硬件内部参与计算
        OTP-->>TA: ③-16 返回比对结果(成功/失败,摘要不出安全边界)

        alt PIN校验失败(③-17 – ③-22)
            TA->>RPMB: ③-17 更新RPMB,保存错误计数、更新冷却时间
            TA-->>TEE_Drv: ③-18 返回校验失败 + 冷却时长
            TEE_Drv-->>HAL: ③-19 透传结果
            HAL-->>LSS: ③-20 返回失败
            LSS-->>UI: ③-21 密码错误
            UI-->>User: ③-22 提示PIN错误
        else PIN校验成功
            Note over TA,OTP: 认证通过 → 进入 图④(凭据换密钥)
        end
    end

4. 从"认证通过"到"拿到密钥":AuthToken → KeyMint → CE

比对成功不等于数据能解密了。中间还要过一次"授权":

  1. TEE 用只有它能用的密钥签一个 AuthToken(含 user SID、时间戳、以及由 KeyMint 侧下发的 challenge);
  2. REE 把它交给 KeyMint / keystore2,验签 + 校验 challenge 与新鲜度 → 授权 auth-bound key 操作;
  3. KeyMint 据此派生 / 释放 CE 密钥(FBE 下还要解开 SP 的包裹),并把密钥载入硬件加密引擎;
  4. 加密引擎用它解密 /data,CE 目录随即挂载。

注意:这里的"解密"解的是用户数据与密钥包裹(SP blob),与 PIN 没有任何比对关系——两份职责在图里是彻底分开的。

图④ 凭据换密钥:AuthToken → KeyMint → CE 密钥 → 硬件引擎

sequenceDiagram
    participant TA as Gatekeeper TA【TEE】
    participant RPMB as RPMB安全存储
    participant TEE_Drv as TEE驱动【REE内核】
    participant HAL as Gatekeeper HAL【REE】
    participant LSS as LockSettingsService【REE】
    participant KM as KeyMint TA【TEE】
    participant OTP as OTP熔丝(HUK)
    participant CRYPTO as 硬件加密引擎【SoC片内】
    participant UI as 锁屏UI【REE】
    actor User as 用户

    Note over TA,User: 图④ 凭据到密钥:AuthToken 换 CE 密钥,密钥不落地 REE
    TA->>RPMB: ④-1 清空错误计数器
    TA->>TA: ④-2 生成带TEE签名的AuthToken + User SID
    TA-->>TEE_Drv: ④-3 返回成功 + AuthToken
    TEE_Drv-->>HAL: ④-4 透传成功结果+AuthToken
    HAL-->>LSS: ④-5 返回认证成功、AuthToken
    Note over TA,KM: 简化示意:AuthToken 实际经 REE 交给 KeyMint/keystore2 验签
    TA->>KM: ④-6 传入AuthToken(含challenge),请求派生/释放CE密钥
    KM->>OTP: ④-7 硬件HKDF(HUK+SID+PIN材料)
    OTP-->>CRYPTO: ④-8 将CE密钥载入硬件加密引擎(此后解密用户数据)
    Note over CRYPTO: CE密钥不会导出到REE内存
    LSS->>UI: ④-9 解锁指令
    UI-->>User: ④-10 解锁成功,进入系统
    Note over LSS: 系统挂载CE加密目录,用户数据可访问

5. 存储:verifier 到底放在哪

一句话:verifier 不存在 TEE 里,而是由 REE(Android 侧)保存在 /data 分区上,作为 synthetic password(SP)体系的一部分;TEE 只在 RPMB 里保存节流 / 防回滚状态与 secure_user_id。

5.1 三处各存什么

位置 存的是 是否含 PIN 相关信息
REE /data(DE 存储) gatekeeper password handle = {version, salt, signature(H)},其中 H = KDF(HUK, salt, PIN) ✅ verifier 本体在此
TEE 侧 RPMB 连续错误计数、冷却/锁定状态、per-user secure_user_id ❌ 只有节流状态,没有 H
OTP 熔丝 HUK(每芯片唯一密钥) ❌ 永不出硬件

即:verifier 是"REE 持有、每次 verify 时传入 TEE",TEE 不持久化它 —— 对应 图③-4 / ③-5(候选 PIN + handle 一起送进 TEE)与 图③-6、③-13、③-17、图④-1(只在 RPMB 读写节流状态)。

5.2 具体落在哪个文件(置信度标注)

场景 路径 置信度
Android 9+ 带 FBE + SP(含 14/15) /data/system_de/<user_id>/spblob/ 目录下:
· *.pwd → 序列化的 PasswordData,内含 gatekeeper handle(salt + H)
· *.spblob → 被 LSKF 派生密钥加密的 synthetic password
· 文件名 = handle 的哈希(Long.toHexString(...))
架构:高
文件名编码:中
早期 FBE(Android 7–8,无 SP) /data/system/gatekeeper/(如 password.key)、/data/misc/gatekeeper/ 中(版本相关)

数据流

  • Enroll(图②-7 → ②-8):TA 返回 handle → framework(SyntheticPasswordManager)把 handle 连同 escrow / 加密后的 SP 写进 spblob/*.pwd;
  • Verify(图③-15):framework 从 spblob/ 取出 handle(protector 数量很少)→ gatekeeper.verify(challenge, handle, PIN) → TA 用 handle 里的 salt 重算 H′,与 handle 里的 H 在 TEE 内比对。

5.3 为什么放在 /data(DE)上也安全

/data/system_de(DE = Device Encrypted)在解锁前就可读,看上去危险,但:

  • 里面只有 salt + H,H 是带 HUK 的不可逆 KDF 输出;
  • HUK 在熔丝内、只能由硬件内部参与运算(对应 图③-15 那条 Note)⇒ 拿到 handle 也无法离线爆破 PIN;
  • RPMB 的节流状态按设备计 ⇒ 攻击者拿不到"免费的尝试次数"。

⇒ 这正是"存 verifier、不存 PIN 密文"的设计动机:若把 PIN 加密保存,"解密出 PIN 再比对"反而会让 /data 上的密文成为离线爆破的目标。


6. 威胁模型与防护边界(HUK 的定位)

H = KDF(HUK, salt, PIN) 的安全性只依赖一个硬假设:HUK 保密。
若攻击者同时拿到 handle(在 /data,可读)与 HUK,即可在外部对候选 PIN 重算 KDF 并比对,完全绕过 RPMB 的节流 / 错误计数——因为那套逻辑只在"验证请求必须经过 TEE"时成立,离线爆破不走 TEE。

攻击者能力 能否拿到 HUK 能否离线爆破 PIN
恶意 App / Root / 篡改系统镜像 ✘ ✘(在线猜,受 RPMB 节流限制)
拆机 + 离线读写存储 / RPMB 重放 ✘(HUK 在 SoC 内) ✘
物理侵入 SoC(去封装 + FIB/微探针读熔丝,或探硬件 KDF 中间值) ✔(实验室级) ✔(可离线穷举)
侧信道 / 故障注入(功耗、EM、电压/时钟毛刺) 取决于实现 可能绕过比对或提取中间值

要点:

  1. HUK 每芯片唯一 ⇒ 读出只攻破该台设备,无法横向扩散(不是"解一个破一万");
  2. 物理侵入是单体、昂贵、不可批量的攻击,与"装个 App 就能爆破"不是一个量级;
  3. 熔丝读出保护 / 调试口锁定能抬高中断门槛,但各厂实现差异大,不宜当作数学保证——安全设计通常假设物理侵入者最终能读到;
  4. 真正决定"HUK 泄露后还能否爆破"的是口令熵:4–6 位 PIN 只有 10⁴–10⁶ 种,HUK 泄露后必然秒破;故高价值场景需强口令,或把验证搬入 StrongBox / 独立安全芯片(SE),并叠加抗侧信道 / 抗故障注入与失败熔断设计。

⇒ 本机制防的是"软件与离线攻击";物理侵入(读 HUK)明确不在其防护边界内,需由安全芯片(StrongBox/SE)与防篡改设计覆盖。即 HUK 的作用不是"不可破",而是把攻击成本从"软件可及"抬到"必须物理侵入单颗芯片"。


7. 安全要点速查

# 要点 为什么
1 不存 PIN,只存 H = KDF(HUK, salt, PIN) PIN 熵极低,任何可逆保存都等于泄露
2 校验 = 重算 verifier 再比对,不是"解密后比对明文" 既没有 PIN 密文,也没有比对对象
3 错误计数先 +1 落 RPMB,再开始比对 比对前杀进程也绕不过计数
4 HUK 全程不出硬件,只作 KDF 内部输入 落盘数据无法离线爆破
5 AuthToken 由 TEE 签名(REE 无法伪造),且含 KeyMint 下发的 challenge 认证绑定到具体密钥请求,防重放
6 CE 密钥载入硬件加密引擎,不导出到 REE 内存 主系统拿不到明文密钥
7 节流状态放 RPMB(按设备计) 攻击者没有"免费尝试次数"
8 解密用户数据走 AuthToken → KeyMint → CE 密钥另一条链 与"PIN 对不对"职责分离,互不耦合

8. 附录:真机核实命令(需 root)

adb shell su -c 'ls -l /data/system_de/0/spblob/'
adb shell su -c 'ls -l /data/system/gatekeeper/ /data/misc/gatekeeper/ 2>/dev/null'
adb shell su -c 'xxd /data/system_de/0/spblob/*.pwd | head'   # 仅示意二进制结构

结语:整条链路的设计意图可以用一句话概括——"PIN 只留指纹,密钥另走一条路"。
把"判断 PIN 对不对"(TEE 内的哈希比对)与"拿到能解密数据的密钥"(AuthToken → KeyMint → CE 密钥)彻底分开,既让低熵的 PIN 不至于变成数据安全的单点,也让数据密钥永远不必明文出现在 Android 主系统里。而这条链的安全边界,最终由 HUK 的物理保密与安全芯片共同决定。


冰与火的战歌:Windows内核攻防实战高级班!从零到实战,融合AI与Windows内核攻防全技术栈,打造具备自动化能力的内核开发高手。

最后于 2026-10-9 11:42 被inquisiter编辑 ,原因:
收藏
点赞 ・6
打赏
分享
最新回复 (4)
雪    币: 875
能力值: ( LV1,RANK:0 )
在线值:
发帖
回帖
粉丝
2 楼
那这个是什么原理?

【手机忘记锁屏密码,不清除数据解锁方法,2026实测-哔哩哔哩】 f33K9s2c8@1M7s2y4Q4x3@1q4Q4x3V1k6Q4x3V1k6T1x3U0y4Q4x3X3g2@1N6W2)9J5c8U0t1$3x3r3q4*7L8$3@1`.
2026-10-9 23:14
0
雪    币: 1909
活跃值: (1676)
能力值: ( LV10,RANK:160 )
在线值:
发帖
回帖
粉丝
3 楼
啊你好哇123 那这个是什么原理? 【手机忘记锁屏密码,不清除数据解锁方法,2026实测-哔哩哔哩】 841K9s2c8@1M7s2y4Q4x3@1q4Q4x3V1k6Q4x3V1k6T1x3U0y4Q4x3X3g2@1N6W2)9J5c8U0t1$3x3r3q4*7L8$3@1`.
你可以试一下,大概率读不出来
2026-10-10 10:35
0
雪    币: 875
能力值: ( LV1,RANK:0 )
在线值:
发帖
回帖
粉丝
4 楼
inquisiter 你可以试一下,大概率读不出来
那他这个是咋做到的
2026-10-10 22:53
0
雪    币: 1909
活跃值: (1676)
能力值: ( LV10,RANK:160 )
在线值:
发帖
回帖
粉丝
5 楼
啊你好哇123 那他这个是咋做到的
可能是特殊机型的专用后门,这个留后面的方法很多,不一定是啥
2026-10-12 09:35
0
游客
登录 | 注册 方可回帖
返回