HarmonyOS鸿蒙Next中如何解决国密SM2密文长度不一致,密文无法解析的问题

HarmonyOS鸿蒙Next中如何解决国密SM2密文长度不一致,密文无法解析的问题

【问题现象】

在使用SM2时,常遇到以下问题:

  1. SM2加密后密文长度不确定,服务端无法解析。
  2. ArkTS中SM2加密后的密文,服务端无法解析。

【背景知识】

SM2是一种中国国家标准的椭圆曲线公钥密码算法。

SM2密文结构

  • C1: 椭圆曲线上的一个点,表示临时公钥(包括C1_X和C1_Y,即XY分量)
  • C2: 加密后的数据(实际密文)
  • C3: 哈希值,用于完整性校验

SM2密文长度

  • C1: 椭圆上的点(x,y),X、Y坐标均为256位(32字节),即C1通常为64字节(512位)
  • C2: 裸密文,长度与明文一致
  • C3: 哈希值,SM2使用SM3哈希算法,生成的哈希值为256位,即32字节

因此,假设明文长度为N字节,则密文长度为64+N+32=N+96。生产中通常会在密文前增加前缀’04’来表示C1未压缩。因此实际密文一般为N+96或N+97。

SM2密文拼接标准

  • C1C2C3: 遵循中国国家标准(GB/T 32918.2-2016)标准
  • C1C3C2: 遵循ISO/IEC 14888-3:2018 标准

目前,ArkTS使用C1C3C2标准。C1、C2、C3相互独立,可重新拼接以满足特定的兼容性或性能需求。

【定位思路】

  1. 根据背景知识中SM2密文长度小节中,通常密文长度固定为明文长度N+96 或 N+97字节,然而在ArkTS中发现密文长度可能为明文长度+95字节、明文长度+95.5字节、明文长度+96字节中的一种。实际上,在ArkTS的SM2加密中,为了保证安全性,防止数据在拼接的时候被截取到,openssl底层返回的数据是直接透传返回的。openssl底层返回的数据规范是高位是0的情况下默认不显示,不影响密钥的生成。
  2. 现如今大多数存量程序中均保持使用N+96 或 N+97的密文长度,解析时通过直接截取固定长度来区分C1、C2、C3,导致ArkTS生成的SM2密文无法被正确解析。
  3. 同时,存量程序中密文一般默认采用C1C2C3的拼接顺序,导致ArkTS生成的SM2密文无法被正确解析。

【解决方案】

根据以上的分析,用户可根据其他存量程序的方案选择以下合适的方法进行适配,即密文长度需要固定的调整长度,拼接顺序不一致的重新拼接。

加密过程此处不再赘述,假定pubKey为公钥,res为加密后的密文:

let pubKey = await this.convertStrToPubKey(pubKeyStr);
let res = await this.encryptMessagePromise(pubKey, str);

调整前准备

无论是长度适配或者是拼接标准适配,用户均需要先解析得到C1(临时公钥,包括C1_X, C1_Y)、C2(密文)、C3(哈希值)。

使用cryptoFramework.SM2CryptoUtil.getCipherTextSpec得到cryptoFramework.SM2CipherTextSpec,SM2CipherTextSpec包含以下几个变量:

  • xCoordinate: X分量,即C1_X
  • yCoordinate: Y分量,即C1_Y
  • cipherTextData: 裸密文,即C2
  • hashData: 哈希值,即C3

解析得到以上密文参数的16进制数:

let spec: cryptoFramework.SM2CipherTextSpec = cryptoFramework.SM2CryptoUtil.getCipherTextSpec(res, 'C1C3C2');
let C1x = spec.xCoordinate.toString(16);
let C1y = spec.yCoordinate.toString(16);
let C2 = buffer.from(spec.cipherTextData).toString('hex');
let C3 = buffer.from(spec.hashData).toString('hex');

调整密文长度

判断X,Y分量长度,若16进制数长度小于64(等于63)即在高位补0

C1x = (C1x.lenth == 64) ? C1x : ('0' + C1x);
C1y = (C1y.lenth == 64) ? C1y : ('0' + C1y);

拼接密文

C1包括C1_X和C1_Y,且C1 = C1_X + C1_Y:

let C1 = C1x + C1y;

用户可根据存量程序的标准进行拼接:

// 若要拼接成C1C2C3
let result = C1 + C2 + C3;
// 若要增加前缀表示C1未压缩
let result = '04 + C1 + C2 + C3;

// 若要拼接成C1C3C2
let result = C1 + C3 + C2;

最终代码为:

async encSM2C1C2C3(str: string, pubKeyStr = this.pubKeyStr) {
  let pubKey = await this.convertStrToPubKey(pubKeyStr)
  let res = await this.encryptMessagePromise(pubKey, str)
  let spec: cryptoFramework.SM2CipherTextSpec = cryptoFramework.SM2CryptoUtil.getCipherTextSpec(res, 'C1C3C2');
  let C1x = spec.xCoordinate.toString(16);
  let C1y = spec.yCoordinate.toString(16);
  let C2 = buffer.from(spec.cipherTextData).toString('hex');
  let C3 = buffer.from(spec.hashData).toString('hex');
  C1x = (C1x.lenth == 64) ? C1x : ('0' + C1x);
  C1y = (C1y.lenth == 64) ? C1y : ('0' + C1y);
  // 拼接C1C2C3, 若要加前缀表示C1未压缩
  return C1x + C1y + C2 + C3
}

【总结】

总而言之,用户在使用方法SM2CryptoUtil.getCipherTextSpec后可得到SM2的所有密文参数,可根据存量程序中的密文使用行为来调整ArkTS的密文参数并重新拼接即可。


更多关于HarmonyOS鸿蒙Next中如何解决国密SM2密文长度不一致,密文无法解析的问题的实战教程也可以访问 https://www.itying.com/category-93-b0.html

1 回复

更多关于HarmonyOS鸿蒙Next中如何解决国密SM2密文长度不一致,密文无法解析的问题的实战系列教程也可以访问 https://www.itying.com/category-93-b0.html


在HarmonyOS鸿蒙Next中解决国密SM2密文长度不一致或无法解析的问题

国密SM2密文长度不一致或无法解析的问题通常与密钥格式、加密参数或编码方式有关。以下是一些可能的解决方案:

  1. 确保密钥格式正确:首先,确保使用的SM2公钥和私钥符合国密标准,且密钥格式正确。

  2. 检查加密参数一致性:检查加密时使用的参数(如曲线类型、哈希算法等)是否与解密时一致。

  3. 确认密文结构完整:SM2加密后的密文通常包含C1、C2、C3三部分,需确保密文结构完整且顺序正确。

  4. 统一编码方式:如果密文长度不一致,可能是编码方式(如ASN.1或DER)未统一,需在加密和解密时使用相同的编码格式。

  5. 检查填充或转换操作:此外,检查是否在加密或解密过程中引入了额外的填充或转换操作,导致密文长度变化。

  6. 确认鸿蒙Next版本支持:最后,确保使用的鸿蒙Next版本支持国密SM2算法,并已正确集成相关库。

通过以上步骤,可有效解决SM2密文长度不一致或无法解析的问题。

回到顶部