守听 发表于 2025-4-7 17:23:42

【HarmonyOS NEXT】关键资产存储开发案例

在 iOS 开发中 Keychain 是一个非常安全的存储系统,用于生存敏感信息,如暗码、证书、密钥等。与文件系统不同,Keychain 提供了更高的安全性,因为它对数据举行了加密,并且只有经过授权的应用步调才能访问存储的数据。那么在鸿蒙内里对应的是什么呢?


1、关键资产(@ohos.security.asset)

在鸿蒙内里也有类似的东西,叫做关键资产(@ohos.security.asset),关键资产存储服务提供了用户短敏感数据的安全存储及管理能力。此中,短敏感数据可以是暗码类(账号/暗码)、Token类(应用根据)、其他关键明文(如银行卡号)等长度较短的用户敏感数据。
关键资产的安全存储,依赖底层的通用密钥库系统。具体来说,关键资产的加/解密操纵以及访问控制校验,都由通用密钥库系统在安全情况(如可信执行情况)中完成,即使系统被攻破,也能包管用户敏感数据不发生泄露。此中,关键资产的加/解密使用AES256-GCM算法。

2、关键资产使用与 asset 的常用操纵

关键资产的访问可分为 4 类(可查看本文第4章节),基于属主的访问控制、基于锁屏状态的访问控制、基于锁屏暗码设置状态的访问控制、基于用户认证的访问控制,业务可根据现实情况决定是否开启(如扫脸验证、解锁验证、暗码验证等),本次文章举例的开发案例均采用默认保护等级。
 2.1 使用关键资产需要导入模块 AssetStoreKit:

import { asset } from '@kit.AssetStoreKit'; 2.2 常用操纵:

方法描述asset.add新增一条关键资产。asset.remove删除符合条件的一条或多条关键资产。asset.update更新符合条件的一条关键资产。asset.query查询一条或多条符合条件的关键资产。若查询需要用户认证的关键资产,则需要在本函数前调用asset.preQuery,在本函数后调用asset.postQuery。asset.preQuery查询的预处理,用于需要用户认证的关键资产。在用户认证乐成后,应当随后调用asset.query、asset.postQuery。asset.postQuery查询的后置处理,用于需要用户认证的关键资产。需与asset.preQuery函数成对出现。
3、常规方法封装

3.1 addSync 设置数据

/**
   * 新增一条关键资产
   * @param key
   * @param value
   * @returns
   */
add(key: string, value: string) {
    let result: Boolean
    let attr: asset.AssetMap = new Map();
    // 关键资产别名,每条关键资产的唯一索引。
    // 类型为Uint8Array,长度为1-256字节。
    attr.set(asset.Tag.SECRET, this.string2Array(value));
    // 关键资产明文。
    // 类型为Uint8Array,长度为1-1024字节
    attr.set(asset.Tag.ALIAS, this.string2Array(key))
    // 关键资产同步类型>THIS_DEVICE只在本设备进行同步,如仅在本设备还原的备份场景。
    attr.set(asset.Tag.SYNC_TYPE, asset.SyncType.THIS_DEVICE);

    //枚举,新增关键资产时的冲突(如:别名相同)处理策略。OVERWRITE》抛出异常,由业务进行后续处理。
    // attr.set(asset.Tag.CONFLICT_RESOLUTION,asset.ConflictResolution.THROW_ERROR)
    // 在应用卸载时是否需要保留关键资产。
    // 需要权限: ohos.permission.STORE_PERSISTENT_DATA。
    // 类型为bool。
    // attr.set(asset.Tag.IS_PERSISTENT, true);//我项目里面没有使用就先注释了,后续有需要这个再打开,并且要设置对应权限

    if (this.isHasKey(key)) {
      result = this.updateAssetMap(attr);
    } else {
      try {
      if (canIUse("SystemCapability.Security.Asset")) {
          asset.addSync(attr);
          result = true
      }
      result = false
      } catch (error) {
      let err = error as BusinessError;
      LogUtil.e(`Failed to add Asset. Code is ${err.code}, message is ${err.message}`);
      result = false
      }
    }
    return result
} 3.2 querySync 获取数据

/**
   * 查询
   * @param key
   * @returns
   */
query(key: string) {
    let query: asset.AssetMap = new Map();
    // 关键资产别名,每条关键资产的唯一索引。
    // 类型为Uint8Array,长度为1-256字节。
    query.set(asset.Tag.ALIAS, this.string2Array(key));
    //关键资产查询返回的结果类型。
    query.set(asset.Tag.RETURN_TYPE, asset.ReturnType.ALL);
    // query.set(asset.Tag.RETURN_TYPE, asset.ReturnType.ATTRIBUTES); // 此处表示仅返回关键资产属性,不包含关键资产明文

    try {
      if (canIUse("SystemCapability.Security.Asset")) {
      let res: Array<asset.AssetMap> = asset.querySync(query);
      for (let i = 0; i < res.length; i++) {
          // parse the attribute.
          if (res != null) {
            // parse the secret.
            let secret: Uint8Array = res.get(asset.Tag.SECRET) as Uint8Array;
            // parse uint8array to string
            let secretStr: string = this.array2String(secret);
            return secretStr;
          }
      }
      }
    } catch (error) {
      let err = error as BusinessError;
      LogUtil.e(TAG, `Failed to query Asset. Code is ${err.code}, message is ${err.message}`);
      return "";
    }
    return ""
} 3.3 querySync 查询 key 数据是否存在

