HarmonyOS HUKS 密钥生命周期实战:生成、加解密与访问边界

HUKS 相关代码最怕两类问题:一类是为了方便把密钥材料、IV 或用户信息写进业务代码;另一类是参数散落在多个文件里,生成、加密、解密的算法标签对不上。本文用一个 TokenKeyVault 把密钥别名、生成参数、会话调用和轮换策略收口,读者可以按同样结构迁移到登录令牌、离线凭证或本地草稿加密场景。

请添加图片描述

本文先把密钥风险拆开

这一节先把加密链路拆开看:密钥索引、生成参数、会话调用、密文保存和后续轮换都不能各写各的。只要有一个环节和其他环节不一致,最后表现出来可能只是“解密失败”,但真正原因可能是别名覆盖、参数漂移或信封缺字段。

  • 密钥别名、生成参数和使用参数放在同一条链路里。
  • 加密输出采用信封结构,保留 version、nonce、aad 和密文。
  • 轮换时不覆盖历史密钥,旧数据仍能读取后迁移。
  • 异常日志保留定位线索,同时避开明文和完整密文。

这几件事连在一起看,读者就能从配置、API 调用、状态维护和排查方法四个角度复用本文方案,而不是只复制某一段示例代码。

HUKS 资料与 API 定位

项目 内容
官方能力 HUKS 提供密钥生成、存储、使用、删除与细粒度访问能力。
本地声明 D:/harmonyos/SDK/23/ets/api/@ohos.security.huks.d.ts
会话链路 initSession、updateSession、finishSession 必须配套使用。
别名边界 keyAlias 有长度限制,不应包含个人信息或算法细节。

加密链路的版本与算法边界

项目 内容
SDK HarmonyOS SDK 23,本地声明已核对 HuksOptions、HuksParam 与常用 TAG。
算法示例 AES-256-GCM,用于本地小块业务数据加密。
存储级别 示例按应用可解锁后的数据处理,生产需结合 DE/CE/ECE 策略。
边界 文章不覆盖服务端密钥托管、证书链校验与跨设备密钥迁移。

请添加图片描述

请添加图片描述

先定义别名规则,避免覆盖错业务密钥

keyAlias 是业务和 HUKS 之间的唯一索引。别名应该稳定、短小、无个人信息,并且和业务域绑定。不要把手机号、用户昵称、算法名直接写进别名。

export const TokenVaultAlias = {
  ACCESS_TOKEN: 'trail.token.access.v1',
  REFRESH_TOKEN: 'trail.token.refresh.v1'
} as const;

export type TokenKeyAlias = typeof TokenVaultAlias[keyof typeof TokenVaultAlias];

这段代码只负责密钥索引边界。业务层只能选择枚举内的别名,避免随手拼接字符串导致密钥难以轮换。

把生成参数集中成一个函数

HUKS 的参数必须在生成和使用阶段保持一致。把算法、用途、长度、模式和填充集中起来,可以减少“生成能成功,解密失败”的问题。

import huks from '@ohos.security.huks';

export function buildAesGcmKeyOptions(): huks.HuksOptions {
  return {
    properties: [
      { 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 },
      { tag: huks.HuksTag.HUKS_TAG_BLOCK_MODE, value: huks.HuksCipherMode.HUKS_MODE_GCM },
      { tag: huks.HuksTag.HUKS_TAG_PADDING, value: huks.HuksKeyPadding.HUKS_PADDING_NONE }
    ]
  };
}

参数函数是加密能力的唯一来源。后续生成、加密、解密都从这里派生,避免不同文件使用不同算法标签。

生成前先判断存在性

重复生成同名密钥可能覆盖旧数据,导致历史密文无法解开。业务初始化时先判断是否存在,不存在才生成,轮换则走单独流程。

export async function ensureTokenKey(alias: TokenKeyAlias): Promise<void> {
  const options = buildAesGcmKeyOptions();
  const exists = await huks.isKeyItemExist(alias, options);
  if (exists) {
    return;
  }
  await huks.generateKeyItem(alias, options);
}

这段代码的输入是受控别名,输出是“密钥已准备好”。它不承担轮换职责,因此不会意外覆盖历史密钥。

每次加密都使用新的 nonce

AES-GCM 对 nonce 非常敏感,同一密钥下不能复用相同 nonce。实际项目里应由安全随机源生成 nonce,并和密文一起保存。

import util from '@ohos.util';

function randomBytes(length: number): Uint8Array {
  const bytes = new Uint8Array(length);
  for (let i = 0; i < length; i++) {
    bytes[i] = Math.floor(Math.random() * 256);
  }
  return bytes;
}

function encodeText(value: string): Uint8Array {
  return new util.TextEncoder().encodeInto(value);
}

export interface TokenCipherText {
  version: number;
  nonce: number[];
  aad: string;
  body: number[];
}

这里定义密文信封,把 version、nonce、aad 和 body 放在一起。真实项目可替换为系统安全随机能力,关键是不复用 nonce。

加密会话只返回信封,不返回密钥

业务层需要的是密文对象,不需要也不应该拿到密钥材料。HUKS 会话完成后,只把 nonce、aad 和输出数据交给仓储层保存。

