1. 项目概述:为什么要在HarmonyNext上做文件加密?

最近在做一个HarmonyNext的POC项目,客户对数据安全的要求非常高,特别是应用内生成和处理的本地文件。他们明确要求,任何存储在设备上的用户数据,都必须经过加密处理,即使设备丢失或应用被逆向,原始数据也不能被轻易获取。这让我把目光投向了基于ArkTS的文件加密与解密系统开发。

你可能觉得,文件加密不就是调用个AES或者RSA的库吗?在Android或iOS上确实有成熟的方案,但在HarmonyNext的ArkTS环境下,情况有些不同。ArkTS作为鸿蒙生态的主力开发语言,它基于TypeScript,运行在方舟运行时(Ark Runtime)上,其安全模型、API设计以及对系统资源的访问方式,都与传统的移动开发有显著差异。你不能直接把Java那套 Cipher 类搬过来用,也不能指望Node.js的 crypto 模块能直接运行。这意味着,我们需要在HarmonyNext的框架约束下,重新思考和构建一套既高效又安全的文件加解密流程。

这个系统的核心价值在于“原生”和“无缝”。它需要深度集成HarmonyNext的文件管理能力( @ohos.file.fs )、安全硬件能力(如果设备支持),并充分利用ArkTS的异步并发特性( async/await , TaskPool )来处理可能的大文件,避免阻塞UI。最终的目标是打造一个模块化、可配置的加密组件,能够被轻松集成到任何需要本地数据保护的HarmonyNext应用中,从笔记App、财务工具到企业内部的文档处理器。

2. 核心思路与架构设计

2.1 技术选型:为何是AES-GCM?

面对加密算法选型,我们主要考虑对称加密。因为文件可能较大,非对称加密如RSA在性能上不适用,通常用于加密对称密钥本身。在对称加密算法中,AES(高级加密标准)是行业黄金标准。

但AES有不同的工作模式,如ECB、CBC、CTR、GCM。ECB模式简单但不安全,相同明文块会产生相同密文块,容易受到攻击。CBC模式更安全,但需要处理初始化向量(IV)和填充(Padding),且是串行计算,不利于并行优化。

我最终选择了 AES-GCM (Galois/Counter Mode)。理由很充分:

  1. 认证加密 :GCM模式同时提供保密性(加密)和完整性(认证)。它在加密的同时会生成一个认证标签(Authentication Tag),解密时会验证该标签,确保密文在传输或存储过程中未被篡改。这比“先加密再计算HMAC”的方案更高效、更不易出错。
  2. 并行化能力 :GCM的计数器(CTR)模式本质支持并行计算,这对于利用ArkTS的 TaskPool 进行大文件分块加密解密有潜在的性能优势。
  3. 官方支持 :从API Version 9开始,HarmonyNext的 @ohos.security.cryptoFramework 密码算法框架明确支持AES-GCM算法。使用官方框架,意味着更好的性能优化和系统兼容性,也避免了引入第三方原生库(Native Library)的复杂性和安全审计风险。

密钥管理上,我们采用“随机生成数据加密密钥(DEK),使用设备或用户密钥加密KEK(Key Encryption Key)来保护DEK”的常见模式。DEK每次加密文件时随机生成,KEK则通过系统密钥库或用户密码派生而来。

2.2 系统架构分层

我们将系统分为清晰的四层,确保职责分离,便于维护和测试:

1. 密钥管理层 (KeyManager) 这是系统的安全基石。负责:

  • 生成强随机数作为数据加密密钥(DEK)。
  • 派生或获取密钥加密密钥(KEK)。例如,从用户输入的密码通过PBKDF2算法派生,或尝试使用系统提供的安全密钥库(如 @ohos.security.huks )来生成和存储KEK。
  • 执行“用KEK加密DEK”和“用KEK解密DEK”的核心操作。
  • 安全地销毁内存中的密钥材料。

2. 加密算法层 (CryptoEngine) 封装 @ohos.security.cryptoFramework 的调用细节,提供干净的异步接口。主要职责:

  • 初始化AES-GCM密码实例,配置密钥、IV(初始化向量)、AAD(附加认证数据)。
  • 执行数据的加密和解密操作( update , doFinal )。
  • 处理加密结果(密文+认证标签)的拼接,以及解密前的数据分离与验证。

