1. 项目概述与核心价值

最近在HarmonyOS应用开发社区里,看到不少开发者对国密算法的集成感到头疼,尤其是SM2签名验签这块。很多朋友反馈,官方文档虽然全面,但想快速上手、跑通一个完整的流程,还是得自己摸索半天,踩不少坑。正好我最近在一个金融类应用项目中,完整地走通了HarmonyOS下使用ArkTS进行SM2密钥对签名验签的全流程,从密钥生成到签名,再到验签和异常处理,积累了一些实战心得。今天我就把这些经验整理出来,目标是让你在5分钟内,能有一个清晰、可运行的代码框架,理解每一步背后的逻辑,而不仅仅是复制粘贴。

SM2作为国家密码管理局发布的椭圆曲线公钥密码算法标准,在金融、政务、物联网等对安全性要求极高的场景中应用越来越广。在HarmonyOS应用开发中,尤其是涉及用户身份认证、数据传输完整性校验、交易指令签名等环节,集成SM2几乎是刚需。ArkTS作为HarmonyOS主推的应用开发语言,其安全API的设计既强大又略显复杂,初次接触容易找不到北。本文将从零开始,手把手带你构建一个完整的SM2签名验签Demo,并深入讲解每个API的用途、参数含义以及实际开发中那些文档里没写的“坑”。

2. 环境准备与项目初始化

2.1 开发环境确认

首先,确保你的开发环境已经就绪。你需要安装最新版本的DevEco Studio。我写这篇文章时使用的是4.1 Release版本,配套的HarmonyOS SDK API版本是10。这一点很重要,因为安全相关的API在不同API版本上可能有细微差别。你可以在DevEco Studio的 File > Settings > SDKs 中查看和管理已安装的SDK版本。

创建一个新的Empty Ability工程,选择ArkTS作为开发语言,Compile SDK版本选择API 10或更高。项目创建好后,我们重点关注两个目录: entry/src/main/ets 下的业务逻辑代码,以及 entry/src/main/resources/base/profile 下的权限配置文件。

2.2 权限声明与模块导入

HarmonyOS的密钥管理服务(Universal Keystore Kit)需要声明相应的权限。打开 module.json5 文件,在 module 字段下的 requestPermissions 数组中添加以下权限:

{
  "name": "ohos.permission.ACCESS_UKS"
}

这个权限是应用访问底层密钥系统服务所必需的。没有它,后续所有关于密钥的操作都会失败。

接下来,在需要用到签名验签功能的ArkTS文件(例如 Index.ets )顶部,导入相关的模块:

import { huks } from '@kit.UniversalKeystoreKit'; // 密钥管理核心模块
import { buffer } from '@kit.ArkTS'; // 用于处理二进制数据
import { BusinessError } from '@kit.BasicServicesKit'; // 错误处理

huks 模块是主角,它提供了密钥生成、导入、使用(加解密、签名验签)等全套接口。 buffer 模块用于在ArkTS中处理ArrayBuffer和Uint8Array,因为密钥和签名数据通常是二进制的。

3. SM2密钥对生成与管理

3.1 密钥属性定义

在HarmonyOS的UKS体系中,一切操作都围绕 HuksParam 这个属性参数来展开。生成一个SM2密钥对,首先需要定义它的属性集。这就像你去定制一把锁,需要告诉锁匠锁的类型(SM2)、用途(签名验签)、密钥长度等信息。

// 定义SM2密钥对的属性参数
let keyAlias = 'my_sm2_key'; // 密钥别名,用于后续查找和管理
let properties: Array<huks.HuksParam> = [
  {
    tag: huks.HuksTag.HUKS_TAG_ALGORITHM,
    value: huks.HuksKeyAlg.HUKS_ALG_SM2
  },
  {
    tag: huks.HuksTag.HUKS_TAG_PURPOSE,
    value: huks.HuksKeyPurpose.HUKS_KEY_PURPOSE_SIGN | huks.HuksKeyPurpose.HUKS_KEY_PURPOSE_VERIFY
  },
  {
    tag: huks.HuksTag.HUKS_TAG_KEY_SIZE,
    value: huks.HuksKeySize.HUKS_SM2_KEY_SIZE_256
  },
  {
    tag: huks.HuksTag.HUKS_TAG_DIGEST,
    value: huks.HuksKeyDigest.HUKS_DIGEST_SM3
  },
  {
    tag: huks.HuksTag.HUKS_TAG_PADDING,
    value: huks.HuksKeyPadding.HUKS_PADDING_NONE
  },
  {
    tag: huks.HuksTag.HUKS_TAG_BLOCK_MODE,
    value: huks.HuksCipherMode.HUKS_MODE_ECB
  }
];

