HarmonyOS鸿蒙Next中基于密钥库(huks)的加解密常见问题指导

HarmonyOS鸿蒙Next中基于密钥库(huks)的加解密常见问题指导

场景描述

1、密钥库中的私钥可以用于保存密钥,在加解密过程中,开发者会因为没有校验密钥是否存在造成密钥出现变更或被删除,使得密钥失效,导致加解密过程出现问题。

2、SM2比较特殊,在交互过程会出现密钥和密文是否是ASN.1的格式问题,导致两端加解密出现解密失败。

3、使用密钥库去做加解密和签名验签,生成密钥失败。

4、在交互过程中,对于加解密的明文是有部分要求的。明文过长或者过短可能会造成加密失败。

方案描述

密钥库中密钥的创建和使用

同时使用密钥库去做加解密和签名验签

在同时做加解密和签名验签的情况下,创建密钥对的时候设置加解密和签名验签的属性,创建密钥失败。

解决方案

在创建密钥对的时候,密钥的目的属性必须是单一或者成对的,不能同时赋予加解密和签名验签的属性。

需要调用两次huks.generateKeyItem方法,分别传入不同的密钥别名和密钥属性。

核心代码

如果涉及加解密和签名验签,这里是正确的示例代码:

//调用两次huks.generateKeyItem方法,分别传入不同的密钥别名和密钥属性
let encryptAndDecryptKeyAlia = '加解密的密钥别名'; 
let signatureVeriftKeyAlia = '签名验签的密钥别名'; 
huks.generateKeyItem(encryptAndDecryptKeyAlia, cryptAndDecryptproPertiesen); 
huks.generateKeyItem(signatureVeriftKeyAlia, signatureVerifProperties); 

//用于创建加解密的密钥属性
let cryptAndDecryptproPertiesen: Array<huks.HuksParam> = [
  {
    tag: huks.HuksTag.HUKS_TAG_ALGORITHM,
    value: huks.HuksKeyAlg.HUKS_ALG_RSA
  },
  {
    tag: huks.HuksTag.HUKS_TAG_PURPOSE,
    value: huks.HuksKeyPurpose.HUKS_KEY_PURPOSE_ENCRYPT | huks.HuksKeyPurpose.HUKS_KEY_PURPOSE_DECRYPT
  },
  {
    tag: huks.HuksTag.HUKS_TAG_KEY_SIZE,
    value: huks.HuksKeySize.HUKS_RSA_KEY_SIZE_3072
  }
];

let huksOptions: huks.HuksOptions = {
  properties: cryptAndDecryptproPertiesen,
  inData: new Uint8Array(new Array())
}

//用于创建签名验签的密钥属性
let signatureVerifProperties: Array<huks.HuksParam> = [
  {
    tag: huks.HuksTag.HUKS_TAG_ALGORITHM,
    value: huks.HuksKeyAlg.HUKS_ALG_RSA
  },
  {
    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_RSA_KEY_SIZE_3072
  }
];

let huksOptions: huks.HuksOptions = {
  properties: signatureVerifProperties,
  inData: new Uint8Array(new Array())
}

密钥库公钥可导出,私钥不可见,如何与服务端交互使用同一套密钥

a. 服务端使用客户端传入的公钥做加密,客户端使用对应私钥做解密;客户端解密失败。

b. 服务端生成密钥对,导出公钥给客户端,客户端加密后密文传递给服务端,服务端解密失败。

c. 客户端使用本端生成的公私钥做加解密;解密失败

解决方案

密钥是有时效性的,如果应用卸载之后密钥是会失效的。

1、不管是在客户端还是服务端,在生成密钥之前,首先需要校验密钥是否存在,如果密钥本身就存在,就不需要再次创建导致密钥被覆盖。

2、再导入密钥的时候要根据对应的算法去使用对应的格式去导入公钥,可以参考支持的导入的算法密钥以及对应的导入格式。

3、在涉及两端交互的过程中,需要判断客户端传出和服务器端接收的公钥别名和数据是否一致,避免因为request请求导致base64字符串特殊字符发生改变。

处理方法:

a. 在交互过程中使用十六进制字符串;

