引言

在现代应用开发中,数据安全是一个不可回避的话题。无论是用户密码的存储、敏感数据的传输,还是接口参数的签名验证,都离不开密码学算法的支撑。HarmonyOS NEXT 通过 @ohos.security.cryptoFramework 模块为开发者提供了一套完整的密码学能力,涵盖消息摘要(Hash)、对称加密(AES)、消息认证码(HMAC)、非对称加密(RSA)、数字签名等全方位的安全功能。

本文将以一个密码学工具箱的实战 Demo 为线索,深入讲解 cryptoFramework 的核心 API 和使用模式。与上一篇文章介绍的 zlib 压缩引擎类似,cryptoFramework 也遵循"创建实例 → 初始化 → 更新数据 → 获取结果"的管道式操作模式,但它在算法丰富度和安全场景覆盖面上要广阔得多。

读完本文,你将掌握:

  • 消息摘要(MD5/SHA) 的计算方法及7种算法对比
  • AES 对称加密 的加密/解密流程及5种模式切换
  • HMAC 消息认证码 的生成与验证
  • DataBlob 数据容器 在密码学 API 中的核心作用
  • 同步 vs 异步 API 的选择策略

环境与权限

cryptoFramework 属于 CryptoArchitectureKit 工具包,自 API 9 开始提供,API 11 起支持跨平台,API 12 起支持原子化服务。本文基于 API 24 (HarmonyOS NEXT) 编写,使用同步 API(xxxSync 系列方法)。

cryptoFramework 不需要申请任何权限,直接导入即可使用:

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

这是密码学 API 相比传感器、蓝牙等功能模块的一大优势——零权限门槛,开箱即用。

核心数据结构:DataBlob

在深入各个算法之前,必须先理解 cryptoFramework 的通用数据容器——DataBlob。它是整个密码学框架的数据交换基础,所有输入输出都通过它来传递。

interface DataBlob {
  data: Uint8Array;
}

DataBlob 本质上是 Uint8Array 的包装。字符串与 DataBlob 之间的转换是所有操作的第一步。我们使用 @kit.ArkTS 中的 util.TextEncoder / util.TextDecoder 来完成:

import { util } from '@kit.ArkTS';

// 字符串 → Uint8Array → DataBlob
function stringToUint8(str: string): Uint8Array {
  let encoder = new util.TextEncoder();
  return encoder.encodeInto(str);
}

// DataBlob → Uint8Array → 字符串
function uint8ToString(buf: Uint8Array): string {
  let decoder = new util.TextDecoder();
  return decoder.decodeToString(buf);
}

实际使用时只需将 Uint8Array 放入对象字面量即可创建 DataBlob:

let dataBlob: cryptoFramework.DataBlob = { data: this.stringToUint8('Hello HarmonyOS') };

一、消息摘要(Hash):MD5、SHA 系列与 SM3

消息摘要算法(又称哈希算法)将任意长度的数据映射为固定长度的"指纹"。cryptoFramework 支持 7 种摘要算法:

算法 摘要长度 安全性 典型用途
MD5 128 bit (16 B) 已破解,不推荐安全场景 文件完整性校验、非安全去重
SHA-1 160 bit (20 B) 已弱化 遗留系统兼容
SHA-224 224 bit (28 B) 中等 资源受限场景
SHA-256 256 bit (32 B) 区块链、证书、签名
SHA-384 384 bit (48 B) 很高 高安全场景
SHA-512 512 bit (64 B) 最高 高安全场景、HMAC
SM3 256 bit (32 B) 国密标准,政务金融

同步 Hash 计算(三步走)

cryptoFramework 的 Hash 流程遵循统一的"创建 → 更新 → 摘要"三步模式:

// 步骤1:创建消息摘要实例
let md: cryptoFramework.Md = cryptoFramework.createMd('SHA256');

// 步骤2:更新数据(可多次调用处理大块数据)
md.updateSync({ data: this.stringToUint8('Hello HarmonyOS!') });

// 步骤3:获取摘要结果
let result: cryptoFramework.DataBlob = md.digestSync();

// result.data 是 Uint8Array,转为十六进制字符串展示
let hexString = this.bufToHex(result.data);
// 输出:32字节的SHA-256摘要

Uint8Array → 十六进制字符串

摘要结果是一段二进制数据,展示时通常转为十六进制:

