在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

一、前置思考

1.1 为什么敏感计算必须"换个世界"

指纹支付、人脸解锁、DRM 内容保护——这些功能有一个共同点:它们必须在"绝对安全"的环境里执行。如果指纹模板存在普通文件系统里,root 后的设备可以直接读出;如果支付密码在普通内存里跑,调试器可以截获。

我们先梳理一个现实:普通操作系统(REE,Rich Execution Environment)本质上是一个"充满恶意软件的世界"。运行在这个世界的任何应用,理论上都可能被 root 提权、被注入 Hook、被内存 dump。在这个世界里做"安全",永远是攻防不对称的——防守方要防住所有路径,攻击方只要找到一条路径。

TEE(Trusted Execution Environment)的答案很朴素也很彻底:既然普通世界不安全,那就再建一个物理隔离的"可信世界"。敏感运算在这个可信世界里执行,普通世界的软件——包括 root 后的攻击者——无法访问可信世界的内存和密钥。

1.2 双世界的边界

TEE 与 REE 的边界由硬件(ARM TrustZone 或同等的安全扩展)保证,不是软件约定。这意味着:

  • 即使 REE 被完全攻破(拿到了 root),TEE 的内存依然不可读
  • 即使 REE 崩溃重启,TEE 中的密钥、会话状态依然完好
  • REE 无法打断 TEE 的执行,TEE 可以调度 REE

1.3 本文结构

本文从五个方面深入:REE/TEE 双世界隔离的硬件机制、安全存储的实现、可信 UI 的防钓鱼原理、安全时钟的价值、以及开发者最常用的 HUKS 密钥管理接口。最后给出实战 Demo 与避坑清单。

二、核心原理

2.1 REE / TEE 双世界隔离

┌─────────────────────────────────────────────┐
│         REE 普通世界 (Rich OS)               │
│  HarmonyOS / 应用进程 / 普通内存              │
│  安全性:受信任但可被攻破                     │
├─────────────────────────────────────────────┤
│         TEE 可信世界 (Trusted OS)            │
│  安全内核 · 安全存储 · 可信UI · 安全时钟       │
│  安全性:物理隔离 + 独立内存 + 独立外设         │
└─────────────────────────────────────────────┘

隔离机制:

  • 内存隔离:TEE 内存区域对 REE 不可见(ARM TrustZone / 安全内存控制器)。TrustZone 把物理内存分成"安全区"和"普通区",安全区的访问由硬件总线级控制——REE 的 CPU 核处于非安全(NS)状态时,物理上无法发出访问安全区的请求。
  • 外设隔离:安全外设(指纹传感器、安全显示通道)只能被 TEE 访问。指纹传感器直接连接 TEE 的安全外设总线,指纹数据从传感器出来就直接进入安全世界,普通世界的驱动根本碰不到。
  • 中断隔离:安全世界中断独立处理,REE 无法打断。安全世界运行期间,REE 的中断被硬件屏蔽,保证敏感计算的原子性。

安全世界切换(SMC):REE 与 TEE 之间通过安全监控调用(Secure Monitor Call)切换 CPU 状态。切换过程由固件(ATF,ARM Trusted Firmware)控制,REE 不能直接进入安全世界——只能通过定义的调用接口(TA 调用),且每个调用都要经过参数校验。

2.2 TEE 内的软件层次

TEE 不是"一块安全内存"这么简单,它内部有一套完整的软件栈:

┌─────────────────────────────────────────────┐
│           可信应用 (Trusted App, TA)          │
│  指纹比对TA · 支付TA · DRM TA · 密钥管理TA     │
├─────────────────────────────────────────────┤
│           TEE 内部 API (TEE Internal API)    │
│  安全存储 · 加密原语 · 安全计时 · 秘钥槽位      │
├─────────────────────────────────────────────┤
│           Trusted OS 内核 (安全内核)           │
│  进程/内存管理 · 中断处理 · 隔离执行环境         │
├─────────────────────────────────────────────┤
│           Secure Firmware (ATF/安全固件)      │
│  安全世界切换 · 启动信任根验证 · 异常处理        │
└─────────────────────────────────────────────┘