/**
   * 查询资产key是否存在
   * @param key
   * @returns
   */
isHasKey(key: string): Boolean {
    if (canIUse("SystemCapability.Security.Asset")) {
      let query: asset.AssetMap = new Map();
      // 关键资产别名,每条关键资产的唯一索引。
      // 类型为Uint8Array,长度为1-256字节。
      query.set(asset.Tag.ALIAS, this.string2Array(key));
      //关键资产查询返回的结果类型。
      query.set(asset.Tag.RETURN_TYPE, asset.ReturnType.ALL);
      const res = this.queryAssetMap(query);
      if (!res || res.length < 1) {
      return false;
      }
      return true;
    }
    return false;
} /**
   * 查询资产 key 的 assetMaps 数据
   * @param query
   * @returns
   */
queryAssetMap(query: asset.AssetMap): Array<asset.AssetMap> {
    const assetMaps: asset.AssetMap[] = [];
    try {
      if (canIUse("SystemCapability.Security.Asset")) {
      const res: asset.AssetMap[] = asset.querySync(query);
      return res;
      }
      return assetMaps;
    } catch (error) {
      const err = error as BusinessError;
      LogUtil.e(TAG, `Failed to query Asset. Code is ${err.code}, message is ${err.message}`);
      return assetMaps;
    }
} 3.4 updateSync 修改资产数据

/**
   * 修改资产数据
   * @param q
   * @returns
   */
updateAssetMap(q: asset.AssetMap): Boolean {
    try {
      if (canIUse("SystemCapability.Security.Asset")) {
      let query: asset.AssetMap = new Map();
      query.set(asset.Tag.ALIAS, q.get(asset.Tag.ALIAS)!);
      let attrsToUpdate: asset.AssetMap = new Map();
      attrsToUpdate.set(asset.Tag.SECRET, q.get(asset.Tag.SECRET)!);
      asset.updateSync(query, attrsToUpdate);
      return true
      }
      return false
    } catch (error) {
      const err = error as BusinessError;
      LogUtil.e(TAG, `Failed to update Asset. Code is ${err.code}, message is ${err.message}`);
      return false;
    }
} 3.5 removeSync 删除资产数据

/**
   * 删除一条关键资产
   * @param key
   */
remove(key: string) {
    let query: asset.AssetMap = new Map();
    // 关键资产别名,每条关键资产的唯一索引。
    query.set(asset.Tag.ALIAS, this.string2Array(key));
    try {
      if (canIUse("SystemCapability.Security.Asset")) {
      asset.removeSync(query);
      return true;
      }
      return false;
    } catch (error) {
      let err = error as BusinessError;
      LogUtil.e(TAG, `Failed to remove Asset. Code is ${err.code}, message is ${err.message}`);
      return false;
    }
} 3.6 string 与 Uint8Array 数据互转换

string2Array(str: string): Uint8Array {
    let textEncoder = new util.TextEncoder();
    return textEncoder.encodeInto(str);
}

array2String(str: Uint8Array): string {
    let textDecoder = new util.TextDecoder();
    return textDecoder.decodeToString(str);
}

4、关键资产的访问控制



[*] 基于属主的访问控制: 所有的关键资产都受属主访问控制保护,业务无需设置。

[*]只允许关键资产被其属主(写入该关键资产的业务)访问。
[*]关键资产属主身份由ASSET从系统服务中获取,即使业务身份被仿冒,仿冒者也无法获取到其他业务的数据。
[*]关键资产加/解密时,其属主身份到场了完整性保护,即使关键资产属主身份被篡改,攻击者也无法获取到其他业务的数据。

[*] 基于锁屏状态的访问控制: 分为以下三种保护等级(安全性依次递增),业务可根据现实情况设置恣意一种,若不设置,则默认保护等级为“首次解锁后可访问”。

[*]开机后可访问:关键资产在开机后被允许访问。
[*]首次解锁后可访问:关键资产在首次解锁后被允许访问。
[*]解锁时可访问:关键资产仅在处于解锁状态时被允许访问。

[*] 基于锁屏暗码设置状态的访问控制: 该访问控制默认不开启,业务可根据现实情况决定是否开启。

[*]在用户设置了锁屏暗码后,关键资产才被允许访问。

[*] 基于用户认证的访问控制: 该访问控制默认不开启,业务可根据现实情况决定是否开启。

[*]关键资产在用户身份认证通过后被允许访问。
[*]恣意一种认证方式(指纹、人脸、PIN码)通过,均可授权本次关键资产的访问。
[*]业务可通过设置认证有效期,告竣一次用户认证、授权多个关键资产访问的效果。认证有效期最长可设置10分钟。


5、参考

华为官网:@ohos.security.asset (关键资产存储服务)

免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。
页: [1]
查看完整版本: 【HarmonyOS NEXT】关键资产存储开发案例