bufToHex(buf: Uint8Array): string {
  let sb = '';
  for (let i = 0; i < buf.length; i++) {
    let h = (buf[i] & 0xFF).toString(16);
    if (h.length === 1) sb += '0';
    sb += h;
  }
  return sb;
}

批量对比:7种算法一键测试

Demo 中提供了"批量对比"功能,对同一段文本依次运行 7 种算法,同时记录每种算法的输出长度和计算耗时,让开发者直观感受不同算法的特点:

batchHash(): void {
  let inputData = this.stringToUint8(this.textInput);
  for (let a = 0; a < this.algos.length; a++) {
    let algo = this.algos[a];
    let md: cryptoFramework.Md = cryptoFramework.createMd(algo);
    let start = Date.now();
    md.updateSync({ data: inputData });
    let result: cryptoFramework.DataBlob = md.digestSync();
    let elapsed = Date.now() - start;
    // 记录算法名、耗时、摘要长度...
  }
}

Md 接口完整方法列表

interface Md {
  update(input: DataBlob, callback: AsyncCallback<void>): void;   // 异步更新
  update(input: DataBlob): Promise<void>;                          // Promise更新
  updateSync(input: DataBlob): void;                               // 同步更新(推荐)
  digest(callback: AsyncCallback<DataBlob>): void;                 // 异步摘要
  digest(): Promise<DataBlob>;                                     // Promise摘要
  digestSync(): DataBlob;                                          // 同步摘要(推荐)
  getMdLength(): number;                                           // 获取摘要长度
}

对于 Demo 场景,同步 API 代码更简洁。生产环境中处理大文件(如几百MB的视频),建议使用异步 Promise 版以避免阻塞主线程。

二、AES 对称加密:5种模式的加密/解密往返

cryptoFramework 的 Cipher(密码器)模块提供完整的对称加密能力。支持的对称算法包括 AES128、AES192、AES256,以及国密 SM4。本文 Demo 聚焦最常用的 AES,提供 5 种配置模式:

模式 transformation 参数 密钥长度 填充方式
AES128-ECB AES128|ECB|PKCS7 16 B PKCS7
AES128-CBC AES128|CBC|PKCS7 16 B PKCS7
AES128-CTR AES128|CTR|NoPadding 16 B 无填充(流式)
AES192-ECB AES192|ECB|PKCS7 24 B PKCS7
AES256-ECB AES256|ECB|PKCS7 32 B PKCS7

transformation 参数的格式为 算法|模式|填充方式,三个部分用 | 连接。

加密/解密六步流程

完整的 AES 加解密往返包含 6 个核心步骤:

aesEncryptDecrypt(): void {
  let transformation = 'AES128|ECB|PKCS7';  // 算法|模式|填充
  let keyStr = this.keyInput;

  // 1. 准备密钥(将字符串填充/截断到算法需要的长度)
  let keyBuf = this.stringToUint8(keyStr);
  let paddedKey = new Uint8Array(16); // AES128 = 16 bytes
  for (let i = 0; i < 16; i++) {
    paddedKey[i] = keyBuf[i % keyBuf.length];
  }

  // 2. 创建对称密钥生成器
  let symKeyGen: cryptoFramework.SymKeyGenerator =
    cryptoFramework.createSymKeyGenerator('AES128');

  // 3. 从字节数据转换为 SymKey 对象
  let symKey: cryptoFramework.SymKey =
    symKeyGen.convertKeySync({ data: paddedKey });

  // 4. 创建 Cipher 并初始化为加密模式
  let cipher: cryptoFramework.Cipher = cryptoFramework.createCipher(transformation);
  let iv: cryptoFramework.IvParamsSpec = {
    algName: 'IvParamsSpec',
    iv: { data: paddedKey }     // ECB模式不需要IV,为统一接口仍传入
  };
  cipher.initSync(cryptoFramework.CryptoMode.ENCRYPT_MODE, symKey, iv);

  // 5. 执行加密(doFinal = 完成并输出)
  let cipherResult: cryptoFramework.DataBlob =
    cipher.doFinalSync({ data: this.stringToUint8(this.textInput) });

  // 6. 重新初始化并解密,验证往返正确性
  let decipher: cryptoFramework.Cipher = cryptoFramework.createCipher(transformation);
  decipher.initSync(cryptoFramework.CryptoMode.DECRYPT_MODE, symKey, iv);
  let plainResult: cryptoFramework.DataBlob =
    decipher.doFinalSync({ data: cipherResult.data });

  let roundtrip = (this.uint8ToString(plainResult.data) === this.textInput);
  // roundtrip === true 说明加解密往返正确
}