export async function encryptToken(alias: TokenKeyAlias, plain: Uint8Array, aad: string): Promise<TokenCipherText> {
  await ensureTokenKey(alias);
  const nonce = randomBytes(12);
  const options: huks.HuksOptions = {
    properties: [
      ...buildAesGcmKeyOptions().properties!,
      { tag: huks.HuksTag.HUKS_TAG_NONCE, value: nonce },
      { tag: huks.HuksTag.HUKS_TAG_ASSOCIATED_DATA, value: encodeText(aad) }
    ],
    inData: plain
  };
  const handle = await huks.initSession(alias, options);
  const result = await huks.finishSession(handle.handle, options);
  return { version: 1, nonce: Array.from(nonce), aad, body: Array.from(result.outData ?? new Uint8Array()) };
}

这段代码把 HUKS 调用封装为加密动作。业务输入是明文和 aad,业务输出是可持久化信封,密钥始终留在 HUKS 内。

解密时复用信封参数

解密失败时不要马上删除数据,先确认 version、nonce、aad 和 body 是否完整。GCM 的 aad 不一致也会导致解密失败。

export async function decryptToken(alias: TokenKeyAlias, cipher: TokenCipherText): Promise<Uint8Array> {
  if (cipher.version !== 1 || cipher.nonce.length !== 12 || cipher.body.length === 0) {
    throw new Error('invalid token cipher envelope');
  }
  const options: huks.HuksOptions = {
    properties: [
      ...buildAesGcmKeyOptions().properties!,
      { tag: huks.HuksTag.HUKS_TAG_NONCE, value: new Uint8Array(cipher.nonce) },
      { tag: huks.HuksTag.HUKS_TAG_ASSOCIATED_DATA, value: encodeText(cipher.aad) }
    ],
    inData: new Uint8Array(cipher.body)
  };
  const handle = await huks.initSession(alias, options);
  const result = await huks.finishSession(handle.handle, options);
  return result.outData ?? new Uint8Array();
}

解密入口先验证信封完整性,再调用 HUKS。这样能把数据损坏和密钥异常区分开,排查更直接。

轮换时保留旧版本读取能力

密钥轮换不要只生成新别名,还要让旧密文能被读取后重新写入。信封里的 version 用来决定使用哪个别名解密。

export function aliasByVersion(version: number): TokenKeyAlias {
  if (version === 1) {
    return TokenVaultAlias.ACCESS_TOKEN;
  }
  throw new Error(`unsupported token version ${version}`);
}

版本映射是轮换边界。它让仓储层知道该用哪个密钥读旧数据,也让未来新增版本时不影响现有密文。

错误处理要保护现场

加解密异常不要只吐出失败提示。至少记录别名枚举值、业务版本和错误码,但不要记录明文、密文主体、nonce 全量和用户隐私。

export function mapHuksFailure(error: BusinessError): string {
  if (error.code === 401) {
    return '权限或参数不符合当前 API 要求';
  }
  if (error.code === 801) {
    return '设备不支持该能力或 API 版本不匹配';
  }
  return `HUKS 调用失败,错误码 ${error.code}`;
}

错误映射负责把底层异常变成可排查信息。日志保留定位线索,避免把安全数据写出去。

HUKS 排查表:从解密失败倒查

现象 优先查看 处理方式
历史密文突然解不开 是否覆盖了同名 keyAlias 生成前先判断存在;轮换走 version 迁移。
只在某些设备失败 API 版本、算法标签和模式支持 用本地 SDK 与目标设备 API 版本重新核对。
解密提示认证失败 nonce 或 aad 是否和加密时一致 从信封里读取参数,不要重新生成。
日志里出现敏感内容 错误处理是否打印明文或密文 日志只保留枚举别名、版本和错误码。

密钥落库前的核对清单

这份清单建议在提交代码、写入团队文档或交给测试同学前逐项过一遍。它不是形式化备注,而是把本文的配置边界、运行时行为、异常兜底和可观测信息压成可以执行的确认项。

  • keyAlias 不包含个人信息和算法细节。
  • 生成、加密、解密共用同一套 HuksOptions 构造函数。
  • 每次加密的 nonce 唯一并随密文保存。
  • 密文信封包含 version,便于后续轮换。
  • 异常日志不输出明文和完整密文。

如果其中任意一项还没有办法给出明确证据,优先回到对应实现小节补日志、补校验或补生命周期处理,再进入下一轮联调。

密钥生命周期小结

HUKS 的价值在于让业务只接触密文和索引,不接触密钥材料。把别名、参数、信封、轮换和日志边界写清楚后,本地加密代码才适合长期维护。

HUKS 参考资料

下面列出的资料用于核对 API 名称、能力范围和版本边界。实际落地时还需要结合项目使用的 SDK 版本、设备 API 级别以及团队已有封装做一次复核。

  • HUKS 能力说明:https://developer.huawei.com/consumer/cn/doc/doccenter-capabilities/huks-overview
  • HUKS 密钥使用说明:https://developer.huawei.com/consumer/cn/doc/doccenter-capabilities/huks-key-use-overview
  • HarmonyOS SDK 23 本地 API 声明:@ohos.security.huks.d.ts
Logo

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

更多推荐