查看: 220|回复: 0

鸿蒙7跨端安全存储适配:Asset Store Kit接口收敛与降级避坑

[复制链接]
发表于 1 小时前 | 显示全部楼层 |阅读模式
跨端项目中常见一个反例:同一套登录逻辑,iOS 端把 Token 存进系统 Keychain(硬件级保险箱),Android 端却把 Token 写进明文 SharedPreferences——在 root 设备上等于裸奔。业务代码看不出任何异常,但整体安全水位被最弱的一端拉垮。这种“不对称安全姿态”是跨端安全最致命的坑,也是本文要解决的核心问题。

HarmonyOS 7(API 26)的“星盾”安全体系把安全做得很深:关键资产走 Asset Store Kit 加密存储,密钥在 TEE 内全生命周期保护,权限从“静态一次性授权”升级为“动态能力契约”。但这些能力是 HarmonyOS 独占的。当通过 ArkUI-X 跨到 Android/iOS 时,必须用对应平台的等价“保险箱”(Android Keystore、iOS Keychain)来接住,并把三端的保护语义收敛到同一套接口契约。

一、HarmonyOS 7 安全隐私 Kit 能力全景

HarmonyOS 7 的安全隐私能力不是零散 API,而是一组按“数据敏感度”与“使用场景”分层的 Kit:


  • Asset Store Kit(@kit.AssetStoreKit):为 API 密钥、登录凭据、支付密码这类关键资产提供独立、受控、可组合认证策略的存取空间。数据由系统加密,密钥绑定锁屏密码,密钥明文不离开安全区。失败时绝不能降级为明文保存。
  • Crypto Architecture Kit + HUKS:密钥从生成、存储、使用到销毁全程在 TEE 内保护——生成不经过普通内存、使用只在 TEE 内运算、销毁同步清理安全区材料。应用只能拿到密钥别名(句柄),拿不到明文。
  • Security Kit 权限模型(动态能力契约):从“应用申请→用户一次性授权→无限制读取”升级为“声明使用场景 + 场景化细粒度授权 + 运行时行为监控 + 数据最小交付”。授权分四维:身份授权(生物/锁屏绑定)、时效授权(到期失效)、场景授权(仅前台可用,后台静默调用被拦截)、应用授权(仅指定签名应用可调用)。
  • Device Security Kit 超级隐私模式(API 26 新增):用户可一键拦截相机、麦克风、定位等敏感组件访问。应用通过 superPrivacyMode.onSuperPrivacyModeOrPolicyChange 订阅状态变更,在隐私被拦截时做功能降级。


二、跨端适配的核心矛盾:三端“保险箱”语义不同

三端都有“安全存储”,但底层语义差异巨大——这是适配的难点,也是容易踩坑的地方。最关键的一条:不要制造不对称安全姿态。某端用保险箱、另一端用明文,等于把全局安全水位降到最弱端。

跨端框架(含 ArkUI-X 的 Bridge 封装)常常“一端映射到 Keychain、另一端悄悄退化成明文偏好”。必须读插件源码而不是只读 README,才能确认底层到底映射到了什么。

三、三层适配层:把安全能力收敛到接口边界

用“统一接口 → 平台实现 → 运行时工厂”的结构,把安全存储抽象成一个与平台无关的契约,让业务代码只依赖接口:

统一接口层:定义 ISecureStorage 与 IPermissionGate,业务只依赖方法签名。
平台实现层:鸿蒙端用 Asset Store Kit;Android/iOS 端通过 @arkui-x.bridge 桥接到原生 Keystore/Keychain。
运行时分发层:用 getOsType() 平台判断,先判平台再决定实现——必须先判平台,因为鸿蒙预览环境无 Bridge,直接调桥接会崩溃。

四、实战代码:从鸿蒙原生到三端收敛