let options: huks.HuksOptions = {
  properties: properties
};

我们来逐一拆解这些属性:

  • HUKS_TAG_ALGORITHM : 算法类型,设为 HUKS_ALG_SM2
  • HUKS_TAG_PURPOSE : 密钥用途。这里用按位或操作符 | 同时指定了 SIGN VERIFY ,表示这个密钥对既可用于签名,也可用于验签。如果你只需要签名,可以只设 SIGN
  • HUKS_TAG_KEY_SIZE : 密钥长度。SM2标准曲线是256位,所以选择 HUKS_SM2_KEY_SIZE_256
  • HUKS_TAG_DIGEST : 摘要算法。SM2签名通常与SM3摘要算法配对使用,这是国密标准组合。
  • HUKS_TAG_PADDING : 填充模式。对于SM2签名,通常不需要额外填充,设为 HUKS_PADDING_NONE
  • HUKS_TAG_BLOCK_MODE : 分组模式。虽然ECB模式通常用于对称加密,但在这里对于非对称算法的密钥属性定义中,它作为一个必填参数,按规范设置为 HUKS_MODE_ECB 即可。

注意:属性集的完整性 。这些属性是一个整体,缺少任何一个关键属性(如DIGEST),密钥生成可能会失败,或者生成的密钥无法用于预期的操作。务必根据官方文档核对所需属性。

3.2 执行密钥生成

定义好属性后,就可以调用API生成密钥了。这是一个异步操作。

async function generateKeyPair(): Promise<void> {
  try {
    await huks.generateKeyItem(keyAlias, options);
    console.info('SM2密钥对生成成功,别名:' + keyAlias);
  } catch (error) {
    let err: BusinessError = error as BusinessError;
    console.error(`密钥生成失败,错误码:${err.code}, 错误信息:${err.message}`);
    // 这里可以根据不同的错误码进行更精细化的处理,例如密钥已存在等。
  }
}

调用 huks.generateKeyItem(keyAlias, options) ,系统会在安全的硬件环境(如果设备支持)或软件密钥库中,生成一对SM2密钥。公钥和私钥都与我们指定的 keyAlias 绑定。私钥受到系统级保护,应用无法直接读取其原始字节,只能通过UKS服务在授权下使用它进行签名操作。

3.3 密钥的导出与存储

生成的密钥对存储在系统密钥库中。有时我们需要将公钥导出,分发给其他方用于验签。导出公钥也是一个异步过程。

async function exportPublicKey(): Promise<Uint8Array | null> {
  let exportOptions: huks.HuksOptions = {
    properties: [
      {
        tag: huks.HuksTag.HUKS_TAG_ALGORITHM,
        value: huks.HuksKeyAlg.HUKS_ALG_SM2
      }
    ]
  };
  try {
    let exportResult = await huks.exportKeyItem(keyAlias, exportOptions);
    // exportResult.outData 是 ArrayBuffer 类型,包含公钥数据
    let publicKeyData = new Uint8Array(exportResult.outData as ArrayBuffer);
    console.info('公钥导出成功,长度:' + publicKeyData.length);
    // 在实际应用中,这里可以将 publicKeyData 转换为Base64或Hex字符串进行传输或存储
    // let publicKeyBase64 = buffer.from(publicKeyData).toString('base64');
    return publicKeyData;
  } catch (error) {
    let err: BusinessError = error as BusinessError;
    console.error(`公钥导出失败,错误码:${err.code}, 错误信息:${err.message}`);
    return null;
  }
}

导出的公钥数据是二进制格式(通常是X.509 SubjectPublicKeyInfo结构的DER编码)。在实际网络传输或存储时,我们通常会将其转换为Base64或十六进制字符串。这里用 buffer.from(publicKeyData).toString('base64') 可以方便地转换。

实操心得:密钥别名管理 keyAlias 是你的应用内访问该密钥的唯一标识。建议设计一套清晰的命名规则,例如 功能_算法_版本 (如 user_sign_sm2_v1 ),并考虑在应用卸载时是否要删除密钥(通过 huks.deleteKeyItem )。对于长期使用的根密钥,妥善管理其别名和生命周期至关重要。

4. 使用SM2密钥进行数据签名

4.1 准备待签名数据

