编者按: 密码明文存本地、Token 写进 SharedPreference、身份证号直接 JSON 序列化扔沙箱目录——这些操作在小项目里太常见了。用户根本不在乎你的数据安不安全,但一旦出了事,锅全是你的。HarmonyOS 6.0 提供了一整套从软件加密到硬件级密钥管理的安全体系,但文档分散、API 链路长,很多人看了半天还是不知道怎么落地。开发者@Frame Not Work 把整个链路串起来讲,从最基础的哈希计算一直讲到 HUKS 硬件级密钥管理,配合实际可跑的 ArkTS 代码,看完你就能直接用到项目里。

一、cryptoFramework 模块总览

HarmonyOS 6.0 的加解密能力主要由 @kit.CryptoArchitectureKit 提供,这套 API 覆盖了三大领域:

  • 哈希(消息摘要):SHA-256、SHA-384、SHA-512、MD5 等,用于数据完整性校验和指纹生成

  • 对称加密:AES-128/192/256,支持 CBC、GCM、ECB、CTR 等模式,适合大数据量加解密

  • 非对称加密:RSA、ECC、SM2 等,用于密钥协商、数字签名、小数据加密

这套 API 的设计模式非常统一:创建实例 → 初始化 → 更新数据 → 获取结果。不管你用哪种算法,流程都是这个套路,上手成本不高。

另外还有一套 @kit.UniversalKeystoreKit(HUKS),专门做密钥管理,密钥全程不离开 TEE 可信执行环境,安全性比 cryptoFramework 高一个级别。后面会详细讲。

二、哈希计算:数据指纹的第一道关

哈希不是加密,但它是安全存储的基础设施。文件完整性校验、密码存储(配合盐值)、数据去重,都离不开哈希。

HarmonyOS 支持的哈希算法:SHA-256(32 字节)、SHA-384(48 字节)、SHA-512(64 字节)、MD5(16 字节)。MD5 已经不推荐用于安全场景了,但做文件去重、缓存 key 之类非安全用途还是挺好使的。

调用流程就三步:createMd → update → digest

import { cryptoFramework } from '@kit.CryptoArchitectureKit';
import { buffer } from '@kit.ArkTS';

async function computeSha256(input: string): Promise<string> {
  let md = cryptoFramework.createMd('SHA256');
  let inputBytes: cryptoFramework.DataBlob = {
    data: new Uint8Array(buffer.from(input, 'utf-8').buffer)
  };
  await md.update(inputBytes);
  let result = await md.digest();
  let hexStr = '';
  for (let i = 0; i < result.data.length; i++) {
    let hex = result.data[i].toString(16).padStart(2, '0');
    hexStr += hex;
  }
  return hexStr;
}

数据量大的场景可以分段 update,结果不受影响:

async function computeSha256BySegment(longText: string): Promise<string> {
  let md = cryptoFramework.createMd('SHA256');
  let bytes = new Uint8Array(buffer.from(longText, 'utf-8').buffer);
  let segmentSize = 4096;
  for (let i = 0; i < bytes.length; i += segmentSize) {
    let end = Math.min(i + segmentSize, bytes.length);
    let segment: cryptoFramework.DataBlob = {
      data: bytes.subarray(i, end)
    };
    await md.update(segment);
  }
  let result = await md.digest();
  let hexStr = '';
  for (let i = 0; i < result.data.length; i++) {
    hexStr += result.data[i].toString(16).padStart(2, '0');
  }
  return hexStr;
}

这里有个细节要注意:update 接口对单次传入的数据量没有限制,分段只是为了控制内存占用。对于文件哈希计算,建议用 4KB 或更大的分段,避免频繁的异步调用开销。

三、AES 对称加密:主力加密方案

对称加密是应用层加密的绝对主力。AES 速度快、安全强度高,加密大文件也不在话下。

完整流程:createSymKeyGenerator → generateSymKey → createCipher → init → update → doFinal

AES-128-CBC 模式

CBC 是最经典的分组模式,需要 IV(初始化向量)参与运算:

import { cryptoFramework } from '@kit.CryptoArchitectureKit';
import { buffer } from '@kit.ArkTS';