4.1 鸿蒙端:Asset Store Kit 关键资产存储
  1. import { asset } from '@kit.AssetStoreKit';
  2. import { util } from '@kit.ArkTS';
  3. export class AssetService {
  4.   private encoder: util.TextEncoder = new util.TextEncoder();
  5.   private decoder: util.TextDecoder = new util.TextDecoder();
  6.   // 保存敏感凭据(如登录 Token):系统加密,密钥绑定锁屏,明文不出安全区
  7.   async saveSecret(alias: string, value: string): Promise<void> {
  8.     const attrs: asset.AssetMap = new Map();
  9.     attrs.set(asset.Tag.ALIAS, this.encoder.encodeInto(alias));
  10.     attrs.set(asset.Tag.SECRET, this.encoder.encodeInto(value));
  11.     // 设备解锁后即可访问;高敏场景改为 DEVICE_FIRST_UNLOCKED 或绑定生物认证
  12.     attrs.set(asset.Tag.ACCESSIBILITY, asset.Accessibility.DEVICE_UNLOCKED);
  13.     // 高敏场景:要求用户认证后才解锁密钥
  14.     // attrs.set(asset.Tag.AUTH_TYPE, asset.AuthType.ANY);
  15.     await asset.add(attrs);
  16.   }
  17.   async querySecret(alias: string): Promise<string | null> {
  18.     const query: asset.AssetMap = new Map();
  19.     query.set(asset.Tag.ALIAS, this.encoder.encodeInto(alias));
  20.     query.set(asset.Tag.RETURN_TYPE, asset.ReturnType.ALL);
  21.     try {
  22.       const res: asset.AssetMap[] = await asset.query(query);
  23.       if (res.length === 0) return null;
  24.       const buf = res[0].get(asset.Tag.SECRET) as Uint8Array;
  25.       return this.decoder.decodeToString(buf);
  26.     } catch (e) {
  27.       return null;
  28.     }
  29.   }
  30.   async removeSecret(alias: string): Promise<void> {
  31.     const query: asset.AssetMap = new Map();
  32.     query.set(asset.Tag.ALIAS, this.encoder.encodeInto(alias));
  33.     await asset.remove(query);
  34.   }
  35. }
复制代码

要点:AssetMap 是真正的 Map(不是对象字面量),SECRET/ALIAS 必须是 Uint8Array,用 util.TextEncoder().encodeInto 编码;查询用 asset.query(异步)或 asset.querySync(同步),返回的是数组,每个元素是 AssetMap。

4.2 统一接口 + 运行时工厂
  1. // 统一安全存储接口:业务只依赖方法签名,不感知背后是谁
  2. export interface ISecureStorage {
  3.   save(alias: string, value: string): Promise<void>;
  4.   load(alias: string): Promise<string | null>;
  5.   clear(alias: string): Promise<void>;
  6. }
  7. // 鸿蒙实现:直接走 Asset Store Kit
  8. class HarmonySecureStorage implements ISecureStorage {
  9.   private asset = new AssetService();
  10.   async save(a: string, v: string): Promise<void> { await this.asset.saveSecret(a, v); }
  11.   async load(a: string): Promise<string | null> { return this.asset.querySecret(a); }
  12.   async clear(a: string): Promise<void> { await this.asset.removeSecret(a); }
  13. }
  14. // Android/iOS 实现:通过 Bridge 桥接原生 Keystore / Keychain
  15. import bridge from '@arkui-x.bridge';
  16. class NativeSecureStorage implements ISecureStorage {
  17.   private native = bridge.createBridge('SecureStoreBridge');
  18.   async save(a: string, v: string): Promise<void> { await this.native.callMethod('save', a, v); }
  19.   async load(a: string): Promise<string | null> {
  20.     return this.native.callMethod('load', a) as Promise<string | null>;
  21.   }
  22.   async clear(a: string): Promise<void> { await this.native.callMethod('clear', a); }
  23. }
  24. // 运行时分发:沿用系列#12 的 getOsType(),先判平台再选型
  25. export function createSecureStorage(): ISecureStorage {
  26.   return getOsType() === 'harmony'
  27.     ? new HarmonySecureStorage()
  28.     : new NativeSecureStorage();
  29. }