签名是针对数据的摘要(哈希值)进行的。首先,我们需要计算原始数据的SM3摘要。虽然UKS的签名接口内部也可以指定摘要算法并计算,但为了更清晰地展示流程,我们这里先显式地计算摘要。

假设我们有一条重要的交易指令字符串需要签名:

let sourceString: string = '支付订单:123456,金额:100.00元,时间:2024-05-27 10:30:00';
let sourceData: Uint8Array = new TextEncoder().encode(sourceString); // 将字符串转为Uint8Array

接下来,我们需要使用SM3算法计算这个数据的摘要。HarmonyOS的 cryptoFramework 提供了摘要计算功能。首先在文件顶部导入:

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

然后计算SM3摘要:

async function sm3Digest(data: Uint8Array): Promise<Uint8Array> {
  let md = cryptoFramework.createMd('SM3'); // 创建SM3摘要实例
  await md.update({ data: data.buffer }); // 更新数据
  let mdResult = await md.digest(); // 计算摘要
  return new Uint8Array(mdResult.data); // 返回摘要的Uint8Array
}

// 调用
let digestData = await sm3Digest(sourceData);
console.info('SM3摘要计算完成,长度:' + digestData.length); // SM3摘要长度为32字节

4.2 构建签名参数与执行签名

有了摘要数据,我们就可以使用之前生成的私钥进行签名了。签名操作同样需要一套属性参数,用于告知UKS服务如何使用密钥。

async function signData(digest: Uint8Array): Promise<Uint8Array | null> {
  // 1. 构建签名操作的参数
  let signProperties: Array<huks.HuksParam> = [
    {
      tag: huks.HuksTag.HUKS_TAG_ALGORITHM,
      value: huks.HuksKeyAlg.HUKS_ALG_SM2
    },
    {
      tag: huks.HuksTag.HUKS_TAG_PURPOSE,
      value: huks.HuksKeyPurpose.HUKS_KEY_PURPOSE_SIGN
    },
    {
      tag: huks.HuksTag.HUKS_TAG_DIGEST,
      value: huks.HuksKeyDigest.HUKS_DIGEST_SM3
    },
    {
      tag: huks.HuksTag.HUKS_TAG_PADDING,
      value: huks.HuksKeyPadding.HUKS_PADDING_NONE
    },
    // 关键参数:指定签名时使用的用户ID(Z值)。SM2签名需要此参数。
    {
      tag: huks.HuksTag.HUKS_TAG_SM2_SIGN_USER_ID,
      value: stringToUint8Array('1234567812345678') // 通常使用默认的16字节用户ID
    }
  ];

  let signOptions: huks.HuksOptions = {
    properties: signProperties,
    inData: digest.buffer // 传入待签名的摘要数据
  };

  // 2. 执行签名
  try {
    let signResult = await huks.sign(keyAlias, signOptions);
    let signature = new Uint8Array(signResult.outData as ArrayBuffer);
    console.info('签名成功,签名长度:' + signature.length); // SM2签名结果通常为64字节(R+S各32字节)
    return signature;
  } catch (error) {
    let err: BusinessError = error as BusinessError;
    console.error(`签名失败,错误码:${err.code}, 错误信息:${err.message}`);
    return null;
  }
}

// 辅助函数:字符串转Uint8Array
function stringToUint8Array(str: string): Uint8Array {
  return new TextEncoder().encode(str);
}

这里有几个关键点:

  1. HUKS_TAG_PURPOSE :必须设置为 HUKS_KEY_PURPOSE_SIGN
  2. HUKS_TAG_SM2_SIGN_USER_ID :这是SM2签名特有的一个参数,称为“用户标识符”或Z值。它参与SM2签名算法中公钥的哈希计算,用于增强安全性。通常使用一个默认的、双方约定的值,比如 1234567812345678 非常重要的一点是,签名和验签时必须使用相同的用户ID,否则验签会失败。 这是新手最容易踩的坑之一。
  3. inData :这里我们传入的是计算好的SM3摘要。实际上, huks.sign 接口也支持传入原始数据,并通过属性指定摘要算法,由UKS内部完成摘要计算。但显式地传入摘要可以让流程更清晰,也方便调试。

签名成功后,会得到一个二进制签名数据。对于SM2-with-SM3,签名结果通常是64字节(R和S分量各32字节)。这个签名值可以随原始数据一起发送给验证方。

5. 使用SM2公钥进行签名验证

5.1 验签方准备工作