async function aesCbcEncrypt(plainText: string): Promise<cryptoFramework.DataBlob> {
  let keyGenerator = cryptoFramework.createSymKeyGenerator('AES128');
  let symKey = await keyGenerator.generateSymKey();

  let ivBytes = cryptoFramework.createRandom().generateRandomSync(16);
  let ivParamsSpec: cryptoFramework.IvParamsSpec = {
    algName: 'IvParamsSpec',
    iv: { data: ivBytes.data }
  };

  let cipher = cryptoFramework.createCipher('AES128|CBC|PKCS7');
  await cipher.init(cryptoFramework.CryptoMode.ENCRYPT_MODE, symKey, ivParamsSpec);

  let input: cryptoFramework.DataBlob = {
    data: new Uint8Array(buffer.from(plainText, 'utf-8').buffer)
  };
  let encryptResult = await cipher.doFinal(input);
  return encryptResult;
}

解密时用同一个 key 和 IV,模式换成 DECRYPT_MODE

async function aesCbcDecrypt(
  symKey: cryptoFramework.SymKey,
  cipherData: cryptoFramework.DataBlob,
  ivData: Uint8Array
): Promise<string> {
  let ivParamsSpec: cryptoFramework.IvParamsSpec = {
    algName: 'IvParamsSpec',
    iv: { data: ivData }
  };

  let decoder = cryptoFramework.createCipher('AES128|CBC|PKCS7');
  await decoder.init(cryptoFramework.CryptoMode.DECRYPT_MODE, symKey, ivParamsSpec);
  let decryptResult = await decoder.doFinal(cipherData);

  let output = buffer.from(decryptResult.data).toString('utf-8');
  return output;
}

CBC 模式有几个坑要注意:IV 必须随机生成,不能硬编码;IV 需要和密文一起存储,解密时要用;PKCS7 填充模式下 doFinal 会自动处理末尾不满一个分块的情况。

AES-256-GCM 模式(推荐)

GCM 是我更推荐的模式。它不仅能加密,还带认证标签(AuthTag),能同时保证数据的机密性和完整性。CBC 模式只能加密,如果你需要验证数据有没有被篡改,还得自己算 HMAC,而 GCM 一步到位。

function buildGcmParamsSpec(): cryptoFramework.GcmParamsSpec {
  let ivBytes = cryptoFramework.createRandom().generateRandomSync(12);
  let aadBytes = new Uint8Array([1, 2, 3, 4, 5, 6, 7, 8]);
  let tagBytes = new Uint8Array(16);

  let gcmParams: cryptoFramework.GcmParamsSpec = {
    algName: 'GcmParamsSpec',
    iv: { data: ivBytes.data },
    aad: { data: aadBytes },
    authTag: { data: tagBytes }
  };
  return gcmParams;
}

async function aesGcmEncrypt(
  symKey: cryptoFramework.SymKey,
  plainText: string
): Promise<cryptoFramework.DataBlob> {
  let gcmParams = buildGcmParamsSpec();

  let cipher = cryptoFramework.createCipher('AES128|GCM|PKCS7');
  await cipher.init(cryptoFramework.CryptoMode.ENCRYPT_MODE, symKey, gcmParams);

  let input: cryptoFramework.DataBlob = {
    data: new Uint8Array(buffer.from(plainText, 'utf-8').buffer)
  };
  let encryptResult = await cipher.doFinal(input);

  // GCM 模式下 doFinal 返回密文,authTag 需要从 gcmParams.authTag 中读取
  // 解密时必须使用加密阶段生成的 authTag
  return encryptResult;
}

解密时需要把加密阶段生成的 authTag 放进 GcmParamsSpec 传给 init,如果 authTag 不匹配,解密直接失败,这就实现了完整性校验:

async function aesGcmDecrypt(
  symKey: cryptoFramework.SymKey,
  cipherData: cryptoFramework.DataBlob,
  gcmParams: cryptoFramework.GcmParamsSpec
): Promise<string> {
  let decoder = cryptoFramework.createCipher('AES128|GCM|PKCS7');
  await decoder.init(cryptoFramework.CryptoMode.DECRYPT_MODE, symKey, gcmParams);
  let decryptResult = await decoder.doFinal(cipherData);

  return buffer.from(decryptResult.data).toString('utf-8');
}
AES-128-CBC vs AES-256-GCM 怎么选
维度 AES-128-CBC AES-256-GCM
密钥长度 128 位 256 位
认证能力 无,需额外 HMAC 内置 AuthTag
IV 长度 16 字节 12 字节(推荐)
安全等级 够用 更高,推荐新项目使用

我的建议:新项目一律用 AES-256-GCM。CBC 模式最大的问题是缺乏认证能力,密文被篡改了你都不知道。GCM 自带认证标签,篡改即失败,省心太多。

四、RSA 非对称加密:公钥加密、私钥解密