关键点:TA 之间也相互隔离。一个 TA 的漏洞不能直接读取另一个 TA 的数据,TA 通过定义良好的接口通信,类似于普通世界的进程隔离——只不过这次隔离发生在安全世界里。

2.3 安全存储(Secure Storage)

安全存储是 TEE 的"保险柜":

数据类别 存储位置 保护级别
指纹模板 TEE Secure Storage 加密存储,不可导出
支付密钥 Secure Element (SE) 硬件级保护
数字证书 TEE Secure Storage 加密存储
DRM 密钥 TEE Secure Storage 与硬件绑定
设备根密钥 Secure Element 出厂烧录,永不导出

安全存储的实现要点:

(1)加密与绑定

安全存储的数据以加密形式落盘(闪存上保存的是密文),解密密钥由 TEE 内部派生,且与设备唯一标识(如 SoC 的 UID、安全芯片 ID)绑定。这意味着:把存储密文拷贝到另一台设备,无法解密——数据是"粘在设备上"的。

(2)防重放攻击(Anti-Replay)

攻击者可能尝试把存储回滚到旧版本(比如恢复一个"已解锁"状态的旧数据)。安全存储通过版本号 + 消息认证码(MAC)机制防重放:每次写入更新版本计数器,读取时校验版本与 MAC,发现回滚即拒绝。

(3)文件级隔离

不同 TA 的安全存储相互隔离,TA 之间不能互相读取对方的存储文件,只有经过授权的接口调用才能访问。

2.4 可信 UI(Trusted UI)

普通 UI 可能被恶意应用覆盖(钓鱼),可信 UI 则提供安全显示通道

普通世界                        可信世界
    │                               │
    │ 调用 Secure Input             │
    │ ────────────────────────────→ │ 独占安全显示层
    │                               │ 密码键盘渲染在安全通道
    │ ←──────────────────────────── │ 防截屏、防录屏、防覆盖
    │                               │
    │ 输入通过安全通道传递           │ 数据不出可信世界

可信 UI 解决的核心问题是"输入输出通道的可信":

  • 防覆盖攻击(Overlay Attack):普通世界的恶意应用可以绘制一个"假支付页面"覆盖在真页面之上,用户输入的密码被假页面截获。可信 UI 的显示通道由 TEE 独占,普通世界无法在其上绘制覆盖层——用户看到的密码键盘一定是真的。
  • 防截屏录屏:安全显示层的内容不出现在系统的截屏/录屏缓冲区中,即使 root 后调用截屏 API,得到的是安全区域之外的画面。
  • 输入直达 TEE:安全键盘上用户的每次按键,输入事件直接由硬件安全通道送达 TEE,不经过普通世界的输入系统——恶意应用无法通过监听输入事件窃取密码。

2.5 安全时钟(Secure Clock)

证书有效期、密钥过期时间、DRM 播放期限都需要可信时间源。安全时钟由 TEE 提供防篡改的时间:

  • 防改时间绕过:普通世界的用户或恶意应用可以修改系统时间(比如把时间拨到证书有效期内)。安全时钟运行在 TEE,普通世界无法修改,保证"有效期校验"的可信。
  • 单调性:安全时钟保证单调递增(不可回拨),防止通过回拨时间让已过期的授权"复活"。
  • 应用场景:DRM 播放期限(视频只能看 48 小时)、临时授权过期校验、证书有效期判断、限时功能的过期控制。

2.6 密钥管理(HUKS)

HUKS(HarmonyOS Universal KeyStore)是 TEE 密钥能力的开放接口:

import { huks } from '@kit.UniversalKeystoreKit';

// 在 TEE 内生成 AES 密钥
const KEY_ALIAS: string = 'payment_aes_key';
const properties: Array<huks.HuksParam> = [
  { tag: huks.HuksTag.HUKS_TAG_ALGORITHM, value: huks.HuksKeyAlg.HUKS_ALG_AES },
  { tag: huks.HuksTag.HUKS_TAG_KEY_SIZE, value: huks.HuksKeySize.HUKS_AES_KEY_SIZE_256 },
  { tag: huks.HuksTag.HUKS_TAG_PURPOSE,
    value: huks.HuksKeyPurpose.HUKS_KEY_PURPOSE_ENCRYPT |
      huks.HuksKeyPurpose.HUKS_KEY_PURPOSE_DECRYPT }
];