在这里插入图片描述
在这里插入图片描述

关键要点

1) CryptoMode 枚举

enum CryptoMode {
  ENCRYPT_MODE,  // 加密模式
  DECRYPT_MODE   // 解密模式
}

同一个 Cipher 实例在 doFinal 之后不可重复使用。如果需要解密,必须创建新的 Cipher 实例,并以 DECRYPT_MODE 重新 init。

2) IV(初始化向量)参数

CBC/CTR 等模式使用 IV 增加随机性,保证同一明文每次产生不同密文。ECB 模式不使用 IV,但 initSync 的第三个参数 params 在 ECB 模式下可传入 null。Demo 中为统一接口风格,对 ECB 也传入了一个占位 IV。

3) 密钥长度与算法的对应

  • AES128 → 16 bytes (128 bits)
  • AES192 → 24 bytes (192 bits)
  • AES256 → 32 bytes (256 bits)

Demo 中自动从 transformation 字符串中提取密钥长度要求,并用取模循环方式填充用户输入的任意长度密钥:

let keyAlg = transformation.split('|')[0]; // 'AES128'
let keyBytesNeeded = parseInt(keyAlg.substring(3)) / 8; // 128/8 = 16
let paddedKey = new Uint8Array(keyBytesNeeded);
for (let i = 0; i < keyBytesNeeded; i++) {
  paddedKey[i] = keyBuf[i % keyBuf.length];
}

三、HMAC 消息认证码:带密钥的哈希

HMAC(Hash-based Message Authentication Code)在哈希算法的基础上引入密钥,同时验证数据的完整性真实性。接收方只有持相同密钥才能生成一致的 HMAC 值,有效防止中间人篡改。

cryptoFramework 为 HMAC 提供了与 Md 完全一致的三步式 API:

computeHmac(): void {
  // 1. 准备对称密钥(HMAC需要共享密钥)
  let keyBuf = this.stringToUint8(this.keyInput);
  let padded = new Uint8Array(16);
  for (let i = 0; i < 16; i++) { padded[i] = keyBuf[i % keyBuf.length]; }

  let symKeyGen: cryptoFramework.SymKeyGenerator =
    cryptoFramework.createSymKeyGenerator('AES128');
  let symKey: cryptoFramework.SymKey =
    symKeyGen.convertKeySync({ data: padded });

  // 2. 创建Mac实例并初始化
  let mac: cryptoFramework.Mac = cryptoFramework.createMac('HMAC|SHA256');
  mac.initSync(symKey);

  // 3. 更新数据并获取结果
  mac.updateSync({ data: this.stringToUint8(this.textInput) });
  let macResult: cryptoFramework.DataBlob = mac.doFinalSync();

  this.hmacOutput = this.bufToHex(macResult.data);
}

HMAC 的算法名称格式为 HMAC|摘要算法,例如:

  • HMAC|SHA1
  • HMAC|SHA256
  • HMAC|SHA384
  • HMAC|SHA512

四、API 全景概览

cryptoFramework 的模块结构层次分明,下图展示了核心类与创建函数的关系:

cryptoFramework (namespace)
├── createMd(algName) ──────────────→ Md ──→ updateSync() + digestSync()
├── createMac(algName) ─────────────→ Mac ─→ initSync() + updateSync() + doFinalSync()
├── createCipher(transformation) ───→ Cipher → initSync() + doFinalSync()
├── createSymKeyGenerator(algName) ─→ SymKeyGenerator ─→ generateSymKeySync() + convertKeySync()
├── createRandom() ─────────────────→ Random ─→ generateRandomSync()
├── createSign(algName) ────────────→ Sign ──→ initSync() + signSync()
├── createVerify(algName) ──────────→ Verify → initSync() + verifySync()
├── createKeyAgreement(algName) ────→ KeyAgreement ─→ generateSecretSync()
└── createAsyKeyGenerator(algName) ─→ AsyKeyGenerator ─→ generateKeyPairSync()

本文 Demo 聚焦最常用的前 4 个模块(Md、Cipher、Mac、SymKeyGenerator),它们已经覆盖了日常开发 90% 的密码学需求。

