HarmonyOS鸿蒙Next中国密2公钥如何转字符串使用sm4加密后与服务端交互

HarmonyOS鸿蒙Next中国密2公钥如何转字符串使用sm4加密后与服务端交互 其他端获取的公钥字符串长度是130位,鸿蒙公钥转16进制字符串后长度多很多,需要截取04以后的字符串去交互吗?如此交互后换来的服务端公钥在用sm2私钥签名后,结果服务端无法匹配。我想知道这个公钥转字符串具体还需要哪些额外操作

12 回复

【背景知识】 使用ECC压缩/非压缩点格式转换(ArkTS):支持将压缩/非压缩的点数据转换为Point对象,用于密钥对象生成;也支持将Point对象转换为压缩/非压缩的点数据。 服务端使用的是明文私钥和公钥,需要提取参数转化成明文私钥公钥与服务端交互。

  • 标准的SM2公钥长度一般为65字节,其中前导04是固定的。
  • 32个字节的私钥在某些情况下被省略了前导的零,需要手动在前面补上00才能构成完整的私钥。

【解决方案】 通过密钥获取密钥参数X和Y合成明文公钥,获取SK参数转化成明文私钥。

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

// Uint8Array to hexadecimal
function uint8ArrayToHexStr(data: Uint8Array): string {
  let hexString = '';
  let i: number;
  for (i = 0; i < data.length; i++) {
    let char = ('00' + data[i].toString(16)).slice(-2);
    hexString += char;
  }
  return hexString;
}

function padding(data: string): string {
  while (data.length < 64) {
    data = '0' + data;
  }
  return data;
}

function gen() {
  let keypairGenerator = cryptoFramework.createAsyKeyGenerator('SM2_256');
  let keyPair = keypairGenerator.generateKeyPairSync();
  let oldPk = uint8ArrayToHexStr(keyPair.pubKey.getEncoded().data);
  let oldSk = uint8ArrayToHexStr(keyPair.priKey.getEncoded().data);
  console.info(`SM2 oldPk.len = ${oldPk.length}`);
  console.info(`SM2 oldSk.len = ${oldSk.length}`);

  let x = keyPair.pubKey.getAsyKeySpec(cryptoFramework.AsyKeySpecItem.ECC_PK_X_BN);
  let y = keyPair.pubKey.getAsyKeySpec(cryptoFramework.AsyKeySpecItem.ECC_PK_Y_BN);
  let pk = '04' + padding(x.toString(16)) + padding(y.toString(16));
  console.info(`pk.len=${pk.length},pk:${pk}`);

  let priKey = keyPair.priKey.getAsyKeySpec(cryptoFramework.AsyKeySpecItem.ECC_SK_BN);
  let sk = padding(priKey.toString(16));
  console.info(`sk.len=${sk.length},sk:${sk}`);
}

@Entry
@Component
struct Index {
  build() {
    RelativeContainer() {
      Text('Hello World')
        .id('HelloWorld')
        .fontSize($r('app.float.page_text_font_size'))
        .fontWeight(FontWeight.Bold)
        .alignRules({
          center: { anchor: '__container__', align: VerticalAlign.Center },
          middle: { anchor: '__container__', align: HorizontalAlign.Center }
        })
        .onClick(() => {
          gen();
        });
    }
    .height('100%')
    .width('100%');
  }
}

更多关于HarmonyOS鸿蒙Next中国密2公钥如何转字符串使用sm4加密后与服务端交互的实战系列教程也可以访问 https://www.itying.com/category-93-b0.html


这个方式可以。现在我进行到了最后一步,使用sm2签名,对应iOS方法为: [GMSm2Utils derEncode:[GMSm2Utils signText:str privateKey:key userID:nil]] 现在的问题是:arkts使用sm2签名后长度是142,与需要的rs128位不符,使用这个方案后位数变成了128位,但也不对,且无法转格式,转格式会失败报错。私钥和签名文本使用iOS签名后复制结果回来使用可以正常使用。麻烦给个解决方案,

SM2签名数据格式转换

这种就api版本我们没升到这么高,有替代方案吗,

有要学HarmonyOS AI的同学吗,联系我:https://www.itying.com/goods-1206.html