验签方需要持有两样东西: 原始数据的摘要 (或原始数据本身,并约定好使用SM3摘要)和 签名值 。当然,最重要的是需要拥有对应的 SM2公钥

公钥的来源有两种:

  1. 从密钥库导出 :如果你是签名方同时也是验签方(例如本地验证),可以直接使用 exportPublicKey 导出的公钥数据。
  2. 从外部接收 :更常见的场景是,服务端或另一台设备收到了签名数据,并同时收到了发送者的公钥(通常是Base64或Hex格式)。你需要将其解码为 Uint8Array

假设我们以第二种场景为例,验签方收到了Base64编码的公钥、原始数据(或摘要)和签名。

// 假设从网络或存储中获取到以下Base64字符串
let receivedPublicKeyBase64: string = 'MFkwEwYHKoZIzj0CAQYIKoEcz1UBgi0DQgAE...'; // 示例,实际很长
let receivedSignatureBase64: string = 'MEUCIQD...'; // 示例
let receivedOriginalData: string = '支付订单:123456,金额:100.00元,时间:2024-05-27 10:30:00';

// 1. 解码Base64数据
import { buffer } from '@kit.ArkTS';
let publicKeyData: Uint8Array = buffer.from(receivedPublicKeyBase64, 'base64').bufferToUint8Array();
let signatureData: Uint8Array = buffer.from(receivedSignatureBase64, 'base64').bufferToUint8Array();

// 2. 计算原始数据的SM3摘要(必须与签名时使用的摘要一致)
let dataToVerify: Uint8Array = new TextEncoder().encode(receivedOriginalData);
let digestToVerify = await sm3Digest(dataToVerify); // 复用之前的sm3Digest函数

5.2 导入公钥并执行验签

HarmonyOS UKS要求使用公钥进行验签前,需要先将公钥数据以“外部密钥”的形式导入到密钥库中,并分配一个临时的别名。

async function verifySignature(publicKey: Uint8Array, digest: Uint8Array, signature: Uint8Array): Promise<boolean> {
  let verifyKeyAlias = 'temp_sm2_pub_key_for_verify'; // 临时公钥别名

  // 1. 构建公钥导入属性
  let importProperties: Array<huks.HuksParam> = [
    {
      tag: huks.HuksTag.HUKS_TAG_ALGORITHM,
      value: huks.HuksKeyAlg.HUKS_ALG_SM2
    },
    {
      tag: huks.HuksTag.HUKS_TAG_PURPOSE,
      value: huks.HuksKeyPurpose.HUKS_KEY_PURPOSE_VERIFY // 注意用途是VERIFY
    },
    {
      tag: huks.HuksTag.HUKS_TAG_KEY_SIZE,
      value: huks.HuksKeySize.HUKS_SM2_KEY_SIZE_256
    },
    {
      tag: huks.HuksTag.HUKS_TAG_DIGEST,
      value: huks.HuksKeyDigest.HUKS_DIGEST_SM3
    },
    {
      tag: huks.HuksTag.HUKS_TAG_PADDING,
      value: huks.HuksKeyPadding.HUKS_PADDING_NONE
    },
    {
      tag: huks.HuksTag.HUKS_TAG_BLOCK_MODE,
      value: huks.HuksCipherMode.HUKS_MODE_ECB
    },
    // 关键属性:声明这是一个外部导入的公钥
    {
      tag: huks.HuksTag.HUKS_TAG_IS_KEY_ALIAS,
      value: true
    },
    {
      tag: huks.HuksTag.HUKS_TAG_IMPORT_KEY_TYPE,
      value: huks.HuksImportKeyType.HUKS_KEY_TYPE_PUBLIC_KEY
    }
  ];

  let importOptions: huks.HuksOptions = {
    properties: importProperties,
    inData: publicKey.buffer // 传入公钥二进制数据
  };

  // 2. 导入公钥
  try {
    await huks.importKeyItem(verifyKeyAlias, importOptions);
    console.info('验签公钥导入成功');
  } catch (error) {
    let err: BusinessError = error as BusinessError;
    console.error(`公钥导入失败,错误码:${err.code}, 错误信息:${err.message}`);
    // 导入失败可能原因:公钥格式错误、属性不匹配等
    return false;
  }

  // 3. 构建验签参数并执行验签
  let verifyProperties: Array<huks.HuksParam> = [
    {
      tag: huks.HuksTag.HUKS_TAG_ALGORITHM,
      value: huks.HuksKeyAlg.HUKS_ALG_SM2
    },
    {
      tag: huks.HuksTag.HUKS_TAG_PURPOSE,
      value: huks.HuksKeyPurpose.HUKS_KEY_PURPOSE_VERIFY
    },
    {
      tag: huks.HuksTag.HUKS_TAG_DIGEST,
      value: huks.HuksKeyDigest.HUKS_DIGEST_SM3
    },
    {
      tag: huks.HuksTag.HUKS_TAG_PADDING,
      value: huks.HuksKeyPadding.HUKS_PADDING_NONE
    },
    // 关键:必须与签名时使用的用户ID完全一致!
    {
      tag: huks.HuksTag.HUKS_TAG_SM2_SIGN_USER_ID,
      value: stringToUint8Array('1234567812345678')
    }
  ];

  let verifyOptions: huks.HuksOptions = {
    properties: verifyProperties,
    inData: digest.buffer, // 传入待验证的摘要
    outData: signature.buffer // 传入待验证的签名
  };

  try {
    await huks.verify(verifyKeyAlias, verifyOptions);
    console.info('验签成功!数据完整且来源可信。');
    // 验签成功后,删除临时导入的公钥
    await huks.deleteKeyItem(verifyKeyAlias);
    return true;
  } catch (error) {
    let err: BusinessError = error as BusinessError;
    console.error(`验签失败,错误码:${err.code}, 错误信息:${err.message}`);
    // 验签失败可能原因:签名无效、数据被篡改、用户ID不匹配、公钥不匹配等
    // 同样,删除临时密钥
    await huks.deleteKeyItem(verifyKeyAlias).catch(() => {});
    return false;
  }
}

