赞
踩
根据研究机构Counterpoint Research发布的最新数据,2024年第一季度,鸿蒙OS份额由去年一季度的8%上涨至17%,iOS份额则从20%下降至16%。
这意味着,华为鸿蒙OS在中国市场的份额超越苹果iOS,已成中国第二大操作系统。
随着鸿蒙市场份额的不断提升,相应的岗位也会迎来一个爆发式的增长。这对于想要换赛道的程序员来说是一个非常好的消息,话说大家最近有想法转型鸿蒙开发吗?
这篇文章主要是讲一下鸿蒙开发过程中的对称加密
HarmonyOS(鸿蒙)SDK API9支持两种对称加密算法:a. AES(Advanced Encryption Standard) b.3DES(也称为 3DESede 或 TripleDES).
官方也给出了一些样例使用代码,可供开发者参考。
本篇从实践出发,完整的通过代码方式来深入了解HarmonyOS中的对称加密用法。
字符串样例:AES256|EBC|PKCS5
1.密钥算法 : AES
2.密文组长度 : 256
3.分组模式 : EBC
4.填充模式 : PCKCS5
关于“AES算法”,“分组模式”,“填充模式”的具体概念,可自行搜索。
加密使用的测试数据源有两个,具体见下图
1.“125字节” (即,占用了125个字节的字符串)
2.“128字节” (即,占用了128个字节的字符串)
在这里插入图片描述
3DES加密算法密文分组长度有三种:128,192,256;支持7种分组模式:ECB,CBC,OFB,CFB,CTR,GCM,CCM;支持3种填充模式:NoPadding, PKCS5, PKCS7
本篇仅以256密文组长度做为实践验证,默认选择“128字节”数据集
片段代码以 “AES256|ECB|PKCS5” 为例
1.生成对动态密钥
//导入加密框架 import cryptoFramework from '@ohos.security.cryptoFramework'; ...... //创建密钥生成器,参数为(密钥算法+密文组长度,如:AES256) let symKeyGenerator = cryptoFramework.createSymKeyGenerator('AES256'); //生成对称密钥 let promiseSymKey = symKeyGenerator.generateSymKey(); //获取密钥 promiseSymKey( key => { //动态密钥 this.symBinKey = key.getEncoded().data; })
2.初始化Cipher
//创建Cipher
globalCipher = cryptoFramework.createCipher('AES256|ECB|PKCS5');
//初始化Cipher
let mode = cryptoFramework.CryptoMode.ENCRYPT_MODE;
globalCipher.init(mode, key, null)
3.加密
//文字转换为Uint8Array const encoder = new util.TextEncoder() let u8a_encoder = encoder.encodeInto('测试') //封装Uint8Array 数据,必须是一个带data属性的对象 let plainText = { data: u8a_encoder }; //开始加密 let promiseUpdate = globalCipher.update(plainText); //获取加密结果 promiseUpdate( result => { //密文 let this.cipherText = result.data })
4.结束加密
上述虽然已经完成了加密,但是需要加密的文字字节长度不一定是128的整数倍,所以使用填充模式会弥补不足位的数据,并且在update之后,采用doFinal的方式结束最终加密
//结束加密
let promiseFinal = globalCipher.doFinal(null);
//获取剩余结果
promiseFinal( finalResult => {
if(finalResult != null){
//剩余加密结果
let authTag = finalResult.data
}
})
1.生成密钥对象
我们这里使用加密过程中产生的密钥数据。注意此密钥数据不能直接用来构建Key对象(即 密钥对象),正确的方法是通过API来生成密钥对象
//准备密钥数据,this.symBinKey 是上述过程生成的密钥
let keyMaterialBlob: cryptoFramework.DataBlob = { data: this.symBinKey };
//创建密钥生成器,参数为(密钥算法+密文组长度,如:AES256)
let symKeyGenerator = cryptoFramework.createSymKeyGenerator('AES256');
//密钥生成器使用密钥数据,开始生成密钥对象
symKeyGenerator.convertKey(keyMaterialBlob).then( key => {
//key 为生成的密钥对象
})
2.初始化Cipher
//创建Cipher
globalCipher = cryptoFramework.createCipher('AES256|EBC|PKCS5');
//初始化Cipher,参数key由第一步生成
let mode = cryptoFramework.CryptoMode.DECRYPT_MODE;
globalCipher.init(mode, key, null)
3.解密
//mergeResult 代表密文,本篇文章中,此值来源于上述加密结果
let promiseUpdate = globalCipher.update({ data: mergeResult });
4.结束解密
//结束解密
let promiseFinal = globalCipher.doFinal(null);
//获取剩余结果
promiseFinal( finalResult => {
if(finalResult != null){
//有剩余解密结果
} else {
//无剩余解密结果
}
})
1.采用GCM分组模式时,需要设置 ‘GcmParamsSpec’ 参数
2.采用CCM分组模式时,需要设置 ‘CcmParamsSpec’ 参数
3.采用CBC,OFB,CFB,CTR模式时,可以使用 ‘IvParamsSpec’ 参数
4.如果选择了"NoPadding"填充模式,需要明文的字节长度如果不是128的整数倍,则会出现截断现象,这种情况算做正常。
5.不要并发加密
6.加密/解密行为之间需要有时间间隔
import cryptoFramework from '@ohos.security.cryptoFramework'; import util from '@ohos.util'; import Logger from '../../common/Logger'; import OriginData from './OriginData'; import emitter from "@ohos.events.emitter"; /** * 对称密钥 * 密钥算法:AES * 密钥规格格式:密钥算法名称 + 密钥长度 * 密钥长度:128,192,256 * 密钥规格列表:AES128, AES192, AES256 * * 对称加密 * 加密算法:AES * 加密规格格式:密钥算法名称 + 密钥长度 + 分组模式 + 填充模式 * 密钥长度:128,192,256 * 分组模式:ECB、CBC、OFB、CFB、CTR、GCM和CCM * 填充模式:NoPadding,PKCS5,PKCS7 * 加密规格样例:AES256|GCM|PKCS5 * * * */ class TestSymmetricAESEncryptDecrypt { private symBinKey: Uint8Array; private cipherText: Uint8Array; private authTag: Uint8Array; private algorithmWithLength: string = 'AES256' private blockCipherMode: string = 'ECB' //ECB、CBC、OFB、CFB、CTR、GCM和CCM private paddingMode: string = 'NoPadding' //NoPadding,PKCS5,PKCS7 //对称加密:AES256|GCM|PKCS5 testSymAESEncryptWith256GCMPKCS5(blockCipherMode: string, paddingMode: string, dataSource128:boolean) { this.authTag = null let originData: string = OriginData.CONTENT_128 if(dataSource128){ originData = OriginData.CONTENT_128 } else { originData = OriginData.CONTENT_125 } this.blockCipherMode = blockCipherMode this.paddingMode = paddingMode let symKeyGenerator = cryptoFramework.createSymKeyGenerator(this.algorithmWithLength); let promiseSymKey = symKeyGenerator.generateSymKey(); let globalCipher: cryptoFramework.Cipher promiseSymKey.then(key => { console.log('密钥已生成',key.format, key.algName, key.getEncoded().data.toString()) Logger.d(this.algorithmWithLength, this.blockCipherMode, this.paddingMode) this.symBinKey = key.getEncoded().data; return key; }).then( key => { globalCipher = cryptoFramework.createCipher(this.algorithmWithLength + '|' + this.blockCipherMode + '|' + this.paddingMode); let mode = cryptoFramework.CryptoMode.ENCRYPT_MODE; if (this.blockCipherMode == 'GCM') { return globalCipher.init(mode, key, this.genGcmParamsSpec()); } else if(this.blockCipherMode == 'CCM') { return globalCipher.init(mode, key, this.genCcmParamsSpec()); } else if(this.blockCipherMode == 'CBC' || this.blockCipherMode == 'CTR' || this.blockCipherMode == 'OFB'|| this.blockCipherMode == 'CFB'){ return globalCipher.init(mode, key, this.genIvParamsSpec()); } else { return globalCipher.init(mode, key, null); } }).then(() => { const encoder = new util.TextEncoder() let u8a_encoder = encoder.encodeInto(originData) Logger.d('开始加密') let plainText = { data: u8a_encoder }; let promiseUpdate = globalCipher.update(plainText); return promiseUpdate; }).then(updateOutput => { if(updateOutput != null && updateOutput.data != null){ this.cipherText = updateOutput.data } if (this.paddingMode != 'NoPadding') { Logger.d('加密分组之后的剩余数据') let promiseFinal = globalCipher.doFinal(null); return promiseFinal; } else { return null } }).then( authTag => { if(authTag != null && authTag.data != null){ console.log('authTag:', authTag.data.toString()) this.authTag = authTag.data } else { this.authTag = null console.log('authTag == null') } console.log('加密结束') return }).then(() => { this.testSymAESDecrypt() return }).catch( error => { console.error(`catch error, ${error.code}, ${error.message}`); this.notificationStatus('加密中-error', error.code + '-' + error.message) }) } //对称解密: AES256|GCM|PKCS5 testSymAESDecrypt() { // 二进制密钥数据 let keyEncode = [242, 202, 181, 197, 174, 191, 60, 94, 138, 7, 53, 123, 64, 30, 32, 236, 93, 165, 234, 21, 136, 142, 12, 161, 238, 9, 56, 211, 192, 134, 39, 236]; let keyMaterialBlob: cryptoFramework.DataBlob = { data: this.symBinKey }; //即将被解密的二进制数据, let willDecryptData = [209, 124, 163, 117, 73, 39, 230, 52, 162, 77, 46, 28, 39, 82, 32, 123, 177, 15, 218, 22, 206, 49, 167, 61] // GCM auth参数 let authOriginData = [125, 81, 34, 43, 37, 200, 200, 251, 207, 183, 121, 185, 59, 143, 212, 128]; let authBlob = { data: this.authTag }; let gcmParamsSpec = null if(this.blockCipherMode == 'GCM'){ console.log('配置'+this.blockCipherMode+'解密参数') gcmParamsSpec = this.genGcmParamsSpec() if(this.authTag != null){ gcmParamsSpec.authTag = authBlob } } else if(this.blockCipherMode == 'CCM'){ console.log('配置'+this.blockCipherMode+'解密参数') gcmParamsSpec = this.genCcmParamsSpec() if(this.authTag != null){ gcmParamsSpec.authTag = authBlob } } else if(this.blockCipherMode == 'CBC' || this.blockCipherMode == 'CTR' || this.blockCipherMode == 'OFB'|| this.blockCipherMode == 'CFB'){ console.log('配置'+this.blockCipherMode+'解密参数') gcmParamsSpec = this.genIvParamsSpec() } let globalCipher: cryptoFramework.Cipher let symKeyGenerator = cryptoFramework.createSymKeyGenerator(this.algorithmWithLength); let first: Uint8Array = null // 根据指定的二进制密钥数据,生成对称密钥对象 symKeyGenerator.convertKey(keyMaterialBlob) .then(key => { console.log('解密-初始化Cipher') globalCipher = cryptoFramework.createCipher(this.algorithmWithLength + '|' + this.blockCipherMode + '|' + this.paddingMode); let mode = cryptoFramework.CryptoMode.DECRYPT_MODE; if (this.blockCipherMode == 'GCM' || this.blockCipherMode == 'CCM' || this.blockCipherMode == 'CBC' || this.blockCipherMode == 'CTR' || this.blockCipherMode == 'OFB'|| this.blockCipherMode == 'CFB') { globalCipher.init(mode, key, gcmParamsSpec) } else { globalCipher.init(mode, key, null); } return }) .then(() => { let mergeResult: Uint8Array if ((this.authTag != null) && ((this.blockCipherMode == 'ECB') || (this.blockCipherMode == 'CBC'))) { mergeResult = new Uint8Array([...this.cipherText, ...this.authTag]) console.log('开始解密', '密文+authTag') } else { mergeResult = this.cipherText console.log('开始解密', '密文') } let promiseUpdate = globalCipher.update({ data: mergeResult }); return promiseUpdate; }) .then(updateOutput => { Logger.d('解密分组之后的剩余数据') first = updateOutput.data if(this.blockCipherMode == 'GCM'){ if(this.paddingMode == 'NoPadding'){ return } } let promiseFinal = globalCipher.doFinal(null); return promiseFinal; }) .then( finalOutput => { if (finalOutput == null) { // 使用finalOutput.data前,先判断结果是否为null let textDecoder = util.TextDecoder.create() let key = textDecoder.decodeWithStream(first); Logger.d('解密完成1: ', key); this.notificationStatus('解密完成', key) } else { let textDecoder = util.TextDecoder.create() let key = textDecoder.decodeWithStream(new Uint8Array([...first, ...finalOutput.data])); Logger.d('解密完成2: ', key); this.notificationStatus('解密完成', key) } console.log('解密结束') }) .catch(error => { console.error(`catch error, ${error.code}, ${error.message}`); this.notificationStatus('解密中-error', error.code + '-' + error.message) }) } //https://developer.huawei.com/consumer/cn/doc/harmonyos-references-V2/js-apis-cryptoframework-0000001477981409-V2#ZH-CN_TOPIC_0000001523488570__gcmparamsspec genGcmParamsSpec() { let arr = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]; // 12 bytes let dataIv = new Uint8Array(arr); let ivBlob = { data: dataIv }; arr = [0, 0, 0, 0, 0, 0, 0, 0]; // 8 bytes let dataAad = new Uint8Array(arr); let aadBlob = { 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 = { data: dataTag }; let gcmParamsSpec = { iv: ivBlob, aad: aadBlob, authTag: tagBlob, algName: "GcmParamsSpec" }; return gcmParamsSpec; } //https://developer.huawei.com/consumer/cn/doc/harmonyos-references-V2/js-apis-cryptoframework-0000001477981409-V2#ZH-CN_TOPIC_0000001523488570__ccmparamsspec genCcmParamsSpec() { let arr = [0, 0, 0, 0, 0, 0, 0]; // 7 bytes let dataIv = new Uint8Array(arr); let ivBlob = { data: dataIv }; arr = [0, 0, 0, 0, 0, 0, 0, 0]; // 8 bytes let dataAad = new Uint8Array(arr); let aadBlob = { data: dataAad }; arr = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]; // 12 bytes let dataTag = new Uint8Array(arr); let tagBlob = { data: dataTag }; let ccmParamsSpec = { iv: ivBlob, aad: aadBlob, authTag: tagBlob, algName: "CcmParamsSpec" }; return ccmParamsSpec; } //https://developer.huawei.com/consumer/cn/doc/harmonyos-references-V2/js-apis-cryptoframework-0000001477981409-V2#ZH-CN_TOPIC_0000001523488570__ivparamsspec genIvParamsSpec() { let arr = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]; // 16 bytes let dataIv = new Uint8Array(arr); let ivBlob = { data: dataIv }; let gcmParamsSpec = { iv: ivBlob, algName: "IvParamsSpec" }; return gcmParamsSpec; } notificationStatus(status:string, result: string){ // 定义一个eventId为1的事件,事件优先级为Low let event = { eventId: 202404063287, priority: emitter.EventPriority.HIGH }; let eventData = { data: { "content": result, 'status': status, } }; // 发送eventId为1的事件,事件内容为eventData emitter.emit(event, eventData); } } export default new TestSymmetricAESEncryptDecrypt();
3DES加密算法密文分组长度仅有一种:192; 支持3种分组模式:ECB,CBC,OFB; 支持3种填充模式:NoPadding, PKCS5, PKCS7
3DES加解密的流程与 “3.2 AES加解密”流程是一致的。
API使用概括起来如下
1.创建密钥生成器: createSymKeyGenerator
2.生成密钥对象:generateSymKey 或 convertKey
3.创建Cipher: createCipher
4.初始化Cipher: init
5.加密/解密:update
6.结束加密/解密:doFinal
import cryptoFramework from '@ohos.security.cryptoFramework'; import util from '@ohos.util'; import Logger from '../../common/Logger'; import OriginData from './OriginData'; import emitter from "@ohos.events.emitter"; /** * 对称密钥 * 密钥算法:3DES * 密钥规格格式:密钥算法名称 + 密钥长度 * 密钥长度:192 * 密钥规格列表:3DES * * 对称加密 * 加密算法:3DES * 加密规格格式:密钥算法名称 + 密钥长度 + 分组模式 + 填充模式 * 密钥长度:192 * 分组模式:ECB、CBC、OFB * 填充模式:NoPadding,PKCS5,PKCS7 * 加密规格样例:3DES192|ECB|NoPadding * * * */ class TestSymmetric3DESEncryptDecrypt { private symBinKey: Uint8Array = null; private cipherText: Uint8Array = null; private authTag: Uint8Array = null; private algorithmWithLength: string = '3DES192' private blockCipherMode: string = 'ECB' //ECB、CBC、OFB private paddingMode: string = 'NoPadding' //NoPadding,PKCS5,PKCS7 //对称加密:3DES192|ECB|PKCS5 testSym3DESEncryptWith192ECBPKCS5(blockCipherMode: string, paddingMode: string, dataSource128:boolean) { let originData: string = OriginData.CONTENT_128 if(dataSource128){ originData = OriginData.CONTENT_128 } else { originData = OriginData.CONTENT_125 } this.blockCipherMode = blockCipherMode this.paddingMode = paddingMode let symKeyGenerator = cryptoFramework.createSymKeyGenerator(this.algorithmWithLength); let promiseSymKey = symKeyGenerator.generateSymKey(); let globalCipher: cryptoFramework.Cipher promiseSymKey.then( key => { console.log(key.format, key.algName, key.getEncoded().data.toString()) Logger.d(this.algorithmWithLength, this.blockCipherMode, this.paddingMode) this.symBinKey = key.getEncoded().data; globalCipher = cryptoFramework.createCipher(this.algorithmWithLength + '|' + this.blockCipherMode + '|' + this.paddingMode); let mode = cryptoFramework.CryptoMode.ENCRYPT_MODE; let initResult = globalCipher.init(mode, key, null); return initResult; }).then(async () => { const encoder = new util.TextEncoder() let u8a_encoder = encoder.encodeInto(originData) Logger.d('原文:' + u8a_encoder.toString()) let plainText = { data: u8a_encoder }; let promiseUpdate = globalCipher.update(plainText); return promiseUpdate; }).then( updateOutput => { if(updateOutput.data != null){ Logger.d('编码后: ' + updateOutput.data.toString()) } this.cipherText = updateOutput.data if (this.paddingMode != 'NoPadding') { let promiseFinal = globalCipher.doFinal(null); return promiseFinal; } else { return null } }).then(authTag => { if ((authTag != null) && (authTag.data != null)) { console.log('authTag:', authTag.data.toString()) this.authTag = authTag.data } else { this.authTag = null } return }) .then(() => { this.testSym3DESDecrypt() return }).catch( error => { console.error(`catch error, ${error.code}, ${error.message}`); this.notificationStatus('加密中-error', error.code + '-' + error.message) }) } //对称解密: 3DES192|ECB|PKCS5 testSym3DESDecrypt() { // 二进制密钥数据 let keyMaterialBlob: cryptoFramework.DataBlob = { data: this.symBinKey }; let globalCipher: cryptoFramework.Cipher let symKeyGenerator = cryptoFramework.createSymKeyGenerator(this.algorithmWithLength); let first: Uint8Array = null // 根据指定的二进制密钥数据,生成对称密钥对象 symKeyGenerator.convertKey(keyMaterialBlob) .then(key => { globalCipher = cryptoFramework.createCipher(this.algorithmWithLength + '|' + this.blockCipherMode + '|' + this.paddingMode); let mode = cryptoFramework.CryptoMode.DECRYPT_MODE; globalCipher.init(mode, key, null) return }).then(() => { let mergeResult: Uint8Array if ((this.authTag != undefined) && (this.authTag != null)) { mergeResult = new Uint8Array([...this.cipherText, ...this.authTag]) } else { mergeResult = this.cipherText } let promiseUpdate = globalCipher.update({ data: mergeResult }); return promiseUpdate; }) .then( updateOutput => { first = updateOutput.data let promiseFinal = globalCipher.doFinal(null); return promiseFinal; }) .then( finalOutput => { if (finalOutput == null) { let textDecoder = util.TextDecoder.create() let key = textDecoder.decodeWithStream(first); Logger.d('解密完成1: ', key); this.notificationStatus('解密完成', key) } else { let textDecoder = util.TextDecoder.create() let key = textDecoder.decodeWithStream(new Uint8Array([...first, ...finalOutput.data])); Logger.d('解密完成2: ', key); this.notificationStatus('解密完成', key) } console.log('解密结束') }) .catch(error => { console.error(`catch error, ${error.code}, ${error.message}`); this.notificationStatus('解密中-error', error.code + '-' + error.message) }) } notificationStatus(status:string, result: string){ // 定义一个eventId为1的事件,事件优先级为Low let event = { eventId: 202404063287, priority: emitter.EventPriority.HIGH }; let eventData = { data: { "content": result, 'status': status, } }; // 发送eventId为1的事件,事件内容为eventData emitter.emit(event, eventData); } } export default new TestSymmetric3DESEncryptDecrypt();
以256长度分组,“128字节”作为数据源
export default class OriginData {
//125字节 * 8
public static readonly CONTENT_125: string =
"hello...iot...modbus,"
+ "加解密算法库框架是,全随机数等相关功能测开发者测可以通过调用加解密1024。"
//128字节 * 8
public static readonly CONTENT_128: string =
"hello...iot...modbus,"
+ "加解密算法库框架是,安全随机数等相关功能测开发者测可以通过调用加解密1024。"
}
T:成功,F:失败
本篇没有演示分段加密和分段解密,原始:代码没有实验成功
既然已经有了密文分组模式和长度,理论上在执行update时,其内部已经完成了分组加密,感觉也没有必要单独分段加密。
有很多小伙伴不知道该从哪里开始学习鸿蒙开发技术?也不知道鸿蒙开发的知识点重点掌握的又有哪些?自学时频繁踩坑,导致浪费大量时间。结果还是一知半解。所以有一份实用的鸿蒙(HarmonyOS NEXT)全栈开发资料用来跟着学习是非常有必要的。
这份鸿蒙(HarmonyOS NEXT)资料包含了鸿蒙开发必掌握的核心知识要点,内容包含了
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。