RSA 的典型场景不是直接加密业务数据——它太慢了,而且有长度限制(1024 位密钥最多加密 117 字节,2048 位最多 245 字节)。RSA 真正的价值在于:密钥协商、数字签名、加密小数据(比如 AES 密钥)。

RSA 加解密
import { cryptoFramework } from '@kit.CryptoArchitectureKit';
import { buffer } from '@kit.ArkTS';

async function rsaEncryptDemo(): Promise<void> {
  // 生成 RSA 2048 密钥对
  let keyGenerator = cryptoFramework.createAsyKeyGenerator('RSA2048');
  let keyPair = await keyGenerator.generateKeyPair();

  let message = 'SensitiveData123';
  let input: cryptoFramework.DataBlob = {
    data: new Uint8Array(buffer.from(message, 'utf-8').buffer)
  };

  // 公钥加密
  let cipher = cryptoFramework.createCipher('RSA2048|PKCS1');
  await cipher.init(cryptoFramework.CryptoMode.ENCRYPT_MODE, keyPair.pubKey, null);
  let encryptResult = await cipher.doFinal(input);

  // 私钥解密(必须创建新的 Cipher 实例)
  let decoder = cryptoFramework.createCipher('RSA2048|PKCS1');
  await decoder.init(cryptoFramework.CryptoMode.DECRYPT_MODE, keyPair.priKey, null);
  let decryptResult = await decoder.doFinal(encryptResult);

  let decrypted = buffer.from(decryptResult.data).toString('utf-8');
  console.info('Decrypted: ' + decrypted);
}

注意两点:一是 RSA 的 Cipher 实例不支持重复 init,每次加解密都要 new 一个;二是非对称加密的 params 参数传 null 就行,不像 AES 那样要传 IvParamsSpec 或 GcmParamsSpec。

RSA 签名验证

签名是 RSA 另一个核心用途——用私钥签名,用公钥验证,证明数据确实来自持有私钥的一方:

async function rsaSignVerifyDemo(): Promise<void> {
  let keyGenerator = cryptoFramework.createAsyKeyGenerator('RSA2048');
  let keyPair = await keyGenerator.generateKeyPair();

  let message = 'Contract content here';
  let input: cryptoFramework.DataBlob = {
    data: new Uint8Array(buffer.from(message, 'utf-8').buffer)
  };

  // 私钥签名
  let signer = cryptoFramework.createSign('RSA2048|PKCS1|SHA256');
  await signer.init(keyPair.priKey);
  let signResult = await signer.sign(input);

  // 公钥验签
  let verifier = cryptoFramework.createVerify('RSA2048|PKCS1|SHA256');
  await verifier.init(keyPair.pubKey);
  let isValid = await verifier.verify(input, signResult);

  console.info('Signature valid: ' + isValid);
}

签名和验签的算法字符串必须一致,RSA2048|PKCS1|SHA256 里的每一项都得对上。另外 RSA 密钥长度建议至少 2048 位,1024 位在当前算力下已经不安全了。

五、HUKS 密钥管理:硬件级安全的天花板

cryptoFramework 做加解密没问题,但密钥的管理是个软肋。你在软件层生成的 AES 密钥,最终还是存在内存里,root 设备或者内存 dump 理论上能拿到。HUKS(Universal Keystore Kit)解决的就是这个问题——密钥生成、存储、使用全在 TEE(可信执行环境)里完成,密钥永远不出 TEE,你的应用代码也拿不到密钥明文。

HUKS 生成密钥
import { huks } from '@kit.UniversalKeystoreKit';

const AES_KEY_ALIAS = 'my_app_aes_key';

function getAesGenerateProperties(): Array<huks.HuksParam> {
  return [
    {
      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_PADDING,
      value: huks.HuksKeyPadding.HUKS_PADDING_NONE
    },
    {
      tag: huks.HuksTag.HUKS_TAG_BLOCK_MODE,
      value: huks.HuksCipherMode.HUKS_MODE_GCM
    }
  ];
}

async function generateHuksAesKey(): Promise<void> {
  let properties = getAesGenerateProperties();
  let options: huks.HuksOptions = {
    properties: properties
  };
  await huks.generateKeyItem(AES_KEY_ALIAS, options);
  console.info('HUKS AES key generated');
}

注意看,这里没有 generateSymKey 返回密钥对象的步骤。HUKS 的密钥由系统管理,你拿到的是一个别名(alias),后续所有操作都通过别名引用。密钥本身你永远接触不到。

HUKS 加密