根据编码规则可以自行提取https://support.huaweicloud.com/dew_faq/dew_01_0344.html。

【背景知识】 在数据存储或传输场景中,可以使用加解密操作用于保证数据的机密性,防止敏感数据泄露,加解密开发可参考加解密开发指导算法库当前提供了SM4加解密常用的7种加密模式:ECB、CBC、CTR、OFB、CFB、CFB128和GCM。 由于SM4为分组加密算法,分组长度为128位。在实际应用中,最后一组明文可能不足128位(16字节),此时可以通过不同的填充模式进行数据填充。

【解决方案】

  • SM4加密代码如下:

    import { nfcController } from '@kit.ConnectivityKit';
    import { promptAction } from '@kit.ArkUI';
    import { cryptoFramework } from '@kit.CryptoArchitectureKit';
    import { buffer } from '@kit.ArkTS';
    
    @Entry
    @Component
    struct Index {
      build() {
        Column() {
          Text('SM4加密')
            .fontSize(50)
            .fontWeight(FontWeight.Bold)
            .onClick(async()=>{
              const symAlgName = 'SM4_128'
              const sKey: string = '9f35eda67432c4ae3892305801b9d0b6';
              const symKeyData = buffer.from(sKey, 'hex')
              let symKeyBlob: cryptoFramework.DataBlob = { data: new Uint8Array(symKeyData.buffer) };
              try {
                let aesGenerator = cryptoFramework.createSymKeyGenerator(symAlgName);
                let symKey = await aesGenerator.convertKey(symKeyBlob);
                console.info(`wsf: sm4 转密钥成功`)
                let plainText: cryptoFramework.DataBlob = { data: new Uint8Array(buffer.from('This is a test', 'utf-8').buffer) };
                const cipher = cryptoFramework.createCipher('SM4_128|ECB|PKCS7')
                await cipher.init(cryptoFramework.CryptoMode.ENCRYPT_MODE, symKey, null)
                const encryptData = await cipher.doFinal(plainText)
                console.info(`wsf: 加密成功`)
              } catch (e) {
                console.error(`wsf: 转密钥报错 e = ${e.code} ${e.message}`)
              }
            })
        }
        .height('100%')
        .width('100%')
      }
    }
    
  • SM4解密代码如下:

    import { nfcController } from '@kit.ConnectivityKit';
    import { promptAction } from '@kit.ArkUI';
    import { cryptoFramework } from '@kit.CryptoArchitectureKit';
    import { buffer } from '@kit.ArkTS';
    
    @Entry
    @Component
    struct Index {
      build() {
        Column() {
          Text('SM4解密')
            .fontSize(50)
            .fontWeight(FontWeight.Bold)
            .onClick(async()=>{
              const symAlgName = 'SM4_128'
              const sKey: string = '9f35eda67432c4ae3892305801b9d0b6';
              const symKeyData = buffer.from(sKey, 'hex')
              let symKeyBlob: cryptoFramework.DataBlob = { data: new Uint8Array(symKeyData.buffer) };
              try {
                let aesGenerator = cryptoFramework.createSymKeyGenerator(symAlgName);
                let symKey = await aesGenerator.convertKey(symKeyBlob);
                let arr = [186,255,20,91,43,218,215,77,185,100,22,186,42,178,121,45];
                let dataAad = new Uint8Array(arr);
                let encryptData: cryptoFramework.DataBlob = { data: dataAad };
                let decoder = cryptoFramework.createCipher('SM4_128|ECB|PKCS7');
                await decoder.init(cryptoFramework.CryptoMode.DECRYPT_MODE, symKey, null);
                let decryptData = await decoder.doFinal(encryptData);
                console.info(`wsf: 解密成功 ${buffer.from(decryptData.data).toString()}`)
              } catch (e) {
                console.error(`wsf: 转密钥报错 e = ${e.code} ${e.message}`)
              }
            })
        }
        .height('100%')
        .width('100%')
      }
    }
    

不是的哥,我其实就是想把arkts生成的公钥私钥对转成字符串,因为我们会对公钥字符串与其他字符串进行穿插混合后用sm4加密上传服务器,然后sm4解密获取服务器公钥(交换密钥),后再与其他字符串穿插混合再sm4加密,sm2私钥签名,上传服务器,这时后服务器进行公钥私钥进行对比,然后发现不匹配导致我这里没办法进行下去。 我其实就是想知道 sm2生成的公钥私钥如何转成其他端那种直接生成的字符串格式,