验签流程比签名多了一个“导入公钥”的步骤。注意导入时的属性:

  • HUKS_TAG_IS_KEY_ALIAS HUKS_TAG_IMPORT_KEY_TYPE :明确告诉UKS,这是一个从外部导入的公钥。
  • HUKS_TAG_PURPOSE :设置为 HUKS_KEY_PURPOSE_VERIFY ,表明这个导入的密钥只用于验签。

最关键的一步是验签参数中的 HUKS_TAG_SM2_SIGN_USER_ID 。它的值 必须 与签名时使用的用户ID字节对字节完全相同。哪怕是一个字符的差异,也会导致验签失败。这是SM2算法实现中的一个特定要求,务必在双方系统中约定好这个值。

验签成功后, huks.verify 调用正常返回。如果签名无效或数据被篡改,则会抛出异常,错误码可以帮助定位问题。

6. 常见问题排查与实战技巧

6.1 错误码分析与处理

UKS操作失败时会抛出 BusinessError ,其中 code 属性是错误码。掌握常见错误码能快速定位问题。

  • 401 (HUKS_ERR_CODE_PERMISSION_DENIED) :权限不足。检查 module.json5 是否已声明 ohos.permission.ACCESS_UKS 权限。
  • 801 (HUKS_ERR_CODE_ILLEGAL_ARGUMENT) :参数非法。这是最常见的问题。仔细检查 HuksOptions 中的 properties 数组:
    • 属性标签 tag 的值是否正确?
    • 属性值 value 的类型和范围是否正确?(例如, PURPOSE 是否用了正确的 HuksKeyPurpose 枚举值)
    • 属性集合是否完整?对比官方文档的“密钥生成属性”或“密码操作属性”表格。
  • 802 (HUKS_ERR_CODE_NOT_EXIST) :密钥不存在。检查 keyAlias 是否拼写正确,密钥是否已成功生成或导入。
  • 803 (HUKS_ERR_CODE_INVALID_KEY_FILE) :密钥文件无效。可能发生在导入外部密钥时,公钥数据格式不正确(不是有效的X.509 DER编码)。
  • 13000001 (HUKS_ERR_CODE_VERIFICATION_FAILED) :验签失败。签名无效、数据被篡改、或者 用户ID不匹配 。首先排查用户ID。
  • -1 :通用错误或系统内部错误。可以查看 message 获取更多信息,或者检查操作逻辑。

建议在捕获异常后,不仅打印错误码,还将当时操作的 keyAlias options.properties 也打印出来,便于调试。