b. 使用base64传递的时候,使用baseurl传递,即在转成字符串的时候把类型设置为BASIC_URL_SAFE。

new util.Base64Helper().encodeToStringSync(data,util.Type.BASIC_URL_SAFE)

4、因为密钥不可见,所以使用huks.generateKeyItem()没有捕获到异常,既代表密钥生成成功,也可以通过huks.hasKeyItem()接口查询是否生成成功。

核心代码

/* 创建密钥对 */
async function generateKey(KeyAlias: string, purpose: huks.HuksKeyPurpose, IsAuth: boolean) {
  //getGenerateProperties是密钥创建的属性,开发者可以根据自己的需求去选择使用什么样的密钥算法以及作用
  let genProperties = getGenerateProperties(purpose, IsAuth); 
  let options: huks.HuksOptions = { 
    properties: genProperties 
  } 
  await huks.generateKeyItem(KeyAlias, options).then((data) => { 
    //生成的data是null是正常的,这里是因为密钥在密钥库是不可见的,返回error为null即为生成成功 
    console.info(`huks callback: generate Key success, data = ${JSON.stringify(data)}`); 
  }).catch((error: BusinessError) => { 
    console.error(`huks callback: generate Key failed`); 
  }) 
}

/* 校验密钥是否存在 */
async function checkHuksKey(keyAlias: string) { 
  //在做密钥校验的时候,huksOptions只需要传入算法名称即可判断密钥是否存在,其他的可以设置为空 
  let keyProperties: Array<huks.HuksParam> = new Array(); 
  let index = 0; 
  keyProperties[index++] = { 
    tag: huks.HuksTag.HUKS_TAG_ALGORITHM, 
    value: huks.HuksKeyAlg.HUKS_ALG_AES 
  }; 
  let huksOptions: huks.HuksOptions = { 
    properties: keyProperties, 
    inData: new Uint8Array() 
  } 
  //这里用return的方法返回校验结果的时候要注意:该接口是异步的,如果直接赋值然后返回就会导致结果不同步,所以这个地方需要在前面加await,等返回结果之后在返回校验结果 
  return await huks.isKeyItemExist(keyAlias, huksOptions); 
}

密钥库中加解密常见问题

SM2比较特殊,如何在交互过程将两端的密钥和密文统一为序列化(ASN.1)和未序列化(裸密文/钥)的数据格式

解决方案

a. 将huks的公钥通过huks.exportKeyItem()接口导出后,因为密钥库没有提供格式转换的接口,需要使用到加解密算法框架中的getAsyKeySpec接口去获取公钥中的x和y的值,进而解析出公钥ASN.1格式为裸密钥。

b. 如果服务端传入的密文是裸密文,需要通过SM2_Ciphertext工具类中的i2d_SM2_Ciphertext方法(附件)转成ASN.1格式的数据,如果已经是ASN.1格式,直接传入解密方法

核心代码

/* 导出密钥对中的公钥并转成裸密文 */
async function exportPublicKeyTonakedCiphertext(keyAlias: string): Promise<string> { 
  let pk = ''; 
  let emptyOptions: huks.HuksOptions = { 
    properties: []; 
  }; 
  try { 
    //huks导出的公钥 
    let puk = await huks.exportKeyItem(keyAlias, emptyOptions); 
    //使用加解密算法框架的接口去转换 ;sm2公钥对应的字符串参数为 X+Y 
    let keypairGenerator = cryptoFramework.createAsyKeyGenerator('SM2_256') 
    let key = await keypairGenerator.convertKeySync({ data: puk.outData }, null); 
    //获取公钥中x的参数值 
    let x = key.pubKey.getAsyKeySpec(cryptoFramework.AsyKeySpecItem.ECC_PK_X_BN); 
    //获取公钥中y的参数值 
    let y = key.pubKey.getAsyKeySpec(cryptoFramework.AsyKeySpecItem.ECC_PK_Y_BN); 
    //将转换后的x 和 y拼接即可得到完整的十六进制字的裸密钥 
    pk = x.toString(16) + y.toString(16); 
  } catch (err) { 
    console.error('huks callback: exportPublicKey failed'); 
  } 
  return pk; 
}

在交互过程中,对于加解密的明文是有部分要求的。如何根据加密方式处理明文