const options: huks.HuksOptions = { properties: properties };

huks.generateKeyItem(KEY_ALIAS, options)
  .then(() => {
    console.info('密钥已在 TEE 内生成');
  })
  .catch((err) => {
    console.error('生成失败: ' + JSON.stringify(err));
  });

关键特性:

  • 密钥永不出 TEE:应用只拿到句柄(alias),拿不到明文密钥。加密/解密操作通过 HUKS 接口在 TEE 内完成,应用侧只有密文进出。
  • 用途绑定:生成时声明用途(加密/签名),防止密钥被滥用于其他场景。比如一个加密密钥不能被用于签名——即使攻击者拿到了这个密钥的句柄。
  • 不可导出:TEE 内生成的密钥默认不可导出,即使 root 也拿不到。只有显式声明可导出的密钥才能导出(导出时通常也经过加密)。
  • 访问控制绑定:密钥可与生物特征(指纹/人脸)或口令绑定,使用密钥前必须通过认证——比如"支付密钥只能在指纹验证通过后使用"。
  • 密钥与安装绑定:密钥关联应用实例,卸载重装后密钥删除(除非云备份迁移)。

HUKS 支持的算法族:对称算法(AES/SM4/3DES)、非对称算法(RSA/ECC/SM2)、摘要算法(SHA-256/SM3)、HMAC、以及密钥派生(HKDF)等。

三、典型攻击场景还原

3.1 场景一:root 后读取指纹模板

攻击者操作:
1. 解锁 Bootloader → 刷入 Magisk → 获取 root
2. 尝试读取指纹模板文件(/data/xxx/fingerprint.dat)
3. 尝试 dump 应用内存中的密钥

被拦截的环节:
① 指纹模板存放在 TEE 安全存储,落盘的是密文,root 进程读到的是密文
② 解密密钥绑定设备 UID + TEE,root 进程无法在 TEE 内执行解密
③ 应用内存中的"密钥句柄"只是 alias,没有明文密钥可 dump

3.2 场景二:键盘记录器窃取密码

攻击者操作:
1. 安装恶意应用,注册无障碍服务
2. 尝试监听输入事件,截获密码

被拦截的环节:
① 如果支付页面使用可信 UI,密码键盘渲染在安全显示通道
② 输入事件不经过普通世界输入系统,无障碍服务监听不到
③ 即使截屏,安全区域内容不在截屏缓冲区中

3.3 场景三:改系统时间绕过有效期

攻击者操作:
1. 把系统时间改到 DRM 视频的播放期限内(比如已过期)
2. 重新播放受保护内容

被拦截的环节:
① DRM 有效期校验使用 TEE 安全时钟,不受系统时间影响
② 安全时钟单调递增,无法回拨
③ 播放器的解密密钥操作在 TEE 内执行,密钥带时间条件访问控制

四、实战落地

对应 Demo 页面:entry/src/main/ets/pages/TeeSecureDemo.ets

Demo 包含三个 Tab:

  1. TEE架构:展示 REE/TEE 双世界、安全存储、可信 UI、安全时钟五个组成部分及状态
  2. 安全存储:列出指纹模板/支付密钥/数字证书等受保护数据,支持模拟安全写入
  3. 密钥管理:展示 HUKS 密钥列表(算法/用途/来源),支持模拟生成新密钥,强调"密钥在 TEE 内生成,私钥不可导出"
// Demo 核心:模拟 TEE 内生成密钥
private generateKey(): void {
  const aliases: string[] = ['new_aes_256_key', 'new_rsa_4096_key', 'new_sm4_key'];
  const idx: number = this.keys.length % aliases.length;
  const newKeys: KeyItem[] = this.keys.slice(0);
  newKeys.push({
    alias: aliases[idx] + '_' + Date.now().toString().substring(7),
    algorithm: idx === 0 ? 'AES-256' : (idx === 1 ? 'RSA-4096' : 'SM4'),
    purpose: '加解密/签名',
    origin: 'HUKS生成(TEE内)', status: '✅ 可用'
  });
  this.keys = newKeys;
}

