HarmonyOS 鸿蒙Next SM2加解密demo请求:按照api文档sm2无法完成解密

发布于 1周前 作者 zlyuanteng 来自 鸿蒙OS

HarmonyOS 鸿蒙Next SM2加解密demo请求:按照api文档sm2无法完成解密

按照api文档sm2无法完成解密,麻烦提供sm2加解密demo

2 回复

鸿蒙支持将密文转为 c1c2c3 以及c1c3c2  但是鸿蒙段进行解密目前仅支持 c1c3c2  需要将c1c2c3的格式在java端进行转换为c1c3c2

import { cryptoFramework } from '[@kit](/user/kit).CryptoArchitectureKit';

import { buffer, util } from '[@kit](/user/kit).ArkTS';

import { BusinessError } from '[@kit](/user/kit).BasicServicesKit';

[@Entry](/user/Entry)

[@Component](/user/Component)

struct sm2Api12Crypto {

  [@State](/user/State) message: string = '点击开始';

  build() {

    Row() {

      Column() {

        Text(this.message)

          .fontSize(50)

          .fontWeight(FontWeight.Bold)

          .onClick(() => {

            main()

          })

      }

      .width('100%')

    }

    .height('100%')

  }

}

// 加密消息

async function encryptMessagePromise(publicKey: cryptoFramework.PubKey, plainText: cryptoFramework.DataBlob) {

  //密钥类型为SM2_256、摘要算法为SM3的Cipher

  let cipher = cryptoFramework.createCipher('SM2_256|SM3');

  await cipher.init(cryptoFramework.CryptoMode.ENCRYPT_MODE, publicKey, null);

  let encryptData = await cipher.doFinal(plainText);

  return encryptData;

}

// 解密消息

async function decryptMessagePromise(privateKey: cryptoFramework.PriKey, cipherText: cryptoFramework.DataBlob) {

  let decoder = cryptoFramework.createCipher('SM2_256|SM3');

  await decoder.init(cryptoFramework.CryptoMode.DECRYPT_MODE, privateKey, null);

  let decryptData = await decoder.doFinal(cipherText);

  return decryptData;

}

//根据密钥参数生成sm2公钥

export async function convertStrToPubKey(keyStr: string): Promise<cryptoFramework.PubKey> {

  const pubKeyStr = keyStr.startsWith('04') ? keyStr.slice(2) : keyStr

  const pkPart1 = pubKeyStr.slice(0, pubKeyStr.length / 2)

  const pkPart2 = pubKeyStr.slice(pubKeyStr.length / 2)

  const pk: cryptoFramework.Point = {

    x: BigInt('0x' + pkPart1),

    y: BigInt('0x' + pkPart2),

  }

  const pubKeySpec: cryptoFramework.ECCPubKeySpec = {

    params: cryptoFramework.ECCKeyUtil.genECCCommonParamsSpec('NID_sm2'),

    pk: pk,

    algName: 'SM2',

    specType: cryptoFramework.AsyKeySpecType.PUBLIC_KEY_SPEC

  }

  const keypairGenerator = cryptoFramework.createAsyKeyGeneratorBySpec(pubKeySpec)

  return await keypairGenerator.generatePubKey()

}

//根据密钥参数生成sm2私钥

export async function convertStrToPriKey(keyStr: string): Promise<cryptoFramework.PriKey> {

  const sk = BigInt('0x' + keyStr)

  const priKeySpec: cryptoFramework.ECCPriKeySpec = {

    params: cryptoFramework.ECCKeyUtil.genECCCommonParamsSpec('NID_sm2'),

    sk: sk,

    algName: 'SM2',

    specType: cryptoFramework.AsyKeySpecType.PRIVATE_KEY_SPEC

  }

  const keypairGenerator = cryptoFramework.createAsyKeyGeneratorBySpec(priKeySpec)

  return await keypairGenerator.generatePriKey()

}

// SM2  main代码