已答复,

https://github.com/muzipiao/GMObjC 这是iOS的库 生成的公钥私钥对

pub: 04A3C51A3CDCBFD00434F06A81618E4891940BDF5D2228BF4FC042E3190601D4647DAC0E529D3E293027F1CDD4D012473B77DC75B19B5AEF67A5DC630A034FE9D4,

pri: A8C8E73D7DC84F6F5C3CE4B8B83E3E027F29D322C1F997F71A4D61C660F9F1FA

可以在https://lzltool.cn/SM2 在线加解密

但是我

let asyKeyGenerator = cryptoFramework.createAsyKeyGenerator('SM2_256');
const keyPair = asyKeyGenerator.generateKeyPairSync()

这样生成的公钥私钥对

pub:3059301306072a8648ce3d020106082a811ccf5501822d03420004364aee52bc1b9e7f49e1ff0a19a9a69d502418a8305fb440ed0331afcd71fc5c95cd5c50185c1ff6dc1a98685413a7626c91dfd4645d76c8cb205101231f16d5

pri:303102010104201a75fcf8a86e5f4a64768f2be65fc9f34ff4781ca6ae5164eadf05ed01361ee8a00a06082a811ccf5501822d

转成字符串无法成功将密文转回,即使我把字符串长度转为和iOS一样的04开头截取130位长度

cke_8379.png

由于我们需要把公钥与一些其他项混合后才会送给后端,所以公钥私钥都需要做字符串处理,很多端生成的sm2公钥私钥都是字符串,长度也是130位,不明白鸿蒙该怎么处理成我要的那种格式

开发者你好,感谢您的提问,为尽快解决您的问题,需要补充以下内容:

  1. 其他端是如何把这些字符串转化为秘钥的。
  2. 公钥,密文与原文以作验证。

在HarmonyOS Next中,使用中国密SM4加密并转换为字符串与服务端交互,可通过@ohos.security.cryptoFrameworkcryptoFramework模块实现。首先,使用createAsyKeyGenerator生成SM4密钥。然后,通过createCipher创建加密器,使用init方法初始化加密模式并传入公钥。加密数据后,使用base64hex编码将加密结果转换为字符串格式传输。服务端需使用对应SM4密钥解密。

在HarmonyOS Next中处理SM2公钥与字符串转换时,需注意以下关键点:

  1. 公钥格式处理:鸿蒙的SM2公钥通常采用ASN.1 DER编码的X.509格式,包含版本、算法标识等完整信息。若服务端要求130字符(65字节)的裸公钥(即04||X||Y),需提取04后的64字节(128字符)十六进制串。可使用huks.HuksKeyMaterial解析密钥材料,或通过security.cryptoFramework解码后提取X、Y坐标。

  2. 密钥转换示例

    // 假设已通过Huks或cryptoFramework获取公钥对象publicKey
    let encodedKey = publicKey.getEncoded(); // 获取DER编码
    // 手动解析或使用asn1.js库提取04后的64字节
    let rawPublicKey = extractRawPublicKey(encodedKey); // 自定义解析函数
    
  3. SM4交互要点:使用SM4加密数据时,需确保双方采用相同模式(如CBC)、填充方式及IV生成逻辑。鸿蒙的cryptoFramework支持SM4,示例:

    let cipher = cryptoFramework.createCipher('SM4|CBC|PKCS7');
    cipher.init(cryptoFramework.CryptoMode.ENCRYPT_MODE, sm4Key, null);
    
  4. 签名验证问题:若服务端无法验证签名,请确认:

    • 签名算法标识是否一致(如SM3withSM2)
    • 签名的数据是否包含SM2标准摘要Z值(用户ID、公钥等哈希)
    • 服务端公钥格式是否与鸿蒙端处理后的格式匹配

建议直接使用security.cryptoFrameworkSM2密钥生成器(createAsyKeyGenerator)和转换接口(convertKey)处理格式转换,避免手动解析误差。同时检查服务端要求的公钥编码规范是否与鸿蒙输出对齐。

回到顶部