6.2 性能优化与最佳实践

  1. 密钥别名复用 :对于需要频繁使用的密钥(如应用内固定的签名密钥),应该在应用初始化时生成或导入一次,并持久化保存其别名。避免在每次签名/验签时都重新生成或导入密钥。
  2. 异步操作与UI :所有UKS API都是异步的。在UI线程中调用时,务必使用 async/await .then/.catch ,避免阻塞主线程。对于耗时操作(如密钥生成),可以给用户一个加载提示。
  3. 临时密钥清理 :像验签场景中导入的临时公钥,在验签操作完成后(无论成功与否),都应及时调用 huks.deleteKeyItem 进行清理,避免密钥别名冲突和资源浪费。
  4. 用户ID标准化 :将SM2用户ID作为一个配置项或常量,在应用的所有签名和验签模块中统一使用。例如,可以定义一个常量文件:
    // constants.ts
    export const SM2_DEFAULT_USER_ID = '1234567812345678';
    export const SM2_DEFAULT_USER_ID_BYTES = new TextEncoder().encode(SM2_DEFAULT_USER_ID);
    
  5. 数据摘要的考量 :在上面的示例中,我们显式地先计算SM3摘要再签名。你也可以利用UKS的“一站式”签名,将原始数据直接传给 huks.sign ,并在属性中指定 HUKS_TAG_DIGEST 。UKS内部会先计算摘要再签名。两种方式都可以,显式计算摘要更灵活(例如,你可以先缓存摘要值),而一站式操作更简洁。根据你的业务场景选择。

6.3 跨平台兼容性考虑

如果你的HarmonyOS应用需要与其他系统(如后端Java服务、Web前端)进行签名验签交互,需要特别注意:

  • 公钥格式 :HarmonyOS UKS导出和导入的公钥是X.509 DER格式。确保其他平台也能正确处理这种格式。常见的Java PublicKey 对象、OpenSSL命令行工具通常都支持导入DER格式的公钥。
  • 签名格式 :UKS生成的SM2签名是简单的R||S拼接(各32字节)。有些平台或库可能使用ASN.1 DER编码的签名格式。如果遇到验签不通过,检查签名格式是否一致,可能需要进行格式转换。
  • 用户ID :这是SM2特有的。必须确保通信双方使用完全相同的用户ID字节序列。

6.4 完整流程串联示例

最后,我们把上面的所有片段串联成一个完整的、可执行的函数流程,模拟一个简单的“客户端签名-服务端验签”场景:

async function demoFullProcess() {
  console.info('=== 开始SM2签名验签完整演示 ===');

  // 客户端:生成密钥对并导出公钥
  await generateKeyPair();
  let publicKeyData = await exportPublicKey();
  if (!publicKeyData) {
    console.error('公钥导出失败,流程终止');
    return;
  }
  let publicKeyBase64 = buffer.from(publicKeyData).toString('base64');
  console.info('客户端公钥(Base64):', publicKeyBase64.substring(0, 64) + '...');

  // 客户端:准备数据并签名
  let originalData = '这是一条需要签名的关键指令';
  let dataBytes = new TextEncoder().encode(originalData);
  let digest = await sm3Digest(dataBytes);
  let signature = await signData(digest);
  if (!signature) {
    console.error('签名失败,流程终止');
    return;
  }
  let signatureBase64 = buffer.from(signature).toString('base64');
  console.info('客户端签名(Base64):', signatureBase64.substring(0, 64) + '...');

  // 模拟网络传输:客户端将 [原始数据, 签名, 公钥] 发送给服务端
  console.info('--- 模拟数据传输 ---');

  // 服务端:接收数据并验签
  let receivedData = originalData;
  let receivedSignature = buffer.from(signatureBase64, 'base64').bufferToUint8Array();
  let receivedPublicKey = buffer.from(publicKeyBase64, 'base64').bufferToUint8Array();

  // 服务端:计算摘要
  let serverDigest = await sm3Digest(new TextEncoder().encode(receivedData));
  // 服务端:执行验签
  let isVerified = await verifySignature(receivedPublicKey, serverDigest, receivedSignature);

  if (isVerified) {
    console.info('=== 验签成功!指令来源可信,可以执行。===');
  } else {
    console.error('=== 验签失败!指令可能被篡改或来源不可信,拒绝执行。===');
  }
}

// 在合适的时机调用,例如按钮点击事件中
// demoFullProcess();

运行这个演示,你将在Log中看到完整的流程输出。通过这个实战演练,你应该对如何在HarmonyOS ArkTS应用中使用SM2密钥对有了清晰的理解。记住,安全无小事,尤其是在处理密钥和签名时,仔细检查每一个参数和步骤,才能构建出可靠的应用。

Logo

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

更多推荐