async function main() {

  let base64 = new util.Base64Helper();

  //十六进制的公私钥

  // let pubKeyStr = "0453402B95F3584F36B9A7129A6B5C6109F2DBC7C94BE7858DB66C48AF38CB5C3B76883EE4BF18E270607191E233EAC0A95ECFB8EF6FE80C5F782DE24F018DEB5F"

  let pubKeyStr = "BDeRJMU7GFuy+9v3VIe34rf3P/mu3ZpJqLzzjIczCyoZ+yiybnWp8KbD8fMOPTt6+jJyGpTraVYtPUw1joURwVs="

  // let priKeyStr = "5B9270E0ADF86A101167610FCCD375A6549DC14E9225951EF3A4640F26D6CD9C"

  let priKeyStr = "JlkWOVILswt9+dRUmAnJjd2C70aLwStFm2KZhEDL2kg="

  // 公钥base64转十六进制

  let u8aPub = base64.decodeSync(pubKeyStr)

  console.error('Uint8Array u8aP result string:' + u8aPub);

  let bufPub = buffer.from(u8aPub);

  console.error('hex u8aPub result string:' + bufPub.toString('hex'));

  let hexPub = "04379124c53b185bb2fbdbf75487b7e2b7f73ff9aedd9a49a8bcf38c87330b2a19fb28b26e75a9f0a6c3f1f30e3d3b7afa32721a94eb69562d3d4c358e8511c15b"

  // 私钥base64转十六进制

  let u8aPri = base64.decodeSync(priKeyStr)

  console.error('Uint8Array u8aP result string:' + u8aPri);

  let bufPri = buffer.from(u8aPri);

  console.error('hex u8aPri result string:' + bufPri.toString('hex'));

  let hexPri = "26591639520bb30b7df9d4549809c98ddd82ef468bc12b459b62998440cbda48"

  //安卓加密后的密文

  /*try {

    let spec : cryptoFramework.SM2CipherTextSpec = {

      xCoordinate: BigInt('20625015362595980457695435345498579729138244358573902431560627260141789922999'),

      yCoordinate: BigInt('48563164792857017065725892921053777369510340820930241057309844352421738767712'),

      cipherTextData: new Uint8Array([100,227,78,195,249,179,43,70,242,69,169,10,65,123]),

      hashData: new Uint8Array([87,167,167,247,88,146,203,234,83,126,117,129,52,142,82,54,152,226,201,111,143,115,169,125,128,42,157,31,114,198,109,244]),

    }

    let data = cryptoFramework.SM2CryptoUtil.genCipherTextBySpec(spec, 'C1C3C2');

    console.info('genCipherTextBySpec success');

  } catch (err) {

    let e: BusinessError = err as BusinessError;

    console.error(`genCipherTextBySpec error, ${e.code}, ${e.message}`);

  }*/

  /*try {

    let cipherTextArray = new Uint8Array([48,118,2,32,45,153,88,82,104,221,226,43,174,21,122,248,5,232,105,41,92,95,102,224,216,149,85,236,110,6,64,188,149,70,70,183,2,32,107,93,198,247,119,18,40,110,90,156,193,158,205,113,170,128,146,109,75,17,181,109,110,91,149,5,110,233,209,78,229,96,4,32,87,167,167,247,88,146,203,234,83,126,117,129,52,142,82,54,152,226,201,111,143,115,169,125,128,42,157,31,114,198,109,244,4,14,100,227,78,195,249,179,43,70,242,69,169,10,65,123]);

    let cipherText : cryptoFramework.DataBlob = {data : cipherTextArray};

    let spec : cryptoFramework.SM2CipherTextSpec = cryptoFramework.SM2CryptoUtil.getCipherTextSpec(cipherText, 'C1C3C2');

    // console.error('getCipherTextSpec success'+ spec);

  } catch (err) {

    let e: BusinessError = err as BusinessError;

    console.error(`getCipherTextSpec error, ${e.code}, ${e.message}`);

  }*/

  //根据密钥参数生成对应的公私钥

  let pubKey = await convertStrToPubKey(hexPub)

  console.error("pubKey=======>" + pubKey.getEncoded().data)

  let priKey = await convertStrToPriKey(hexPri)

  console.error("priKey=======>" + priKey.getEncoded().data)

  // 此处为明文

  // 把字符串按utf-8解码为Uint8Array

  let message = "aaaa"

  let plainText: cryptoFramework.DataBlob = { data: new Uint8Array(buffer.from(message, 'utf-8').buffer) };

  let encryptText = await encryptMessagePromise(pubKey, plainText);

  console.error("encryptText=======>" + encryptText.data.toString())

  console.error("encryptHex=======>" + buffer.from(encryptText.data).toString('hex'))

  //reslut 是加密后的密文数据

  let spec: cryptoFramework.SM2CipherTextSpec = cryptoFramework.SM2CryptoUtil.getCipherTextSpec(encryptText, 'C1C3C2');

  /*

  * C1 = spec.xCoordinate.toString(16) + spec.yCoordinate.toString(16)

  * C2 = buffer.from(spec.cipherTextData).toString('hex')

  * C3 = buffer.from(spec.hashData).toString('hex')

  * */

  console.error(""+spec.xCoordinate.toString(16).length)

  let encryptHex =  spec.xCoordinate.toString(16) + spec.yCoordinate.toString(16) + buffer.from(spec.cipherTextData).toString('hex') + buffer.from(spec.hashData).toString('hex')

  console.error("C1C2C3解码后16进制数据=======>" + encryptHex)

  let encryptBase64 = base64.encodeToStringSync(new Uint8Array(buffer.from(encryptHex, 'hex').buffer))

  console.error("C1C2C3解码后数据Base64Str=======>" + encryptBase64)

  //将加密的密文数据解码转换为安卓可用数据(用于鸿蒙和安卓的交接)

  // let b = new SM2_Ciphertext().d2i_SM2_Ciphertext(buffer.from(str).toString('hex'))

  // console.log("解码后数据=======>" + b)

 /* //解密得到结果

  let res = await decryptMessagePromise(priKey, encryptText)

  console.log("a=======>" + buffer.from(res.data).toString('utf-8'))*/

  //针对安卓的密文处理,转成鸿蒙可用uint8Array数组数据

  let c = new Uint8Array(buffer.from(new SM2_Ciphertext().i2d_SM2_Ciphertext(encryptHex), 'hex').buffer)

  console.error("java转asn1=======>" + c)

  //对安卓生成的的密文进行解密

  try {

    let resa = await decryptMessagePromise(priKey, { data: c }/*encryptText*/)

    console.log("安卓的密文解码数据=======>" + buffer.from(resa.data).toString('utf-8'))

  }catch (error){

    console.error(JSON.stringify(error));

  }

}