在使用密钥库加解密的时候,因为使用的填充方式或者分组模式,会对可加密的明文有一定的限制条件,如果不满足,就会导致加密失败。即使使用分段加解密要求也是一致

解决方案

具体的限制请参考下面的表格数据,其中:

原始数据长度 => 指的是明文的字节长度。

密钥长度 => 指的是密钥的位数除以8,例如使用的AES256,密钥位数为256,那密钥的长度为256/8 = 32 字节。

摘要长度 => 指的是摘要的位数除以8,例如使用的是SHA256摘要,位数为256,那摘要长度为256/8 = 32字节。

未压缩密文 => 指的是04+C1+C2+C3,由于各算法库差异,04可以视情况省略。

压缩 => 指的是c1(03+x) + c3(32字节) + c2或c1(02+x) + c3(32字节) + c2。

表格数据

加解密算法 分组模式 填充方式 原始数据长度 加密后的数据长度 备注
AES/SM4 ECB/CBC NoPadding 16字节整数倍 和原始数据长度一致 如果加密的数据不满足16的整数倍,可以通过填补固定字符串的方式凑齐16整数倍的数据去加密。
AES/SM4 ECB/CBC PKCS7 无要求 (原始数据长度+1)/16*16 无要求
AES/SM4 CFB/GCM/CCM NoPadding/PKCS7/PKCS5 无要求 和原始数据长度一致 无要求
RSA 默认ECB NoPadding 和密钥长度一致 和密钥长度一致 如果加密的数据过大,可以使用分段加解密实现,分段的长度根据要求设置即可。
RSA 默认ECB PKCSV1.5 小于等于密钥长度-11字节 和密钥长度一致 无要求
RSA 默认ECB OAEPPadding 小于等于密钥长度-2*摘要长度-2 和密钥长度一致 如果涉及RSA
SM2 \ \ 无要求 len(C1) + 消息长度 + 摘要长度 len(C1) = 密钥长度+1(有压缩) 或 2倍密钥长度+1(无压缩)

更多关于HarmonyOS鸿蒙Next中基于密钥库(huks)的加解密常见问题指导的实战教程也可以访问 https://www.itying.com/category-93-b0.html

2 回复

HarmonyOS鸿蒙Next中,密钥库(HUKS)提供了安全的密钥管理和加解密功能。常见问题包括密钥生成、存储、使用和销毁。密钥生成时需指定算法和用途,存储时确保密钥安全,使用时通过HUKS API进行加解密操作,销毁时需彻底清除密钥数据。HUKS支持多种加密算法,如AES、RSA等,确保数据安全。开发者需遵循HUKS规范,避免密钥泄露和误用。

更多关于HarmonyOS鸿蒙Next中基于密钥库(huks)的加解密常见问题指导的实战系列教程也可以访问 https://www.itying.com/category-93-b0.html


关于HarmonyOS Next中HUKS加解密的常见问题,我总结以下几点关键解决方案:

  1. 密钥创建与使用:
  • 加解密和签名验签需要分别创建密钥对,不能共用同一密钥
  • 创建前务必先检查密钥是否存在(使用huks.hasKeyItem())
  • 密钥时效性需注意,应用卸载后密钥会失效
  1. SM2格式处理:
  • 导出公钥后需用getAsyKeySpec获取x/y值转换为裸密钥
  • 裸密文需用i2d_SM2_Ciphertext转换为ASN.1格式
  • 服务端交互需统一数据格式(ASN.1或裸密文)
  1. 明文长度要求:
  • AES/SM4在ECB/CBC模式下:
    • NoPadding要求16字节整数倍
    • PKCS7无长度限制
  • RSA加密:
    • NoPadding要求等于密钥长度
    • PKCS1要求≤密钥长度-11字节
    • OAEP要求≤密钥长度-2*摘要长度-2
  1. 交互注意事项:
  • 建议使用十六进制字符串传输密钥数据
  • 如需base64传输,应使用BASIC_URL_SAFE类型
  • 两端需保持一致的密钥别名和数据格式

这些方案可解决大部分HUKS加解密过程中的常见问题,开发者应根据具体场景选择合适的处理方式。

回到顶部