1. 介绍
HarmonyOS(鸿蒙)SDK API9支持两种对称加密算法:a. AES(Advanced Encryption Standard) b.3DES(也称为 3DESede 或 TripleDES).
官方也给出了一些样例使用代码,可供开发者参考。
本篇从实践出发,完整的通过代码方式来深入了解HarmonyOS中的对称加密用法。
2. 基础概念
字符串样例:AES256|EBC|PKCS5
1.密钥算法 : AES
2.密文组长度 : 256
3.分组模式 : EBC
4.填充模式 : PCKCS5
关于“AES算法”,“分组模式”,“填充模式”的具体概念,可自行搜索。
3. 实践
加密使用的测试数据源有两个,具体见下图
1.“125字节” (即,占用了125个字节的字符串)
2.“128字节” (即,占用了128个字节的字符串)
3.1 MMI入口
3.2 AES加解密
3DES加密算法密文分组长度有三种:128,192,256;支持7种分组模式:ECB,CBC,OFB,CFB,CTR,GCM,CCM;支持3种填充模式:NoPadding, PKCS5, PKCS7
本篇仅以256密文组长度做为实践验证,默认选择“128字节”数据集
片段代码以 “AES256|ECB|PKCS5” 为例
3.2.1 加密
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
}
})
3.2.2 解密
1.生成密钥对象
我们这里使用加密过程中产生的密钥数据。注意此密钥数据不能直接用来构建Key对象(即 密钥对象),正确的方法是通过API来生成密钥对象
//准备密钥数据,this.symBinKey 是上述过程生成的密钥
let keyMaterialBlob: cryptoFramework.DataBlob = { data: this.symBinKey };
//创建密钥生成器,参数为(密钥算法+密文组长度,如:AES256)
let symKeyGenerator = cryptoFramework.createSymKeyGenerator('AES256');
//密钥生成器使用密钥数据,开始生成密钥对象
symKeyGenerator.convertKey(keyMaterialBlob).then( key => {
//key 为生成的密钥对象
})
- 初始化Cipher
//创建Cipher
globalCipher = cryptoFramework.createCipher('AES256|EBC|PKCS5');
//初始化Cipher,参数key由第一步生成
let mode = cryptoFramework.CryptoMode.DECRYPT_MODE;
globalCipher.init(mode, key, null)
- 解密
//mergeResult 代表密文,本篇文章中,此值来源于上述加密结果
let promiseUpdate = globalCipher.update({ data: mergeResult });
- 结束解密
//结束解密
let promiseFinal = globalCipher.doFinal(null);
//获取剩余结果
promiseFinal( finalResult => {
if(finalResult != null){
//有剩余解密结果
} else {
//无剩余解密结果
}
})
3.2.3 注意点
1.采用GCM分组模式时,需要设置 ‘GcmParamsSpec’ 参数
2.采用CCM分组模式时,需要设置 ‘CcmParamsSpec’ 参数
3.采用CBC,OFB,CFB,CTR模式时,可以使用 ‘IvParamsSpec’ 参数
4.如果选择了"NoPadding"填充模式,需要明文的字节长度如果不是128的整数倍,则会出现截断现象,这种情况算做正常。
5.不要并发加密
6.加密/解密行为之间需要有时间间隔
3.2.4 源码
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();
3.3 3DES加解密
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
3.3.1 源码
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:失败
AES256
3DES
写在最后
有很多小伙伴不知道学习哪些鸿蒙开发技术?不知道需要重点掌握哪些鸿蒙应用开发知识点?而且学习时频繁踩坑,最终浪费大量时间。所以有一份实用的鸿蒙(HarmonyOS NEXT)资料用来跟着学习是非常有必要的。
这份鸿蒙(HarmonyOS NEXT)资料包含了鸿蒙开发必掌握的核心知识要点,内容包含了(ArkTS、ArkUI开发组件、Stage模型、多端部署、分布式应用开发、音频、视频、WebGL、OpenHarmony多媒体技术、Napi组件、OpenHarmony内核、openHarmony南向开发、鸿蒙项目实战等等)鸿蒙(HarmonyOS NEXT)技术知识点。
希望这一份鸿蒙学习资料能够给大家带来帮助,有需要的小伙伴自行领取,限时开源,先到先得~无套路领取!!
获取这份完整版高清学习路线,请点击→纯血版全套鸿蒙HarmonyOS学习资料
鸿蒙(HarmonyOS NEXT)最新学习路线
有了路线图,怎么能没有学习资料呢,小编也准备了一份联合鸿蒙官方发布笔记整理收纳的一套系统性的鸿蒙(OpenHarmony )学习手册(共计1236页)与鸿蒙(OpenHarmony )开发入门教学视频,内容包含:ArkTS、ArkUI、Web开发、应用模型、资源分类…等知识点。
获取以上完整版高清学习路线,请点击→纯血版全套鸿蒙HarmonyOS学习资料
《鸿蒙 (OpenHarmony)开发入门教学视频》
《鸿蒙生态应用开发V3.0白皮书》
《鸿蒙 (OpenHarmony)开发基础到实战手册》
OpenHarmony北向、南向开发环境搭建
《鸿蒙开发基础》
●ArkTS语言
●安装DevEco Studio
●运用你的第一个ArkTS应用
●ArkUI声明式UI开发
.……
《鸿蒙开发进阶》
●Stage模型入门
●网络管理
●数据管理
●电话服务
●分布式应用开发
●通知与窗口管理
●多媒体技术
●安全技能
●任务管理
●WebGL
●国际化开发
●应用测试
●DFX面向未来设计
●鸿蒙系统移植和裁剪定制
……
《鸿蒙进阶实战》
●ArkTS实践
●UIAbility应用
●网络案例
……