HarmonyOS鸿蒙Next ohos.security.cryptoFramework加解密框架使用使用

HarmonyOS鸿蒙Next ohos.security.cryptoFramework加解密框架使用使用 使用的国密,需要使用sm2与sm4结合的加解密组合。

3 回复

sm2 使用公钥 加密sm4 的密钥demo如下

@Entry
@Component
struct SM2Crypto {
  @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) {
  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;
}

export async function convertStrToPriKey(keyStr: string): Promise<cryptoFramework.PriKey> {
  let sk = BigInt("0x" + keyStr)
  let priKeySpec: cryptoFramework.ECCPriKeySpec = {
    params: cryptoFramework.ECCKeyUtil.genECCCommonParamsSpec('NID_sm2'),
    sk: sk,
    algName: "SM2",
    specType: cryptoFramework.AsyKeySpecType.PRIVATE_KEY_SPEC
  }
  let keypairGenerator = cryptoFramework.createAsyKeyGeneratorBySpec(priKeySpec)
  return await keypairGenerator.generatePriKey()
}

export async function convertStrToPubKey(keyStr: string): Promise<cryptoFramework.PubKey> {
  let pubKeyStr = keyStr.startsWith("04") ? keyStr.slice(2) : keyStr
  let pkPart1 = pubKeyStr.slice(0, pubKeyStr.length / 2)
  let pkPart2 = pubKeyStr.slice(pubKeyStr.length / 2)
  let pk: cryptoFramework.Point = {
    x: BigInt("0x" + pkPart1),
    y: BigInt("0x" + pkPart2),
  }
  let pubKeySpec: cryptoFramework.ECCPubKeySpec = {
    params: cryptoFramework.ECCKeyUtil.genECCCommonParamsSpec('NID_sm2'),
    pk: pk,
    algName: "SM2",
    specType: cryptoFramework.AsyKeySpecType.PUBLIC_KEY_SPEC
  }
  let keypairGenerator = cryptoFramework.createAsyKeyGeneratorBySpec(pubKeySpec)
  return await keypairGenerator.generatePubKey()
}

function detachSM2CipherText(cipherText: cryptoFramework.DataBlob): string {
  let spec: cryptoFramework.SM2CipherTextSpec = cryptoFramework.SM2CryptoUtil.getCipherTextSpec(cipherText, 'C1C3C2');
  let c1str: string = "04" + spec.xCoordinate.toString(16) + spec.yCoordinate.toString(16);
  let c1: Uint8Array = new Uint8Array(buffer.from(c1str, 'utf-8').buffer);
  let c2: Uint8Array = spec.cipherTextData;
  let c3: Uint8Array = spec.hashData;
  let c1c2c3: Uint8Array = new Uint8Array(c1.length + c2.length + c3.length);
  c1c2c3.set(c1);
  c1c2c3.set(c2, c1.length);
  c1c2c3.set(c3, c1.length + c2.length);
  return new util.Base64Helper().encodeToStringSync(c1c2c3);
}

// SM2  main代码
async function main() {
  let base64 = new util.Base64Helper();
  
  let pubKeyStr2 = "BJZL3PngK3I93QcMCyCUbbN8DuoSjp0fn2AjtnGWDklz2Zrt4Nw0AUYtnk4/2D+/YNNQCvuvawFBn4yBnlu9jIA=";
  let u8aP = base64.decodeSync(pubKeyStr2)
  console.error('Uint8Array u8aP result string:' + u8aP);
  let buf = buffer.from(u8aP);
  console.log('hex u8aP result string:' + buf.toString('hex')); 

  let hexpubKeyStr = "04964bdcf9e02b723ddd070c0b20946db37c0eea128e9d1f9f6023b671960e4973d99aede0dc3401462d9e4e3fd83fbf60d3500afbaf6b01419f8c819e5bbd8c80"

  let pubKey = await convertStrToPubKey(hexpubKeyStr)

  // 此处为明文
  let message = 'sm4密钥';
  // 把字符串按utf-8解码为Uint8Array
  let plainText: cryptoFramework.DataBlob = { data: new Uint8Array(buffer.from(message, 'utf-8').buffer) };
  let encryptText = await encryptMessagePromise(pubKey, plainText);
  console.error("encryptText=======&gt;" + buffer.from(encryptText.data).toString('hex'))

  // reslut 是加密后的密文数据
  let spec: cryptoFramework.SM2CipherTextSpec = cryptoFramework.SM2CryptoUtil.getCipherTextSpec(encryptText, 'C1C3C2');
  let str = "04" + spec.xCoordinate.toString(16) + spec.yCoordinate.toString(16) + buffer.from(spec.cipherTextData).toString('hex') + buffer.from(spec.hashData).toString('hex')
  console.error("C1C2C3解码后16进制数据=======&gt;" + str)

  let Base64Str = base64.encodeToStringSync(new Uint8Array(buffer.from(str, 'hex').buffer))
  console.error("C1C2C3解码后数据Base64Str=======&gt;" + Base64Str)
}