class ASN1Util {

  static BOOLEAN: string = "01";

  static INTEGER: string = "02";

  static BIT_STRING: string = "03";

  static OCTET_STRING: string = "04";

  static NULL: string = "05";

  static REAL: string = "09";

  static ENUMERATED: string = "0a";

  static SEQUENCE: string = "30";

  static SET: string = "31";

}

class SM2_Ciphertext {

  /**

   * 用于将SM2裸密文数据序列化

   * [@param](/user/param) primal_data SM2裸密钥数据,长度为96+明文长度(字节),输入格式为C1C3C2的Hex字符串

   * [@returns](/user/returns) 返回序列化后的标准密文数据,输出格式为Hex字符串*/

  i2d_SM2_Ciphertext(primal_data: string): string {

    let sm2_sequence = new SM2_SEQUENCE();

    sm2_sequence.C1x = primal_data.slice(0, 64);

    primal_data = primal_data.slice(64, primal_data.length);

    sm2_sequence.C1y = primal_data.slice(0, 64);

    primal_data = primal_data.slice(64, primal_data.length);

    sm2_sequence.C3 = primal_data.slice(0, 64);

    primal_data = primal_data.slice(64, primal_data.length);

    sm2_sequence.C2 = primal_data;

    let C1x_title: string = (Number.parseInt("0x" + sm2_sequence.C1x.slice(0, 2)) > 127) ? "022100" : "0220";

    let C1y_title: string = (Number.parseInt("0x" + sm2_sequence.C1y.slice(0, 2)) > 127) ? "022100" : "0220";

    let C3_title: string = "0420";

    let C2_title: string = "04" + this.genLenHex(sm2_sequence.C2);

    let sequence_message: string = C1x_title + sm2_sequence.C1x + C1y_title + sm2_sequence.C1y + C3_title + sm2_sequence.C3 + C2_title + sm2_sequence.C2;

    // let sequence_message: string = C1x_title + sm2_sequence.C1x + C1y_title + sm2_sequence.C1y + C2_title + sm2_sequence.C2 + C3_title + sm2_sequence.C3;

    let sequence_lenHex:string = this.genLenHex(sequence_message);

    let standard_data = "30" + sequence_lenHex + sequence_message;

    return standard_data;

  }