五、实战 Demo:密码学工具箱

页面结构

Demo 页面 CryptoFrameworkPage.ets 采用"输入区 + 三大功能面板 + 历史记录"的卡片式布局:

密码学工具箱
├── 状态栏 — 实时反馈当前操作结果
├── 输入区
│   ├── 文本输入框(3000字上限)
│   └── AES密钥输入框(自动填充至所需长度)
├── 消息摘要 (Hash) 面板
│   ├── 7种算法横向选择(MD5/SHA1/SHA224/SHA256/SHA384/SHA512/SM3)
│   ├── 计算Hash 按钮 — 对输入文本执行选中算法
│   ├── 批量对比 按钮 — 对同一输入执行全部7种算法
│   └── 结果展示区 — 十六进制摘要 + 耗时
├── AES 对称加密面板
│   ├── 5种模式选择(AES128-ECB/CBC/CTR, AES192-ECB, AES256-ECB)
│   ├── 加密并解密 按钮 — 执行完整的加密→解密往返
│   ├── 密文展示(hex格式,前40字符预览)
│   └── 解密验证结果(对比原文,显示"通过/失败")
├── HMAC 消息认证码面板
│   ├── 计算 HMAC-SHA256 按钮
│   └── MAC值展示(hex格式)
├── 摘要/认证记录 — 最近8次Hash和HMAC操作
├── 加解密记录 — 最近8次AES操作及往返验证状态
└── 核心API参考 — 12个关键API速查表

4 个交互点

  1. 算法选择与单次Hash — 点击7种算法标签切换,输入文本后计算Hash,实时显示摘要和耗时
  2. 批量算法对比 — 一键对同一文本执行全部7种算法,记录在历史面板中直观对比摘要长度和速度
  3. AES加解密往返 — 选择5种模式之一点击加密,自动执行解密并验证往返正确性(对比原文)
  4. HMAC认证码生成 — 用密钥对输入文本生成MAC,展示键控哈希与普通哈希的区别

核心代码位置

完整代码在 dev/entry/src/main/ets/pages/CryptoFrameworkPage.ets(约 380 行),路由已注册为 pages/CryptoFrameworkPage

六、同步 vs 异步 API 的选择

cryptoFramework 为每个操作提供了 3 种调用方式:Callback 异步、Promise 异步、同步(Sync)。以 Md.digest 为例:

// 方式1:Callback 异步
md.digest((err, result) => {
  if (err) { console.error('digest failed'); return; }
  let hex = bufToHex(result.data);
});

// 方式2:Promise 异步
md.digest().then((result) => {
  let hex = bufToHex(result.data);
}).catch((err) => { console.error('digest failed'); });

// 方式3:同步(Demo 推荐)
let result: cryptoFramework.DataBlob = md.digestSync();
let hex = bufToHex(result.data);

选择策略:

场景 推荐方式 原因
小数据(< 1 KB) 同步 Sync 代码简洁,阻塞时间可忽略
中等数据(1 KB - 1 MB) Promise 异步 避免UI卡顿,保持响应
大数据(> 1 MB) Promise 异步 + update 分块 利用 update 逐块处理,内存友好
Worker 线程 同步 Sync Worker 不阻塞主线程,Sync 更简单

七、常见问题与排查

1. “algName is invalid” 错误

算法名称字符串区分大小写且不能有额外空格。标准算法名称为:

正确: 'SHA256', 'AES128|ECB|PKCS7', 'HMAC|SHA256'
错误: 'sha256', 'AES128|ECB|PKCS7 '(尾部有空格)

2. doFinalSync 只能调用一次

每个 Md/Cipher/Mac 实例的 doFinalSync / digestSync 只能调用一次。如果需要重复计算,必须创建新实例:

// 错误 —— 重复使用同一个Md
let md = cryptoFramework.createMd('SHA256');
md.digestSync();
md.digestSync(); // 抛出异常!

// 正确 —— 每次创建新实例
let md1 = cryptoFramework.createMd('SHA256');
md1.updateSync({ data: data1 });
let result1 = md1.digestSync();

let md2 = cryptoFramework.createMd('SHA256');
md2.updateSync({ data: data2 });
let result2 = md2.digestSync();

3. AES 加解密往返验证失败