3. 文件处理层 (FileProcessor) 负责与文件系统( @ohos.file.fs )打交道,处理IO流。考虑到文件可能很大,必须采用流式处理(Streaming),避免一次性将整个文件读入内存。

  • 实现一个 processFileInChunks 方法,以固定的块大小(如64KB或256KB)读取文件。
  • 对于加密,读取明文块,送入 CryptoEngine 加密,将密文块写入新文件。
  • 对于解密,读取密文块(注意末尾块包含认证标签),送入 CryptoEngine 解密,将明文块写入新文件。
  • 管理文件描述符(fd)的打开、关闭和错误处理。

4. 任务调度与UI交互层 (TaskScheduler) HarmonyNext的UI是单线程的,文件加解密是耗时操作,必须放在后台执行。

  • 使用 TaskPool Worker 将具体的 FileProcessor 任务抛到后台线程。
  • 管理任务状态(排队、进行中、完成、失败)。
  • 通过 Emitter 或状态管理(如 @State )向UI线程发送进度更新和结果通知。
  • 提供取消任务的能力。

3. 核心实现细节与ArkTS特色实践

3.1 密钥的生成与派生

安全始于密钥。在ArkTS中,我们可以使用 cryptoFramework 来生成随机密钥。

import cryptoFramework from '@ohos.security.cryptoFramework';
import util from '@ohos.util';

class KeyManager {
  // 生成一个随机的256位(32字节)AES密钥作为DEK
  async generateRandomAesKey(): Promise<Uint8Array> {
    try {
      let randGenerator = cryptoFramework.createRandom();
      let randomData = await randGenerator.generateRandom(32); // 32字节 = 256位
      return randomData;
    } catch (error) {
      console.error(`[KeyManager] Failed to generate random key: ${JSON.stringify(error)}`);
      throw new Error('Generate random key failed.');
    }
  }

  // 从用户密码派生KEK (使用PBKDF2)
  async deriveKeyFromPassword(password: string, salt: Uint8Array): Promise<Uint8Array> {
    let algName = 'PBKDF2|SHA256'; // 使用PBKDF2算法,HMAC用SHA256
    let genParams: cryptoFramework.PBKDF2Params = {
      algName: algName,
      password: util.encodeUtf8(password), // 密码转Uint8Array
      salt: salt,
      iterations: 100000, // 迭代次数,增加暴力破解成本
      keySize: 256 // 派生出的密钥长度,256位
    };

    let pbkdf2 = cryptoFramework.createKeyAgreement(algName);
    let symKeyGenerator = cryptoFramework.createSymKeyGenerator('AES256');
    let derivedKey = await pbkdf2.generateSecret(genParams);
    // derivedKey是一个SymKey对象,我们需要获取其二进制数据
    let keyData = await derivedKey.getEncoded();
    return keyData;
  }
}

注意 :盐(Salt)必须是随机生成的,并且需要和派生出的KEK一起安全存储(例如,和加密后的DEK放在一起)。迭代次数(iterations)应根据设备性能调整,在安全性和用户体验间取得平衡,10万次是一个当前合理的起点。

3.2 使用cryptoFramework进行AES-GCM加密解密

这是最核心的加密操作。我们需要正确设置GCM模式的参数。

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

class CryptoEngine {
  private static readonly ALG_NAME = 'AES256|GCM|PKCS7'; // 算法名:256位AES,GCM模式,PKCS7填充(GCM通常不需要填充,但框架参数如此)

  // 加密一段数据
  async encryptData(plainData: Uint8Array, key: Uint8Array, iv: Uint8Array, aad?: Uint8Array): Promise<{cipherData: Uint8Array, authTag: Uint8Array}> {
    let cipherData: Uint8Array = new Uint8Array(0);
    let authTag: Uint8Array = new Uint8Array(0);

    try {
      // 1. 创建对称密钥生成器,并转换密钥材料为SymKey对象
      let symKeyGenerator = cryptoFramework.createSymKeyGenerator('AES256');
      let symKey: cryptoFramework.SymKey = await symKeyGenerator.convertKey(key);

      // 2. 创建加密器并设置参数
      let transformer = cryptoFramework.createCipher(this.ALG_NAME);
      let gcmParams: cryptoFramework.GcmParams = {
        algName: 'GcmParams',
        iv: iv, // 初始化向量,必须唯一,通常随机生成12字节
        aad: aad, // 附加认证数据,可为空
        authTagLen: 16 // 认证标签长度,16字节(128位)是GCM的常用值
      };
      await transformer.init(cryptoFramework.CryptoMode.ENCRYPT_MODE, symKey, gcmParams);

      // 3. 分段更新(本例一次处理,流式处理见文件层)
      let updateOutput = await transformer.update(plainData);
      let finalOutput = await transformer.doFinal(null); // doFinal生成最终的密文和认证标签

      // 4. 组装结果
      cipherData = this.concatUint8Arrays(updateOutput, finalOutput.data);
      authTag = finalOutput.authTag!; // 认证标签

      return { cipherData, authTag };
    } catch (error) {
      console.error(`[CryptoEngine] Encrypt failed: ${JSON.stringify(error)}`);
      throw error;
    }
  }

