【鸿蒙实战开发】基于加解密算法框架的常见规格问题

101 篇文章 0 订阅
101 篇文章 0 订阅

场景描述

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

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

场景一:SM2加解密,安卓和HarmonyOS的sm2密文,密钥格式不符,不能直接使用,需要一定的转换。

场景二:AES加解密,缺少基础的加解密示例,在原有的文档示例基础上不知道如何修改。

方案描述

场景一:

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

方案

1、对于传入的密钥中公钥是带04的的十六进制的130位字符串,在传入的时候,密钥参数对应的格式为 04+x+y,x和y的长度是一致的,私钥的十六进制就直接放入对应的参数即可

传入不带04的十六进制的128位字符串,对应的格式就是x+y,代码中 keyStr.startsWith(“04”) ? keyStr.slice(2) : keyStr正是为了判断这个。

2、对于安卓加密的密文,HarmonyOS这边的格式是ASN.1包裹的格式,因此HarmonyOS这边解密的时候,需要先序列化:HexStrTouint8Array(new SM2_Ciphertext().i2d_SM2_Ciphertext(“安卓的密文”));同理HarmonyOS生成的密文要先解码:new SM2_Ciphertext().d2i_SM2_Ciphertext(uint8ArrayToHexStr(HarmonyOS密文)),其中安卓的密文为十六进制字符串,HarmonyOS密文为Uint8Array数组

具体实现如下:

效果图

核心代码

根据密钥参数生成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

}

使用过程中安卓和 HarmonyOS 的格式转换介绍

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)

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

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

  //针对安卓的密文处理,转成HarmonyOS可用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

分组模式适用的加解密方式所需参数备注
ECBAES、SM4、3DES没有偏移量等参数
CBC、CTR、OFB、CFBAES、SM4、3DES(不支持CTR)指明加解密参数iv。
* AES的iv长度为16字节
* 3DES的iv长度为8字节
* SM4iv长度为16字节。
GCMAES指明加解密参数iv,长度为116字节,常用为12字节。<br/>指明加解密参数aad,长度为0INT_MAX字节,常用为16字节。
指明加解密参数authTag,长度为16字节。
在GCM模式下,需要从加密后的数据中取出末尾16字节,作为解密时初始化的认证信息

效果图

核心代码

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;

}

鸿蒙全栈开发全新学习指南

为了积极培养鸿蒙生态人才,让大家都能学习到鸿蒙开发最新的技术,针对一些在职人员、0基础小白、应届生/计算机专业、鸿蒙爱好者等人群,整理了一套纯血版鸿蒙(HarmonyOS Next)全栈开发技术的学习路线【包含了大厂APP实战项目开发】

本路线共分为四个阶段

第一阶段:鸿蒙初中级开发必备技能

在这里插入图片描述

第二阶段:鸿蒙南北双向高工技能基础:gitee.com/MNxiaona/733GH

第三阶段:应用开发中高级就业技术

第四阶段:全网首发-工业级南向设备开发就业技术:gitee.com/MNxiaona/733GH

《鸿蒙 (Harmony OS)开发学习手册》(共计892页)

如何快速入门?

1.基本概念
2.构建第一个ArkTS应用
3.……

开发基础知识:gitee.com/MNxiaona/733GH

1.应用基础知识
2.配置文件
3.应用数据管理
4.应用安全管理
5.应用隐私保护
6.三方应用调用管控机制
7.资源分类与访问
8.学习ArkTS语言
9.……

基于ArkTS 开发

1.Ability开发
2.UI开发
3.公共事件与通知
4.窗口管理
5.媒体
6.安全
7.网络与链接
8.电话服务
9.数据管理
10.后台任务(Background Task)管理
11.设备管理
12.设备使用信息统计
13.DFX
14.国际化开发
15.折叠屏系列
16.……

鸿蒙开发面试真题(含参考答案):gitee.com/MNxiaona/733GH

鸿蒙入门教学视频:

美团APP实战开发教学:gitee.com/MNxiaona/733GH

写在最后

  • 如果你觉得这篇内容对你还蛮有帮助,我想邀请你帮我三个小忙:
  • 点赞,转发,有你们的 『点赞和评论』,才是我创造的动力。
  • 关注小编,同时可以期待后续文章ing🚀,不定期分享原创知识。
  • 想要获取更多完整鸿蒙最新学习资源,请移步前往小编:gitee.com/MNxiaona/733GH

  • 4
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值