HUKS 加密是三段式操作:initSession → updateSession(可选)→ finishSession

async function huksEncryptData(plainText: string): Promise<Uint8Array> {
  let iv = cryptoFramework.createRandom().generateRandomSync(12).data;
  let encryptProps: Array<huks.HuksParam> = [
    // ... 配置属性
    {
      tag: huks.HuksTag.HUKS_TAG_NONCE,
      value: iv
    },
    {
      tag: huks.HuksTag.HUKS_TAG_ASSOCIATED_DATA,
      value: new Uint8Array([1, 2, 3, 4, 5, 6, 7, 8])
    }
  ];
  let options: huks.HuksOptions = {
    properties: encryptProps,
    inData: new util.TextEncoder().encode(plainText)
  };

  let initResult = await huks.initSession(AES_KEY_ALIAS, options);
  let finishResult = await huks.finishSession(initResult.handle, options);
  return finishResult.outData as Uint8Array;
}
HUKS 解密

解密流程和加密一模一样,只是 PURPOSE 换成 DECRYPT,并且 GCM 模式下需要传入 AEAD 标签:

async function huksDecryptData(
  cipherData: Uint8Array,
  iv: Uint8Array,
  aeadTag: Uint8Array
): Promise<string> {
  let decryptProps: Array<huks.HuksParam> = [
    // ... 配置属性
    {
      tag: huks.HuksTag.HUKS_TAG_NONCE,
      value: iv
    },
    {
      tag: huks.HuksTag.HUKS_TAG_AE_TAG,
      value: aeadTag
    }
  ];
  let options: huks.HuksOptions = {
    properties: decryptProps,
    inData: cipherData
  };

  let initResult = await huks.initSession(AES_KEY_ALIAS, options);
  let finishResult = await huks.finishSession(initResult.handle, options);
  let plainBytes = finishResult.outData as Uint8Array;
  return new util.TextDecoder().decodeToString(plainBytes);
}

HUKS 的核心价值:加密解密操作在 TEE 内完成,密钥明文永远不会出现在普通执行环境(REE)的内存中。即使攻击者拿到了设备的 root 权限,也无法提取 HUKS 管理的密钥。这是软件层加密做不到的。

六、安全存储策略选择

HarmonyOS 6.0 提供了三层安全方案,安全性从低到高排列:

Base64 编码(不是加密)
import { util } from '@kit.ArkTS';

function base64Encode(input: string): string {
  let encoder = new util.Base64Helper();
  let bytes = new util.TextEncoder().encode(input);
  return encoder.encodeToString(bytes);
}

Base64 只是编码,不是加密。任何人都能解码,没有任何安全性可言。

cryptoFramework 软件加密

适合中等敏感度数据:用户设置项、非关键业务数据、需要跨设备传输的加密数据。密钥在软件层管理,安全性取决于密钥存储方式。

HUKS 硬件级加密

适合高敏感数据:密码、Token、身份证号、金融信息、健康数据。密钥由 TEE 管理,不可提取。这是目前 HarmonyOS 上你能拿到的最高安全等级。

七、沙箱隔离与 CE/ECE 加密存储区

HarmonyOS 的应用沙箱机制是安全存储的基础。每个应用有自己独立的沙箱目录,应用 A 默认无法访问应用 B 的文件。这个隔离是系统强制的,不需要你做任何额外工作。

沙箱目录结构:

  • context.filesDir:应用私有文件目录

  • context.cacheDir:缓存目录

  • context.tempDir:临时文件目录

  • context.preferencesDir:偏好设置目录

  • context.databaseDir:数据库目录

这些目录在 el2 加密分区下(默认),开机后首次解锁才能访问。

HarmonyOS 按加密强度把沙箱目录分成了四个等级:

等级 说明 适用场景
el1 设备级加密,开机即可访问 闹钟、壁纸、通知
el2 用户级加密,首次解锁后可访问 默认档位,大多数应用数据
el3 文件关闭后锁屏,再次打开需重新解锁 即时通讯消息、邮件
el4 锁屏 10 秒后密钥丢弃,重新解锁才能访问 金融应用、密码管理器

八、RDB 加密数据库:结构化数据的安全存储

如果你的敏感数据是结构化的(比如用户信息表、交易记录表),用文件加密存储解析起来太麻烦,直接用加密的 RDB 数据库是更好的选择。

创建加密数据库只需要在 StoreConfig 里设置 encrypt: true

import { relationalStore } from '@kit.ArkData';
import { common } from '@kit.AbilityKit';