  /**

 * 用于将标准SM2密文数据解码

 * [@param](/user/param) standard_data 标准SM2密文数据,符合ASN.1编码标准,输入格式为Hex字符串

 * [@returns](/user/returns) 返回ASN.1解码后的SM2密文数据*/

  d2i_SM2_Ciphertext(standard_data: string): string {

    let message: string = standard_data;

    // 起始标识为03

    if (!message.startsWith(ASN1Util.SEQUENCE)) {

      this.ciphertextErr();

    }

    message = message.slice(ASN1Util.SEQUENCE.length, message.length);

    // SM2 sequence内容的长度

    let sequence_lenHex: string = this.getLenHex(message);

    message = message.slice(sequence_lenHex.length, message.length);

    let sequence_len: number = this.lenHex2number(sequence_lenHex);

    if (sequence_len != message.length / 2) {

      this.ciphertextErr();

    }

    let sm2_sequence = new SM2_SEQUENCE();

    message = this.readC1(sm2_sequence, message);

    message = this.readC3(sm2_sequence, message);

    message = this.readC2(sm2_sequence, message);

    // message = this.readC3(sm2_sequence, message);

    // console.log(sm2_sequence.toString());

    let primal_data: string = sm2_sequence.C1x + sm2_sequence.C1y + sm2_sequence.C3 + sm2_sequence.C2;

    // let primal_data: string = sm2_sequence.C1x + sm2_sequence.C1y + sm2_sequence.C2 + sm2_sequence.C3;

    return primal_data;

  }

  // 生成传入内容的长度域

  genLenHex(content: string): string {

    let size: number = content.length / 2;

    let lenHex: string ;

    if (size.toString(16).length % 2 == 1 ) {

      lenHex= '0' + size.toString(16);

    }else {

      lenHex = size.toString(16);

    }

    if(size < 0x80){

      return lenHex;

    }

    let lenHex_size: number = lenHex.length / 2;

    return (lenHex_size | 0x80).toString(16) + lenHex;

  }

  // 提取长度域的Hex字符串

  getLenHex(data: string): string {

    let byte: number = Number.parseInt("0x" + data.slice(0, 2));

    let len_size: number = byte > 127 ? byte - 0x80 + 1 : 1;

    return data.slice(0, len_size * 2);

  }

  // 将长度域的Hex字符串转为整型

  lenHex2number(lenHex: string): number {

    if (lenHex.length == 2) {

      return Number.parseInt("0x" + lenHex);

    }

    return Number.parseInt("0x" + lenHex.slice(2, lenHex.length));

  }

  ciphertextErr() {

    console.error("d2i_SM2_Ciphertext", "密文格式错误");

    throw new Error("SM2 ciphertext error!")

  }

  readC1(sm2_sequence: SM2_SEQUENCE, data: string): string {

    let xy: string[] = [];

    for (let i = 0; i < 2; i++) {

      if (data.startsWith("0220")) {

        xy[i] = data.slice(4, 68);

        data = data.slice(68, data.length);

      } else if (data.startsWith("022100")) {

        xy[i] = data.slice(6, 70);

        data = data.slice(70, data.length);

      } else {

        this.ciphertextErr();

      }

    }

    sm2_sequence.C1x = xy[0];

    sm2_sequence.C1y = xy[1];

    return data;

  }