  // 解密并验证
  async decryptData(cipherDataWithTag: Uint8Array, key: Uint8Array, iv: Uint8Array, authTag: Uint8Array, aad?: Uint8Array): Promise<Uint8Array> {
    try {
      // ... 类似加密过程,创建SymKey和GcmParams ...
      let symKeyGenerator = cryptoFramework.createSymKeyGenerator('AES256');
      let symKey: cryptoFramework.SymKey = await symKeyGenerator.convertKey(key);

      let transformer = cryptoFramework.createCipher(this.ALG_NAME);
      let gcmParams: cryptoFramework.GcmParams = {
        algName: 'GcmParams',
        iv: iv,
        aad: aad,
        authTagLen: authTag.length
      };
      // 注意这里是DECRYPT_MODE
      await transformer.init(cryptoFramework.CryptoMode.DECRYPT_MODE, symKey, gcmParams);

      // 假设cipherDataWithTag是密文(不含标签)
      let updateOutput = await transformer.update(cipherDataWithTag);
      // doFinal时需要传入认证标签进行验证
      let finalOutput = await transformer.doFinal(authTag);

      // 如果验证失败,doFinal会抛出错误
      let plainData = this.concatUint8Arrays(updateOutput, finalOutput.data);
      return plainData;
    } catch (error) {
      // 特别处理认证失败错误
      if (error?.code === 200) { // 假设200是认证失败的错误码,需查阅官方文档确认
        throw new Error('Decryption failed: Authentication tag verification failed. The data may be corrupted or tampered with.');
      }
      console.error(`[CryptoEngine] Decrypt failed: ${JSON.stringify(error)}`);
      throw error;
    }
  }

  private concatUint8Arrays(arr1: Uint8Array, arr2: Uint8Array): Uint8Array {
    let result = new Uint8Array(arr1.length + arr2.length);
    result.set(arr1, 0);
    result.set(arr2, arr1.length);
    return result;
  }
}

实操心得 cryptoFramework 的API是异步的(返回Promise),这很符合ArkTS的编程模型。在 doFinal 时,GCM模式会返回 authTag (加密时)或验证它(解密时)。解密时如果认证标签不匹配,会抛出异常,这是GCM模式提供数据完整性保护的关键体现,务必在UI层做好相应的错误提示(如“文件可能已损坏”)。

3.3 大文件的流式分块处理

这是性能的关键。绝不能把整个文件读进内存。

import fs from '@ohos.file.fs';
import common from '@ohos.app.ability.common';

class FileProcessor {
  private context: common.UIAbilityContext;

  constructor(context: common.UIAbilityContext) {
    this.context = context;
  }

