1

场景描述

对于加解密在鸿蒙和安卓相互转换,以及鸿蒙安卓互调的各种场景下使用密文密钥的问题。

应用经常会遇到如下的业务诉求:

场景一:SM2加解密,安卓和鸿蒙的sm2密文,密钥格式不符,不能直接使用,需要一定的转换。
场景二:AES加解密,缺少基础的加解密示例,在原有的文档示例基础上不知道如何修改。

方案描述

场景一

对于使用sm2加解密,安卓生成的密钥拿到鸿蒙使用如何导入,密文如何去转换、鸿蒙生成的密文如何拿到安卓去解密。

方案

  1. 对于传入的密钥中公钥是带04的的十六进制的130位字符串,在传入的时候,密钥参数对应的格式为 04+x+y,x和y的长度是一致的,私钥的十六进制就直接放入对应的参数即可
  2. 传入不带04的十六进制的128位字符串,对应的格式就是x+y,代码中 keyStr.startsWith("04") ? keyStr.slice(2) : keyStr正是为了判断这个。
  3. 对于安卓加密的密文,鸿蒙这边的格式是ASN.1包裹的格式,因此鸿蒙这边解密的时候,需要先序列化:HexStrTouint8Array(new SM2_Ciphertext().i2d_SM2_Ciphertext("安卓的密文"));同理鸿蒙生成的密文要先解码:new SM2_Ciphertext().d2i_SM2_Ciphertext(uint8ArrayToHexStr(鸿蒙密文)),其中安卓的密文为十六进制字符串,鸿蒙密文为Uint8Array数组

具体实现如下:

效果图

image.png
image.png

核心代码

根据密钥参数生成sm2私钥

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() 
}

根据密钥参数生成sm2公钥

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() 
}

加密消息

