往期知识点整理
- 鸿蒙(HarmonyOS)北向开发知识点记录~
- 【鸿蒙实战开发】ArkTS多线程的多线程系列(一):ArkTS多线能力入门
- 【鸿蒙实战开发】ArkTS多线程的多线程系列(二):基于Sendable共享对象实现跨线程通信及UI状态刷新
- 【鸿蒙实战开发】ArkTS多线性的多线程系列(三):基于单例实现跨线程缓存
- 【鸿蒙实战开发】ArkTS多线程的多线程系列(四):基于生产者-消费者实现多线程协同
- 【鸿蒙实战开发】ArkTS多线程的多线程系列(五):通过子线程实现全局弹窗
- 【鸿蒙UI实战开发】基于List和Scroller由简单到复杂列表布局开发实践
- 【鸿蒙UI实战开发】基于原生能力的键盘控制
- 【鸿蒙UI实战开发】基于ArkUI现有能力实现自定义弹窗封装方案
- 【鸿蒙实战开发】Native保存图片到应用沙箱
- 【鸿蒙实战开发】基于加解密算法框架的常见规格问题
- 持续更新中……
场景描述
对于加解密在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
分组模式 | 适用的加解密方式 | 所需参数 | 备注 |
---|---|---|---|
ECB | AES、SM4、3DES | 没有偏移量等参数 | |
CBC、CTR、OFB、CFB | AES、SM4、3DES(不支持CTR) | 指明加解密参数iv。 * AES的iv长度为16字节 * 3DES的iv长度为8字节 * SM4iv长度为16字节。 | |
GCM | AES | 指明加解密参数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;
}
总是有很多小伙伴反馈说:鸿蒙开发不知道学习哪些技术?不知道需要重点掌握哪些鸿蒙开发知识点? 为了解决大家这些学习烦恼。在这准备了一份很实用的鸿蒙全栈开发学习路线与学习文档给大家用来跟着学习。
针对一些列因素,整理了一套纯血版鸿蒙(HarmonyOS Next)全栈开发技术的学习路线,包含了鸿蒙开发必掌握的核心知识要点,内容有(OpenHarmony多媒体技术、Napi组件、OpenHarmony内核、OpenHarmony驱动开发、系统定制移植……等)技术知识点。
《鸿蒙 (Harmony OS)开发学习手册》(共计892页):https://gitcode.com/HarmonyOS_MN/733GH/overview
如何快速入门?
1.基本概念
2.构建第一个ArkTS应用
3.……
开发基础知识:
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.……
鸿蒙开发面试真题(含参考答案):https://gitcode.com/HarmonyOS_MN/733GH/overview
OpenHarmony 开发环境搭建
《OpenHarmony源码解析》:https://gitcode.com/HarmonyOS_MN/733GH/overview
- 搭建开发环境
- Windows 开发环境的搭建
- Ubuntu 开发环境搭建
- Linux 与 Windows 之间的文件共享
- ……
- 系统架构分析
- 构建子系统
- 启动流程
- 子系统
- 分布式任务调度子系统
- 分布式通信子系统
- 驱动子系统
- ……