  async encryptFile(sourcePath: string, destPath: string, key: Uint8Array, iv: Uint8Array): Promise<void> {
    let srcFd: number | undefined;
    let dstFd: number | undefined;
    const CHUNK_SIZE = 64 * 1024; // 64KB 块大小

    try {
      // 1. 打开源文件(只读)和目标文件(创建、只写)
      srcFd = await fs.open(sourcePath, fs.OpenMode.READ_ONLY);
      dstFd = await fs.open(destPath, fs.OpenMode.CREATE | fs.OpenMode.READ_WRITE);

      // 2. 在目标文件头部写入IV和可能的其他元数据(如加密版本、算法标识)
      // 例如,先写入12字节的IV
      await fs.write(dstFd, iv.buffer);

      let cryptoEngine = new CryptoEngine();
      let totalBytes = (await fs.stat(sourcePath)).size;
      let processedBytes = 0;
      let buffer = new ArrayBuffer(CHUNK_SIZE);

      // 3. 循环读取、加密、写入
      while (processedBytes < totalBytes) {
        let readOptions = {
          offset: processedBytes,
          length: CHUNK_SIZE
        };
        let readResult = await fs.read(srcFd, buffer, readOptions);
        let bytesRead = readResult.bytesRead;
        if (bytesRead <= 0) break;

        let chunkData = new Uint8Array(buffer, 0, bytesRead);
        // 加密当前块
        let { cipherData, authTag } = await cryptoEngine.encryptData(chunkData, key, iv);
        // 注意:GCM模式每个块使用相同的IV,但计数器不同。对于文件流,更常见的做法是整体加密,或使用分段加密但需要更复杂的IV管理。
        // 简化方案:将整个文件视为一个数据流进行加密。但cryptoFramework.update是支持分段的。
        // 这里演示分段更新,但需要保存最后的authTag。实际中,对于大文件,更推荐使用“生成随机DEK -> 用GCM加密整个文件 -> 存储(IV, authTag, 密文)”的流程。
        // 以下代码调整为模拟流式加密(实际cryptoFramework.update支持流式)

        // 写入加密后的数据块
        await fs.write(dstFd, cipherData.buffer);

        processedBytes += bytesRead;
        // 通知UI进度 (通过Emitter)
        // this.emitProgress(processedBytes / totalBytes);
      }

      // 4. 循环结束后,获取最终的authTag并写入文件末尾
      // 注意:上述循环中的encryptData调用是独立的,不适合流式GCM。正确做法是:
      // let cipher = createCipher... init...
      // while(read chunk){ await cipher.update(chunk); }
      // let finalOutput = await cipher.doFinal(null);
      // authTag = finalOutput.authTag;
      // 然后将所有update的输出和finalOutput.data写入文件,最后追加authTag。
      // 由于篇幅,此处指出关键点:必须使用同一个cipher实例进行连续的update。

      console.info(`[FileProcessor] File encrypted successfully.`);
    } catch (error) {
      console.error(`[FileProcessor] Encryption failed: ${JSON.stringify(error)}`);
      // 如果失败,尝试删除可能已部分创建的目标文件
      try { if (dstFd !== undefined) await fs.unlink(destPath); } catch (e) {}
      throw error;
    } finally {
      // 5. 确保关闭文件描述符
      if (srcFd !== undefined) await fs.close(srcFd);
      if (dstFd !== undefined) await fs.close(dstFd);
    }
  }
}

踩坑记录 :HarmonyNext的文件API( @ohos.file.fs )同样是异步Promise-based。 fs.read fs.write offset 参数对于随机访问至关重要。在处理流式加密时,最大的陷阱是 错误地复用IV 。对于AES-GCM,同一个密钥下,每个加密操作的IV必须唯一。通常我们为 每个文件 生成一个随机IV,并将其存储在加密文件的开头。在流式处理(分块update)中,使用的是同一个cipher实例,所以IV在init时设置一次即可,框架内部会处理计数器的递增。千万不要对每个数据块都调用一次 init

3.4 使用TaskPool进行后台任务调度

UI流畅性至关重要。

import taskpool from '@ohos.taskpool';
import emitter from '@ohos.events.emitter';

@Concurrent
function encryptFileTask(input: taskpool.TaskMessage): void {
  // 注意:@Concurrent函数参数和返回值必须可序列化(如基本类型、普通对象)
  let { sourcePath, destPath, keyBase64, ivBase64 } = input as { sourcePath: string, destPath: string, keyBase64: string, ivBase64: string };
  // 将base64的key和iv转回Uint8Array
  let key = Uint8Array.from(atob(keyBase64), c => c.charCodeAt(0));
  let iv = Uint8Array.from(atob(ivBase64), c => c.charCodeAt(0));

  // 这里需要能访问到UIAbilityContext?不行,@Concurrent函数不能直接访问UI资源。
  // 因此,FileProcessor需要重构,使其核心逻辑不依赖context,或者通过其他方式获取文件路径权限。
  // 一种方法是在启动Task前,在UI线程用context获取文件的真实路径(如`getFilesDir()`里的路径),然后将这个绝对路径传给Task。
  console.info(`[encryptFileTask] Starting encryption in background.`);
  // 模拟耗时操作
  // ... 调用一个不依赖context的纯逻辑加密函数 ...
  let success = true; // 假设成功
  return { success, destPath };
}

