HarmonyOS鸿蒙Next中国密2公钥如何转字符串使用sm4加密后与服务端交互
HarmonyOS鸿蒙Next中国密2公钥如何转字符串使用sm4加密后与服务端交互 其他端获取的公钥字符串长度是130位,鸿蒙公钥转16进制字符串后长度多很多,需要截取04以后的字符串去交互吗?如此交互后换来的服务端公钥在用sm2私钥签名后,结果服务端无法匹配。我想知道这个公钥转字符串具体还需要哪些额外操作
【背景知识】 使用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签名后复制结果回来使用可以正常使用。麻烦给个解决方案,
这种就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位长度

由于我们需要把公钥与一些其他项混合后才会送给后端,所以公钥私钥都需要做字符串处理,很多端生成的sm2公钥私钥都是字符串,长度也是130位,不明白鸿蒙该怎么处理成我要的那种格式
开发者你好,感谢您的提问,为尽快解决您的问题,需要补充以下内容:
- 其他端是如何把这些字符串转化为秘钥的。
- 公钥,密文与原文以作验证。
在HarmonyOS Next中,使用中国密SM4加密并转换为字符串与服务端交互,可通过@ohos.security.cryptoFramework的cryptoFramework模块实现。首先,使用createAsyKeyGenerator生成SM4密钥。然后,通过createCipher创建加密器,使用init方法初始化加密模式并传入公钥。加密数据后,使用base64或hex编码将加密结果转换为字符串格式传输。服务端需使用对应SM4密钥解密。
在HarmonyOS Next中处理SM2公钥与字符串转换时,需注意以下关键点:
-
公钥格式处理:鸿蒙的SM2公钥通常采用ASN.1 DER编码的X.509格式,包含版本、算法标识等完整信息。若服务端要求130字符(65字节)的裸公钥(即04||X||Y),需提取04后的64字节(128字符)十六进制串。可使用
huks.HuksKeyMaterial解析密钥材料,或通过security.cryptoFramework解码后提取X、Y坐标。 -
密钥转换示例:
// 假设已通过Huks或cryptoFramework获取公钥对象publicKey let encodedKey = publicKey.getEncoded(); // 获取DER编码 // 手动解析或使用asn1.js库提取04后的64字节 let rawPublicKey = extractRawPublicKey(encodedKey); // 自定义解析函数 -
SM4交互要点:使用SM4加密数据时,需确保双方采用相同模式(如CBC)、填充方式及IV生成逻辑。鸿蒙的
cryptoFramework支持SM4,示例:let cipher = cryptoFramework.createCipher('SM4|CBC|PKCS7'); cipher.init(cryptoFramework.CryptoMode.ENCRYPT_MODE, sm4Key, null); -
签名验证问题:若服务端无法验证签名,请确认:
- 签名算法标识是否一致(如SM3withSM2)
- 签名的数据是否包含SM2标准摘要Z值(用户ID、公钥等哈希)
- 服务端公钥格式是否与鸿蒙端处理后的格式匹配
建议直接使用security.cryptoFramework的SM2密钥生成器(createAsyKeyGenerator)和转换接口(convertKey)处理格式转换,避免手动解析误差。同时检查服务端要求的公钥编码规范是否与鸿蒙输出对齐。