export 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";
}

export class SM2_Ciphertext {
  // 用于将SM2裸密文数据序列化
  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_lenHex: string = this.genLenHex(sequence_message);

    let standard_data = "30" + sequence_lenHex + sequence_message;
    return standard_data;
  }

  // 用于将标准SM2密文数据解码
  d2i_SM2_Ciphertext(standard_data: string): string {
    let message: string = standard_data;
    if (!message.startsWith(ASN1Util.SEQUENCE)) {
      this.ciphertextErr();
    }
    message = message.slice(ASN1Util.SEQUENCE.length, message.length);

    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);
    let primal_data: string = sm2_sequence.C1x + sm2_sequence.C1y + sm2_sequence.C3 + sm2_sequence.C2;
    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() {
    hilog.error(0, "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 = 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 {
        signText = message.slice(6, 6+64);
      }
    } catch (e) {
      hilog.error(0x1, "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 ohos.security.cryptoFramework加解密框架使用使用的实战系列教程也可以访问 https://www.itying.com/category-93-b0.html


HarmonyOS鸿蒙Next中的ohos.security.cryptoFramework加解密框架提供了多种加密算法和功能,支持对称加密、非对称加密、摘要算法、密钥生成与管理等操作。

  1. 对称加密:支持AES、3DES等算法,适用于大数据量的加密和解密。
  2. 非对称加密:支持RSA、ECC等算法,适用于密钥交换和数字签名。
  3. 摘要算法:支持SHA-1、SHA-256等,用于生成数据的唯一哈希值。
  4. 密钥生成与管理:支持生成和存储对称密钥、非对称密钥对,并提供密钥的安全存储机制。

使用步骤:

  1. 初始化加密框架:通过CryptoFramework类获取加解密实例。
  2. 创建密钥生成器:使用KeyGenerator生成对称或非对称密钥。
  3. 创建加密/解密器:使用Cipher类进行加密或解密操作。
  4. 执行加解密操作:调用CipherinitupdatedoFinal方法完成加解密。

示例代码:

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

// 初始化加密框架
let cipher = cryptoFramework.createCipher('AES128|ECB|PKCS7');

// 生成密钥
let keyGenerator = cryptoFramework.createKeyGenerator('AES128');
let symKey = keyGenerator.generateKey();

// 初始化加密器
cipher.init(cryptoFramework.CryptoMode.ENCRYPT_MODE, symKey, null);

// 加密数据
let plainText = 'Hello, HarmonyOS!';
let encryptData = cipher.doFinal({ data: new Uint8Array(plainText) });

ohos.security.cryptoFramework适用于数据加密、安全通信等场景,确保数据的安全性和完整性。

HarmonyOS的ohos.security.cryptoFramework提供了丰富的加解密功能,支持对称加密、非对称加密、消息摘要等。使用时,首先通过CryptoFactory创建加密实例,然后配置密钥和算法,最后进行数据加解密操作。例如,使用AES对称加密时,需生成或导入密钥,调用cipher.init()初始化,再调用cipher.doFinal()完成加解密。开发者需根据业务需求选择合适的算法和密钥管理方式,确保数据安全。

回到顶部