  readC2(sm2_sequence: SM2_SEQUENCE, data: string): string {

    if (data.startsWith(ASN1Util.OCTET_STRING)) {

      data = data.slice(ASN1Util.OCTET_STRING.length, data.length);

      let C2_lenHex = this.getLenHex(data);

      data = data.slice(C2_lenHex.length, data.length)

      if (this.lenHex2number(C2_lenHex) != data.length / 2) {

        this.ciphertextErr()

      }

      sm2_sequence.C2 = data;

    } else {

      this.ciphertextErr();

    }

    return data;

  }

  readC3(sm2_sequence: SM2_SEQUENCE, data: string): string {

    if (data.startsWith("0420")) {

      sm2_sequence.C3 = data.slice(4, 68);

      data = data.slice(68, data.length);

    } else {

      this.ciphertextErr();

    }

    return data;

  }

 /** 转鸿蒙签名后的数据

 **/

  d2i_SM2_SignText(standard_data: string): string {

    let randNum:string = "";

    let signText:string = "";

    let message = standard_data.slice("30".length, standard_data.length);

    let sequence_lenHex: string = this.getLenHex(message);

    message = message.slice(sequence_lenHex.length, message.length);

    try {

      let randNum_len: string = message.slice(0, 4);

      if(randNum_len == "0220"){

        randNum = message.slice(4, 4+64);

        message = message.slice(4+64, message.length);

      } else {

        //randNum_len == "022100"

        randNum = message.slice(6, 6+64);

        message = message.slice(6+64, message.length);

      }

      let signText_len: string = message.slice(0, 4);

      if(signText_len == "0220"){

        signText = message.slice(4, 4+64);

      } else {

        //randNum_len == "022100"

        signText = message.slice(6, 6+64);

      }

    } catch (e) {

      console.error("d2i_SM2_SignText", "SignText format is err");

    }

    return randNum+signText

  }

}

export class SM2_SEQUENCE{

  private _C1x: string = "";

  private _C1y: string = "";

  private _C2: string = "";

  private _C3: string = "";

  public set C1x(value: string) {

    this._C1x = value;

  }

  public get C1x(): string {

    return this._C1x;

  }

  public set C1y(value: string) {

    this._C1y = value;

  }

  public get C1y(): string {

    return this._C1y;

  }

  public set C2(value: string) {

    this._C2 = value;

  }

  public get C2(): string {

    return this._C2;

  }

  public set C3(value: string) {

    this._C3 = value;

  }

  public get C3(): string {

    return this._C3;

  }

  public toString():string{

    return JSON.stringify(this);

  }

}

更多关于HarmonyOS 鸿蒙Next SM2加解密demo请求:按照api文档sm2无法完成解密的实战系列教程也可以访问 https://www.itying.com/category-93-b0.html


针对您提出的HarmonyOS鸿蒙Next SM2加解密demo请求,若按照API文档SM2无法完成解密,可能是以下几个原因导致的:

  1. 密钥对不匹配:确保加密使用的公钥与解密使用的私钥是一对匹配的密钥。

  2. 填充方式不一致:加密和解密过程中使用的填充方式必须一致,如PKCS#1、OAEP等。

  3. 参数设置错误:检查API调用时传入的参数,如随机数生成器、哈希函数等,确保它们符合SM2算法的要求。

  4. 数据格式问题:加密后的数据格式和解密时接收的数据格式必须一致,包括字节序、编码方式等。

  5. API版本问题:确认您使用的HarmonyOS SDK版本是否支持您正在使用的SM2 API,以及是否存在已知的bug或限制。

  6. 编码转换:在数据传输或存储过程中,确保加密数据的编码和解码方式一致,避免数据损坏。

如果以上检查均无误,但问题依旧存在,可能是由于API实现或系统环境的特定问题。此时,您可以尝试联系官网客服获取进一步的技术支持。官网地址是:https://www.itying.com/category-93-b0.html ,以便获得更专业的帮助。

回到顶部