async function encryptMessagePromise(publicKey: cryptoFramework.PubKey, plainText: string) { 
  let cipher = cryptoFramework.createCipher('SM2_256|SM3') 
  await cipher.init(cryptoFramework.CryptoMode.ENCRYPT_MODE, publicKey, null) 
  let encryptData = await cipher.doFinal({ data:stringToUint8Array(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 test(data: string) { 
  //十六进制的公私钥 
  let pubKeyStr = "0453402B95F3584F36B9A7129A6B5C6109F2DBC7C94BE7858DB66C48AF38CB5C3B76883EE4BF18E270607191E233EAC0A95ECFB8EF6FE80C5F782DE24F018DEB5F" 
  let priKeyStr = "5B9270E0ADF86A101167610FCCD375A6549DC14E9225951EF3A4640F26D6CD9C" 
  //安卓加密后的密文 
  let a = "53ce193ad865c6d97742da78b18a21d0ca66200fe080284d774d5500915be2425cea2f310c9a423bc2d08ce5c1e78a75cfd66d88688a0e2076a45614307e4372aa10b514841cfe7bff08fc82d96bdf35754696571e5fbedd552d1ab7c54bff796a0e3fd72902"; 
  //根据密钥参数生成对应的公私钥 
  let pk = await convertStrToPubKey(pubKeyStr) 
  let sk = await convertStrToPriKey(priKeyStr) 
  //加密 
  let encryptText = await encryptMessagePromise(pk, data) 
  //将加密的密文数据解码转换为安卓可用数据(用于鸿蒙和安卓的交接) 
  let b = new SM2_Ciphertext().d2i_SM2_Ciphertext(uint8ArrayToHexStr(encryptText.data)) 
  console.log("解码后数据=======>" + b) 
  //解密得到结果 
  let res = await decryptMessagePromise(sk, encryptText) 
  console.log("a=======>" + uint8ArrayToString(res.data)) 
  //针对安卓的密文处理,转成鸿蒙可用uint8Array数组数据 
  let c = HexStrTouint8Array(new SM2_Ciphertext().i2d_SM2_Ciphertext(a)) 
  //对安卓生成的的密文进行解密 
  let resa = await decryptMessagePromise(sk, { data:c }) 
}

场景二

缺少基础的加解密示例(AES|ECB|PKCS7 demo)

方案

对于不同的分组模式下表中给出了相应的参数适用说明,代码以AES128为例,这里的密钥传入的为base64格式,偏移量IV为字符串,对于格式的可以参考格式转换。对于GCM的参数设置,这里给了IV的,其余参数参考IV的写法即可。模板中使用的加解密算法以及密钥规格可以参考以下链接:
对称密钥加解密算法规格:https://developer.huawei.com/consumer/cn/doc/harmonyos-guides-V5/crypto-sym-encrypt-decrypt-spec-0000001774120458-V5?catalogVersion=V5
对称密钥生成和转换规格:https://developer.huawei.com/consumer/cn/doc/harmonyos-guides-V5/crypto-sym-key-generation-conversion-spec-0000001821000065-V5

image.png

效果图

image.png
image.png

核心代码

ECB加解密模板

//加密 
async function aesEncrypt(text:string,puKey:string): Promise<string>{ 
  let globalResult = "" 
  try { 
    //这里已AES加解密为例支持AES、SM4、3DES 
    let cipherAlgName = 'AES128|ECB|PKCS7'; 
    // 创建加解密对象 
    let globalCipher = cryptoFramework.createCipher(cipherAlgName); 
    //这里已AES加解密为例支持AES、SM4、3DES 
    let symAlgName = 'AES128'; 
    //创建密钥对象 
    let symKeyGenerator = cryptoFramework.createSymKeyGenerator(symAlgName); 
    //将传入的base格式的密钥转为Uint8Array数组 
    let dataUint8Array = base.decodeSync(puKey) 
    let keyBlob: cryptoFramework.DataBlob = { data: dataUint8Array } 
    //导入外部密钥 
    let promiseSymKey = await symKeyGenerator.convertKey(keyBlob) 
    //初始化 
    await globalCipher.init(cryptoFramework.CryptoMode.ENCRYPT_MODE, promiseSymKey, null); 
    //加密 
    let result = await globalCipher.doFinal({data:stringToUint8Array(text)}) 
    //将加密结果转换为base64格式,用于保存或者传递 
    globalResult = base.encodeToStringSync(result.data); 
  } catch (err) { 
    console.log(err.message) 
  } 
  return globalResult; 
}
// 解密 
async function aesDecrypt(text: string, key: string) { 
  let globalResult = "" 
  try { 
    //这里已AES加解密为例支持AES、SM4、3DES 
    let cipherAlgName = 'AES128|ECB|PKCS7'; 
    // 创建加解密对象 
    let globalCipher = cryptoFramework.createCipher(cipherAlgName); 
    //这里已AES加解密为例支持AES、SM4、3DES 
    let symAlgName = 'AES128'; 
    //创建密钥对象 
    let symKeyGenerator = cryptoFramework.createSymKeyGenerator(symAlgName); 
    //将传入的base格式的密钥转为Uint8Array数组 
    let dataUint8Array = base.decodeSync(key) 
    let keyBlob: cryptoFramework.DataBlob = { data: dataUint8Array } 
    //导入外部密钥 
    let promiseSymKey = await symKeyGenerator.convertKey(keyBlob) 
    await globalCipher.init(cryptoFramework.CryptoMode.DECRYPT_MODE, promiseSymKey, null); 
    let plainText: cryptoFramework.DataBlob = { data: base.decodeSync(text) } 
    let result = await globalCipher.doFinal(plainText) 
    //将解密后的结果result解码之后得到明文 
    globalResult = uint8ArrayToString(result.data); 
    console.log("解密后的明文==》" + globalResult) 
  } catch (err) { 
    console.log(err.message) 
  } 
}

CBC加解密模板

//加密 
async function aesEncrypt(text: string, key: string, iv:string): Promise<string> { 
  let globalResult = "" 
  try { 
    //这里已AES加解密为例支持AES、SM4、3DES 
    let cipherAlgName = 'AES128|CBC|PKCS7'; 
    let globalCipher = cryptoFramework.createCipher(cipherAlgName); 
    //这里已AES加解密为例支持AES、SM4、3DES 
    let symAlgName = 'AES128'; 
    let symKeyGenerator = cryptoFramework.createSymKeyGenerator(symAlgName); 
    let dataUint8Array = base.decodeSync(key) 
    let keyBlob: cryptoFramework.DataBlob = { data: dataUint8Array } 
    let promiseSymKey = await symKeyGenerator.convertKey(keyBlob) 
 
    let ivData = stringToUint8Array(iv); 
    let ivdata: cryptoFramework.DataBlob = { data: ivData }; //偏移 
    let iv: cryptoFramework.IvParamsSpec = { iv: ivdata, algName: 'IvParamsSpec' } //cbc 模式的参数 
 
    await globalCipher.init(cryptoFramework.CryptoMode.ENCRYPT_MODE, promiseSymKey, iv); 
    let plainText: cryptoFramework.DataBlob = { data: this.stringToUint8Array(text) } 
    let result = await globalCipher.doFinal(plainText) 
    globalResult = base.encodeToStringSync(result.data); 
  } catch (err) { 
    console.log(err.message) 
  } 
 
  return globalResult; 
}
// 解密 
async function aesDecrypt(text: string, key: string,iv:string) { 
  let globalResult = "" 
  try { 
    let cipherAlgName = 'AES128|CBC|PKCS7'; 
    let globalCipher = cryptoFramework.createCipher(cipherAlgName); 
 
    let symAlgName = 'AES128'; 
    let symKeyGenerator = cryptoFramework.createSymKeyGenerator(symAlgName); 
 
    let dataUint8Array = base.decodeSync(key) 
    let keyBlob: cryptoFramework.DataBlob = { data: dataUint8Array } 
    let promiseSymKey = await symKeyGenerator.convertKey(keyBlob) 
 
    // /*设置偏移量 */ 
    let ivData = stringToUint8Array(iv); 
    let ivdata: cryptoFramework.DataBlob = { data: ivData }; //偏移 
    let iv: cryptoFramework.IvParamsSpec = { iv: ivdata, algName: 'IvParamsSpec' } //cbc 模式的参数 
 
    await globalCipher.init(cryptoFramework.CryptoMode.DECRYPT_MODE, promiseSymKey, globalCbcParams); 
    let plainText: cryptoFramework.DataBlob = { data: base.decodeSync(text) } 
    let result = await globalCipher.doFinal(plainText) 
    globalResult = uint8ArrayToString(result.data); 
    console.log("解密后的明文==》" + globalResult) 
  } catch (err) { 
    console.log(err.message) 
  } 
  return globalResult; 
}

GCM加解密模板

//GCM的参数设置 
function genGcmParamsSpec() { 
  let arr = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]; // 12 bytes 
  let dataIv = new Uint8Array(arr); 
  let ivBlob: cryptoFramework.DataBlob = { data: dataIv }; 
  arr = [0, 0, 0, 0, 0, 0, 0, 0]; // 8 bytes 
  let dataAad = new Uint8Array(arr); 
  let aadBlob: cryptoFramework.DataBlob = { data: dataAad }; 
  arr = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]; // 16 bytes 
  let dataTag = new Uint8Array(arr); 
  let tagBlob: cryptoFramework.DataBlob = { 
    data: dataTag 
  }; 
  // GCM的authTag在加密时从doFinal结果中获取,在解密时填入init函数的params参数中 
  let gcmParamsSpec: cryptoFramework.GcmParamsSpec = { 
    iv: ivBlob, 
    aad: aadBlob, 
    authTag: tagBlob, 
    algName: "GcmParamsSpec" 
  }; 
  return gcmParamsSpec; 
}
//加密 
export async function aesEncryptGCM(text: string, key: string,iv:string): Promise<string> { 
  let globalResult = "" 
  try { 
    let cipherAlgName = 'AES128|GCM|PKCS5'; 
    let globalCipher = cryptoFramework.createCipher(cipherAlgName); 
 
    let symAlgName = 'AES128'; 
    let symKeyGenerator = cryptoFramework.createSymKeyGenerator(symAlgName); 
    let dataUint8Array = stringToUint8Array(key) 
    let keyBlob: cryptoFramework.DataBlob = { data: dataUint8Array } 
    let promiseSymKey = await symKeyGenerator.convertKey(keyBlob) 
    let getParamsSpec: cryptoFramework.GcmParamsSpec = genGcmParamsSpec(); 
    getParamsSpec.iv = { data: stringToUint8Array(iv) } 
    await globalCipher.init(cryptoFramework.CryptoMode.ENCRYPT_MODE, promiseSymKey, getParamsSpec); 
 
    let plainText: cryptoFramework.DataBlob = { data: stringToUint8Array(text) } 
    let res = await globalCipher.doFinal(plainText) 
    authTag = res.data.subarray(res.data.length - 16, res.data.length)//authTag 
    let a = res.data.subarray(0, res.data.length - authTag.length);//密文 
    globalResult = base.encodeToStringSync(a); 
  } catch (err) { 
    console.log(err.message) 
  } 
  return globalResult; 
}
// 解密 
export async function aesDecryptGCM(text: string, key: string) { 
  let globalResult = "" 
  try { 
    let cipherAlgName = 'AES128|GCM|PKCS5'; 
    let globalCipher = cryptoFramework.createCipher(cipherAlgName); 
    let symAlgName = 'AES128'; 
    let symKeyGenerator = cryptoFramework.createSymKeyGenerator(symAlgName); 
    let dataUint8Array = stringToUint8Array(key) 
    let keyBlob: cryptoFramework.DataBlob = { data: dataUint8Array } 
    let promiseSymKey = await symKeyGenerator.convertKey(keyBlob) 
    let getParamsSpec: cryptoFramework.GcmParamsSpec = genGcmParamsSpec(); 
    getParamsSpec.authTag = {data:authTag} 
    getParamsSpec.iv = { data: stringToUint8Array(iv) } 
    await globalCipher.init(cryptoFramework.CryptoMode.DECRYPT_MODE, promiseSymKey, getParamsSpec); 
    let plainText: cryptoFramework.DataBlob = { data: base.decodeSync(text) } 
    let result = await globalCipher.doFinal(plainText) 
    globalResult = uint8ArrayToString(result.data); 
    console.log("解密后的明文==》" + globalResult) 
  } catch (err) { 
    console.log(err.message) 
  } 
  return globalResult; 
}

HarmonyOS码上奇行
5k 声望2.5k 粉丝