复制代码

4.3 隐私:订阅超级隐私模式 + 运行时权限
  1. import { superPrivacyMode } from '@kit.DeviceSecurityKit';
  2. const onPolicyChange = (info: superPrivacyMode.SuperPrivacyPolicyInfo): void => {
  3.   // 超级隐私开启时,相机/麦克风/定位被系统强制拦截
  4.   if (info.superPrivacyMode) {
  5.     // 引导用户关闭或做功能降级,而不是崩溃
  6.   }
  7. };
  8. // 订阅(API 26+,需设备支持超级隐私)
  9. superPrivacyMode.onSuperPrivacyModeOrPolicyChange(onPolicyChange);
  10. // 不再需要时务必取消订阅,避免泄漏
  11. // superPrivacyMode.offSuperPrivacyModeOrPolicyChange(onPolicyChange);
复制代码

对于普通敏感权限(定位、相册等),仍走运行时动态授权:abilityAccessCtrl 的 checkAccessToken() 检查、requestPermissionsFromUser() 精准拉起授权弹框,且每次访问受保护接口前都必须重新检查授权状态。

五、把跨端安全成本压到最低:避坑清单

结合金融类 App 的真实落地,沉淀以下避坑清单:


  • 最致命:不对称安全姿态。禁止一端保险箱、另一端明文。统一 ISecureStorage 接口,三端都进“保险箱”,宁可慢一点也不要留明文后门。
  • Keystore 硬件 backing 缺失:部分设备无 TEE/StrongBox,密钥生成可能静默降级为软件级,需定义降级路径并如实记录安全等级,不能假设“有 Keystore 就一定硬件级”。
  • 加密 ≠ 绝对安全:本地加密防静态读取,防不了 in-app hook,支付/购买等高价值逻辑仍要服务端校验 + 防重放。
  • 敏感数据失败不降级明文:Asset Store 不可用、锁屏未设密码等异常时,应失败拒绝而非落到明文 Preferences,否则前功尽弃。
  • 超级隐私模式及时 off:onSuperPrivacyModeOrPolicyChange 订阅后,页面销毁或不再需要时必须 off,否则泄漏回调与隐私风险。
  • 备份开关对齐:Android allowBackup=false、iOS 关闭 iCloud 同步对应项,避免密文/保险箱被备份带走。
  • 分级存储策略:高敏(Token/支付密钥)进保险箱;中敏(用户资料)进加密数据库;低敏(主题/语言)才进普通偏好。什么该进保险箱、什么不该,是生产准则而非可选项。


六、总结:收敛与分级是两条铁律

HarmonyOS 7 的星盾体系把安全做得很深——Asset Store Kit 让关键资产密钥不落盘、Crypto+HUKS 让密钥明文不出 TEE、动态能力契约让权限从“一次性授权”变“场景化契约”、超级隐私模式让用户一键收回敏感组件。但这些是 HarmonyOS 独占能力。

跨端适配的本质,是用 ArkUI-X 的三层适配层(统一接口 → 平台实现 → 运行时工厂),把这套安全语义收敛到同一份接口契约。两条铁律:收敛(三端都进保险箱,拒绝不对称姿态)与分级(按敏感度决定存储位置)。做到这两点,“一次开发”才不会变成“一次把安全漏洞铺到三端”。
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 注册

本版积分规则

指导单位

江苏省公安厅

江苏省通信管理局

浙江省台州刑侦支队

DEFCON GROUP 86025

Hacking Group 021A

旗下站点

态势感知中心

应急响应中心

红盟安全

联系我们

官方QQ群:112851260

官方邮箱:security#ihonker.org(#改成@)

官方核心成员

关注微信公众号

Archiver|手机版|小黑屋| ( 沪ICP备2021026908号 )

GMT+8, 2026-10-25 09:51 , Processed in 0.054781 second(s), 28 queries , Gzip On.

Powered by ihonker.com

Copyright © 2015-现在.

  • 返回顶部