class TaskScheduler {
  async startEncryptionTask(sourceUri: string, key: Uint8Array, iv: Uint8Array): Promise<void> {
    // 1. 在UI线程准备任务数据
    let keyBase64 = btoa(String.fromCharCode(...key));
    let ivBase64 = btoa(String.fromCharCode(...iv));
    // 获取源文件和目标文件的绝对路径(这里需要用到context,在UI线程完成)
    // let sourcePath = ...;
    // let destPath = ...;

    let taskMessage: taskpool.TaskMessage = {
      sourcePath: sourcePath,
      destPath: destPath,
      keyBase64: keyBase64,
      ivBase64: ivBase64
    };

    // 2. 创建并执行任务
    let task = new taskpool.Task(encryptFileTask, taskMessage);
    try {
      let result = await taskpool.execute(task) as { success: boolean, destPath: string };
      // 3. 任务完成,发送事件到UI线程
      let eventData = {
        data: {
          taskId: 'encrypt_001',
          success: result.success,
          message: result.success ? `文件已加密保存至:${result.destPath}` : '加密失败',
          resultPath: result.destPath
        }
      };
      emitter.emit({ eventId: 1 }, eventData); // 假设事件ID 1 表示加密任务完成
    } catch (error) {
      console.error(`[TaskScheduler] Task execution failed: ${JSON.stringify(error)}`);
      let eventData = {
        data: {
          taskId: 'encrypt_001',
          success: false,
          message: `后台任务执行异常:${error.message}`
        }
      };
      emitter.emit({ eventId: 1 }, eventData);
    }
  }
}

注意事项 @Concurrent 装饰的函数运行在独立的Worker线程中,不能直接访问UI组件、 AppStorage UIAbilityContext 。所有需要的数据必须在任务开始前通过 TaskMessage 传递进去,且必须是可序列化的(不支持函数、类实例)。结果也需要通过可序列化的方式返回。与UI的通信推荐使用 Emitter 。另外,文件路径需要使用应用沙箱内的绝对路径,对 data:// file:// 协议的URI需要进行转换,这部分操作应在UI线程完成后再将路径字符串传递给任务。

4. 常见问题、性能优化与安全加固

4.1 开发与调试中的典型问题

问题1: cryptoFramework 相关API调用报错“error code 401”

  • 排查 :这通常是参数错误。检查算法名称字符串是否完全正确(如 'AES256|GCM|PKCS7' ),密钥长度是否匹配(AES256需要32字节密钥),IV长度是否合适(GCM推荐12字节)。确保传入的 Uint8Array 是有效的,没有为 null undefined
  • 解决 :仔细对照官方文档 《密码算法库》 的API说明。使用 try...catch 包裹并打印详细的错误信息。

问题2:大文件加密时内存占用过高或应用崩溃

  • 排查 :检查是否一次性将整个文件读入了内存(例如使用 fs.readText )。流式处理没有正确分块,或者块大小( CHUNK_SIZE )设置得过大(比如超过1MB)。
  • 解决 :严格采用流式分块处理。将 CHUNK_SIZE 调整为64KB或128KB,在内存占用和IO效率间取得平衡。确保在 finally 块中关闭所有文件描述符。

问题3:解密时认证失败(Authentication tag mismatch)

  • 排查 :这是GCM模式的核心安全特性。可能原因有:
    1. 加密和解密使用的 密钥不一致
    2. 加密和解密使用的 IV不一致
    3. 加密文件在存储后被 篡改 部分损坏
    4. 加密时使用的 AAD 和解密时提供的 AAD 不一致。
    5. 文件读写过程中, 密文数据或认证标签的拼接/拆分逻辑有误 ,导致解密时数据对不上。
  • 解决 :确保密钥和IV被正确保存和还原。检查文件IO逻辑,确保写入和读取的字节序列完全一致。对于AAD,如果加密时使用了,解密时必须提供相同的值。在调试时,可以先对一个固定的小字符串进行加密解密测试,验证基础流程是否正确。

问题4:后台任务不执行或无法更新UI进度

  • 排查 @Concurrent 函数内部不能直接修改 @State 变量或调用UI方法。
  • 解决 :使用 Emitter 进行线程间通信。在UI组件中订阅事件,在 @Concurrent 函数内通过 taskpool.Task 的返回值传递状态,或者通过 Emitter 发送进度事件。确保事件ID定义一致。