如果解密后的明文与原文本不一致,检查以下几点:

  • 加密和解密使用的 transformation 参数是否完全一致
  • 加密和解密的 IV 是否一致
  • 密钥是否一致(是否被意外截断或补零)
  • 密文在传输过程中是否被修改(如错误的反序列化)

4. cipher.init: params is null

ECB/CTR 等不需要 IV 的模式,initSync 的第三个参数 params 可传入 null。但对于 CBC 模式,必须提供 IvParamsSpec

八、安全最佳实践

1. 密码存储:绝不用 MD5

MD5 和 SHA-1 已被证明存在碰撞漏洞。存储用户密码时,应使用 SHA-256 + 随机盐值 或专门的密钥派生函数。

// 不安全的密码存储
let hash = cryptoFramework.createMd('MD5');
hash.updateSync({ data: passwordData });
let result = hash.digestSync(); // MD5 易碰撞

// 更好的做法(简化示意)
let hash = cryptoFramework.createMd('SHA256');
let salt = cryptoFramework.createRandom().generateRandomSync(16); // 随机盐
let combined = new Uint8Array(salt.data.length + passwordData.data.length);
combined.set(salt.data, 0);
combined.set(passwordData.data, salt.data.length);
hash.updateSync({ data: combined });
let result = hash.digestSync();
// 存储 salt + result

2. IV 必须随机且不重复

CBC 模式下,每次加密都应使用新的随机 IV。重复使用相同 IV 会泄露明文模式:

let random = cryptoFramework.createRandom();
let iv = random.generateRandomSync(16); // 16字节随机IV

3. ECB 模式的局限性

ECB 模式下相同的明文块产生相同的密文块,可能泄露数据模式。对于包含重复结构的数据(如 JSON、XML),应优先使用 CBC 或 CTR 模式。

4. 密钥管理

Demo 中的密钥输入仅供演示。生产环境中,密钥应:

  • 使用 cryptoFramework.createRandom().generateRandomSync() 生成
  • 存储在系统安全区域(如 HUKS 密钥管理服务)
  • 绝不明文硬编码在代码中
  • 使用后及时从内存清除

九、与其它平台对比

如果你有 Android 或 iOS 开发经验,以下对照表可帮助理解:

功能 HarmonyOS cryptoFramework Android javax.crypto iOS CryptoKit
消息摘要 createMd('SHA256').digestSync() MessageDigest.getInstance("SHA-256").digest() SHA256.hash(data:)
AES加密 createCipher('AES128|CBC|PKCS7') Cipher.getInstance("AES/CBC/PKCS5Padding") AES.GCM.seal()
HMAC createMac('HMAC|SHA256') Mac.getInstance("HmacSHA256") HMAC<SHA256>.authenticationCode()
对称密钥生成 createSymKeyGenerator('AES256').generateSymKeySync() KeyGenerator.getInstance("AES").generateKey() SymmetricKey(size:)

可以看到,HarmonyOS 的 API 设计融合了 Android 的灵活性(字符串参数指定算法)和 iOS 的类型安全性(强类型接口),同时加入了国密算法(SM3、SM4)的原生支持,这是其它平台不具备的独特优势。

十、总结

cryptoFramework 是 HarmonyOS NEXT 密码学能力的核心入口,它提供了:

  1. 7种摘要算法 — MD5、SHA-1/224/256/384/512、SM3,覆盖国际标准和国密
  2. AES对称加密 — AES128/192/256 × ECB/CBC/CTR 多种组合
  3. HMAC认证码 — 基于SHA-256的键控哈希,验证完整性和真实性
  4. 统一的管道式API — 所有模块遵循"创建→初始化→更新→完成"四步模式
  5. 同步/异步双轨支持 — 小数据用 Sync,大文件用 Promise,灵活切换
  6. 零权限门槛 — 无需申请任何系统权限即可使用

与 zlib 压缩引擎一样,cryptoFramework 也体现了 HarmonyOS API 设计的统一哲学:用 createXxx() 创建实例,用 xxxSync() 完成同步操作,用 DataBlob 传递二进制数据。掌握这一个模式,就能触类旁通地驾驭整个框架。

下一篇文章我们将继续探索 HarmonyOS NEXT 的其它新特性,敬请期待。

Logo

作为“人工智能6S店”的官方数字引擎,为AI开发者与企业提供一个覆盖软硬件全栈、一站式门户。

更多推荐