async function createEncryptedDb(context: common.UIAbilityContext): Promise<relationalStore.RdbStore> {
  const STORE_CONFIG: relationalStore.StoreConfig = {
    name: 'SecureApp.db',
    securityLevel: relationalStore.SecurityLevel.S3,
    encrypt: true
  };

  let store = await relationalStore.getRdbStore(context, STORE_CONFIG);

  const CREATE_TABLE_SQL = 'CREATE TABLE IF NOT EXISTS user_credentials ' +
    '(id INTEGER PRIMARY KEY AUTOINCREMENT, ' +
    'username TEXT NOT NULL, ' +
    'encrypted_password TEXT NOT NULL, ' +
    'salt TEXT NOT NULL)';

  await store.executeSql(CREATE_TABLE_SQL);
  return store;
}

几个重要细节:

  • encrypt 参数只在首次创建数据库时生效

  • securityLevel 要和你的数据敏感度匹配

  • 系统默认加密的数据库不支持跨设备打开或卸载重装后打开

九、实战:HUKS + el2 二次加密方案

对于最高敏感度的数据(S4 级别),官方推荐的做法是二次加密:先用 HUKS 在 TEE 内加密数据,再把密文写入 el2 加密目录。两层独立,缺一不可。

async function secureWriteData(
  context: common.UIAbilityContext,
  fileName: string,
  plainData: string
): Promise<void> {
  // 1. 确保 HUKS 密钥存在
  await initSecureKey();

  // 2. 用 HUKS 加密数据
  let iv = cryptoFramework.createRandom().generateRandomSync(12).data;
  let plainBytes = new util.TextEncoder().encode(plainData);
  // ... HUKS 加密操作,得到 cipherData

  // 3. 将 IV + 密文拼接后写入 el2 目录
  let fileData = new Uint8Array(iv.length + cipherData.length);
  fileData.set(iv, 0);
  fileData.set(cipherData, iv.length);

  let filePath = context.filesDir + '/' + fileName;
  let file = fileIo.openSync(filePath, fileIo.OpenMode.CREATE | fileIo.OpenMode.WRITE_ONLY);
  fileIo.writeSync(file.fd, fileData.buffer);
  fileIo.closeSync(file.fd);
}

这段代码做了什么? 明文数据经过 HUKS(在 TEE 内)用 AES-256-GCM 加密,IV 和密文拼接后写入 el2 目录。攻击者就算拿到了文件,面对的是两层加密:HUKS 的 AES-256-GCM 和 el2 的磁盘级加密。密钥在 TEE 里,文件在加密分区里,两把锁缺一把都打不开。

十、常见坑与实操建议

正确做法
IV 硬编码 每次加密随机生成 IV,和密文一起存储
密钥写在代码里 用 HUKS 管理密钥,至少也要用安全的密钥派生方案
Base64 当加密 Base64 只用于数据格式转换,不要当作安全手段
encrypt 参数后改 建库时就想好要不要加密,首次创建就指定
GCM 解密不传 AuthTag 加密时保存 AuthTag,解密时必须传入
HUKS 密钥不判断是否存在 先 isKeyItemExist 检查,不存在再创建
el4 目录后台读写 后台需要持续访问的数据放 el2,别放 el4
RSA 直接加密大文件 大文件用 AES 加密,RSA 只加密 AES 密钥(混合加密)
HUKS session 不 finish 三段式操作必须走完:init → update(可选) → finish

十一、写在最后

安全存储不是一道选择题,而是一道必答题。HarmonyOS 6.0 给了你从软件加密到硬件级密钥管理的完整工具链:cryptoFramework 解决日常加密需求,HUKS 兜底高敏感数据,el2/el4 分级目录做系统层防护,RDB 加密数据库处理结构化数据。工具都在这了,用不用、怎么用,就看你对自己用户数据的态度了。

最后说一句大实话:安全方案没有绝对的安全,只有成本和收益的权衡。HUKS + el2 的二次加密方案已经是目前 HarmonyOS 上你能做到的极限了。别想着自己造轮子搞什么"更安全"的方案,密码学的东西,用经过验证的标准实现比自己瞎折腾靠谱一万倍。

📌 文基于开发者 @Frame Not Work 创作的文章整理,感谢开发者的精彩分享。

👉原文指路:HarmonyOS 6.0 文件加密与安全存储:从哈希到硬件级密钥管理全链路实战

你在 HarmonyOS 安全存储中还遇到过哪些难题?欢迎在评论区留言交流!

Logo

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

更多推荐