4.2 性能优化建议

  1. 调整块大小 CHUNK_SIZE 是性能关键。太小会导致频繁的IO和加密上下文切换开销;太大会增加内存压力和单次操作延迟。建议通过基准测试,在目标设备上测试从32KB到256KB不同块大小的性能,找到甜点。
  2. 利用TaskPool并行处理 :如果设备性能足够强,且加密大量小文件,可以考虑使用 TaskPool 并行加密多个文件。但注意并发任务数不宜过多,避免过度竞争系统资源。
  3. 密钥缓存 :对于同一个会话中需要多次加密的操作,可以安全地缓存数据加密密钥(DEK),避免重复的密钥生成或派生操作。但务必在应用退出或会话结束时从内存中彻底清除。
  4. 进度反馈优化 :频繁的进度更新(如每块都通知)会导致大量UI线程事件,可能卡顿。可以设定一个阈值(如每处理1MB数据或进度增加1%才通知一次),进行节流(throttle)处理。

4.3 安全加固要点

  1. 密钥生命周期管理
    • 内存安全 :密钥在内存中时,使用后尽快用随机数据覆盖(如 key.fill(0) )。虽然JavaScript/ArkTS的垃圾回收机制使得完全控制内存擦除比较困难,但这是一个良好的安全习惯。
    • 存储安全 :KEK或加密后的DEK应存储在系统提供的安全存储中,如HarmonyOS的 密钥管理服务 @ohos.security.huks )。绝对避免以明文形式存储在 Preferences 或普通文件中。
  2. IV的唯一性 :确保每次加密操作都使用 密码学安全的随机数生成器 (CSPRNG)生成新的IV。重复使用(Key, IV)对在GCM模式下是灾难性的,会完全破坏安全性。
  3. 算法与参数 :坚持使用AES-256-GCM。认证标签长度使用128位(16字节)。迭代次数(PBKDF2)不低于10万次。这些参数是当前业界公认的安全底线。
  4. 错误处理 :解密失败时,返回通用的错误信息(如“解密失败”),避免泄露具体细节(如“密钥错误”还是“认证标签错误”),这可以防止基于侧信道的信息泄露。
  5. 代码混淆与加固 :发布应用前,使用DevEco Studio提供的代码混淆和加固功能,增加逆向工程分析核心加密逻辑的难度。

5. 进阶扩展与生态集成

一个基础的加密模块完成后,可以考虑将其扩展为更通用的解决方案:

1. 开发一个 FileCrypto 通用组件 将上述密钥管理、加密引擎、文件处理层封装成一个ArkUI组件或一个独立的HarmonyOS库( .har )。对外提供简洁的API,如 FileCrypto.encrypt(sourceUri, options) FileCrypto.decrypt(encryptedUri, password) 。这样可以复用在所有你的HarmonyNext项目中。

2. 集成系统密钥库(HUKS) 对于高安全场景,探索使用 @ohos.security.huks 。可以将KEK存储在由系统TEE(可信执行环境)保护的安全密钥库中,加密解密操作在安全环境中进行,密钥材料永不暴露给应用层。这能极大提升密钥存储的安全性。

3. 支持多种加密模式和算法 CryptoEngine 中设计一个算法接口,未来可以轻松扩展支持 ChaCha20-Poly1305 等其他认证加密算法,或者为了兼容旧数据而支持 AES-CBC + HMAC-SHA256 的模式。

4. 实现加密文件格式 定义你自己的加密文件格式。例如,文件头可以包含:魔数(标识你的格式)、版本号、加密算法标识、IV、加密后的DEK(用KEK加密)、AAD长度等信息,然后是密文正文,最后是认证标签。这使你的加密文件自成一体,包含了解密所需的所有元数据(除了KEK)。

5. 性能监控与日志 在关键路径加入轻量级的性能打点,记录加密解密不同大小文件所需的时间。这有助于在实际部署后监控性能,并为用户提供更准确的进度预估。日志要谨慎,绝不能记录密钥、IV等敏感信息。

整个项目走下来,最大的体会是,在HarmonyNext的ArkTS环境下开发安全功能,既要遵循通用的密码学最佳实践,又要深刻理解其异步并发模型、线程限制以及特有的API设计。从 cryptoFramework 的异步调用,到 TaskPool 的后台任务,再到 fs 的文件流操作,每一环都需要仔细处理。成功实现后,这个加密解密系统不仅能成为你应用的安全盾牌,更能让你对HarmonyNext的系统能力有一个更深层次的掌握。

Logo

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

更多推荐