4.1 应用侧使用 HUKS 的完整流程

一个典型的"本地数据加密"流程:

1. 生成密钥:HUKS.generateKeyItem(alias, {算法, 大小, 用途})
2. 加密数据:HUKS.encrypt(alias, 明文) → 返回密文
3. 存储密文:密文落盘(数据库/文件),密钥不落盘
4. 解密数据:HUKS.decrypt(alias, 密文) → 返回明文
5. 密钥轮换:删除旧密钥 → 重新生成 → 旧数据重新加密
6. 数据作废:HUKS.deleteKeyItem(alias)(注销/解绑场景)

注意:加密解密操作本身也在 TEE 内执行,应用侧只是把数据交给 HUKS,HUKS 在 TEE 内完成运算。所以即使应用进程被注入 Hook,Hook 到的也只是密文进出,拿不到明文密钥。

4.2 何时用 HUKS,何时用 SE

场景 推荐方案 理由
应用数据加密 HUKS 容量大、接口通用、支持多种算法
支付密钥 SE(如可能) 最高等级硬件保护
生物特征模板 TEE 安全存储 系统级管理
DRM 密钥 TEE + 硬件绑定 防拷贝防导出
设备身份证书 SE 出厂预置、不可篡改

实际开发中,绝大多数场景用 HUKS 就足够了。SE 通常用于对安全等级要求极高的支付行业,普通应用接触不到。

五、避坑速查

现象 原因 解决
密钥丢失 卸载重装后加密数据无法解密 密钥绑定应用安装 使用系统级 KeyStore + 云备份策略,或导出后重新加密
HUKS 同步调用阻塞 UI 卡顿 huks 部分接口是同步阻塞 用 Promise 异步接口,或在 TaskPool 中调用
用途声明过宽 加密密钥被拿去签名 PURPOSE 未细分 严格声明用途,签名/加密分开建密钥
弱算法残留 安全审计不通过 历史 3DES/DES 密钥未清理 扫描弱算法密钥并轮换
指纹变化 生物识别失效 模板绑定传感器 模板变更时引导重新录入
密钥句柄泄露 其他模块拿到 alias 把 alias 当作密钥本身 alias 只是索引,密钥本体在 TEE,仍无法导出
混淆破坏 HUKS 调用 生成/使用报错 混淆改动了 HUKS 相关类 混淆规则白名单排除 HUKS 接口类
忽视密钥轮换 长期使用同一密钥 未设计轮换机制 密钥设定有效期,到期强制轮换
在 TEE 外算密钥 密钥可被 dump 自己在应用层算密钥 所有密钥生成/运算走 HUKS
把密码当密钥 爆破风险 直接使用用户口令 通过 HUKS 的派生接口(PBKDF/HKDF)生成密钥

六、总结

TEE 的本质是**“用硬件隔离换取绝对安全”**:

  1. 物理隔离是根本:TrustZone 级别的内存/外设隔离,让"主系统被攻破"不影响安全世界
  2. 密钥不出 TEE 是红线:所有敏感密钥只在可信世界内使用
  3. 安全存储是保险柜:指纹/密钥/证书等敏感数据加密存放且不可导出
  4. 可信 UI 是防钓鱼关键:输入输出通道被 TEE 独占,覆盖攻击和键盘记录失效

开发者落地建议:

  • 敏感数据(支付密钥、生物特征、DRM)务必走 HUKS,不要自己存文件
  • 密钥用途声明要严格(加密/签名分离)
  • 理解密钥与安装的绑定关系,做好卸载重装场景的兼容策略
  • 不要在应用层实现"伪 TEE"(自写加密存储),系统能力足够且更可靠

落地检查清单:

  • 敏感数据全部密文落盘,密钥走 HUKS
  • 密钥用途声明严格(加密/签名/派生分离)
  • HUKS 调用全部走异步接口
  • 弱算法密钥已识别并轮换
  • 卸载重装的数据策略已明确
  • 支付/登录等敏感页评估是否启用可信 UI
Logo

讨论HarmonyOS开发技术,专注于API与组件、DevEco Studio、测试、元服务和应用上架分发等。

更多推荐