【分享】HarmonyOS 鸿蒙Next对称加密
【分享】HarmonyOS 鸿蒙Next对称加密
1. 介绍
HarmonyOS(鸿蒙)SDK API9支持两种对称加密算法:a. AES(Advanced Encryption Standard) b.3DES(也称为 3DESede 或 TripleDES).
官方也给出了一些样例使用代码,可供开发者参考。
本篇从实践出发,完整的通过代码方式来深入了解HarmonyOS中的对称加密用法。
加密使用的测试数据源有两个,具体见下图
- “125字节” (即,占用了125个字节的字符串)
- “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 加密
- 生成对动态密钥
//导入加密框架
import cryptoFramework from '@ohos.security.cryptoFramework';
......
//创建密钥生成器,参数为(密钥算法+密文组长度,如:AES256)
let symKeyGenerator = cryptoFramework.createSymKeyGenerator('AES256');
//生成对称密钥
let promiseSymKey = symKeyGenerator.generateSymKey();
//获取密钥
promiseSymKey(key => {
//动态密钥
this.symBinKey = key.getEncoded().data;
})
- 初始化Cipher
//创建Cipher
globalCipher = cryptoFramework.createCipher('AES256|ECB|PKCS5');
//初始化Cipher
let mode = cryptoFramework.CryptoMode.ENCRYPT_MODE;
globalCipher.init(mode, key, null)
- 加密
//文字转换为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
})
- 结束加密
//结束加密
let promiseFinal = globalCipher.doFinal(null);
//获取剩余结果
promiseFinal(finalResult => {
if(finalResult != null){
//剩余加密结果
let authTag = finalResult.data
}
})
3.2.2 解密
- 生成密钥对象
//准备密钥数据,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 注意点
- 采用GCM分组模式时,需要设置 ‘GcmParamsSpec’ 参数
- 采用CCM分组模式时,需要设置 ‘CcmParamsSpec’ 参数
- 采用CBC,OFB,CFB,CTR模式时,可以使用 ‘IvParamsSpec’ 参数
- 如果选择了"NoPadding"填充模式,需要明文的字节长度如果不是128的整数倍,则会出现截断现象,这种情况算做正常。
- 不要并发加密
- 加密/解密行为之间需要有时间间隔
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";
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
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)
})
}
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)
})
}
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;
}
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, 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;
}
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){
let event = {
eventId: 202404063287,
priority: emitter.EventPriority.HIGH
};
let eventData = {
data: {
"content": result,
'status': status,
}
};
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使用概括起来如下
- 创建密钥生成器: createSymKeyGenerator
- 生成密钥对象:generateSymKey 或 convertKey
- 创建Cipher: createCipher
- 初始化Cipher: init
- 加密/解密:update
- 结束加密/解密: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";
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
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)
})
}
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){
let event = {
eventId: 202404063287,
priority: emitter.EventPriority.HIGH
};
let eventData = {
data: {
"content": result,
'status': status,
}
};
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。"
}
组合实验结果
组合 | 结果 |
---|---|
ECB-NoPadding | T |
ECB-PKCS5 | T |
ECB-PKCS7 | T |
CBC-NoPadding | T |
CBC-PKCS5 | T |
CBC-PKCS7 | T |
OFB-NoPadding | T |
OFC-PKCS5 | T |
OFB-PKCS7 | T |
CFB-NoPadding | T |
CFB-PKCS5 | T |
CFB-PKCS7 | T |
CTR-NoPadding | T |
CTR-PKCS5 | T |
CTR-PKCS7 | T |
GCM-NoPadding | T |
GCM-PKCS5 | T |
GCM-PKCS7 | T |
CCM-NoPadding | F |
CCM-PKCS5 | T |
CCM-PKCS7 | T |
组合 | 结果 |
---|---|
ECB-NoPadding | T |
ECB-PKCS5 | T |
ECB-PKCS7 | T |
CBC-NoPadding | T |
CBC-PKCS5 | T |
CBC-PKCS7 | T |
OFB-NoPadding | T |
OFC-PKCS5 | T |
OFB-PKCS7 | T |
结束
本篇没有演示分段加密和分段解密,原始:代码没有实验成功
既然已经有了密文分组模式和长度,理论上在执行update时,其内部已经完成了分组加密,感觉也没有必要单独分段加密。
祝好运!
更多关于【分享】HarmonyOS 鸿蒙Next对称加密的实战教程也可以访问 https://www.itying.com/category-93-b0.html
更多关于【分享】HarmonyOS 鸿蒙Next对称加密的实战系列教程也可以访问 https://www.itying.com/category-93-b0.html
鸿蒙Next中的对称加密是一种高效的加密方式,使用相同的密钥进行加密和解密。常见的对称加密算法包括AES、DES和3DES。在鸿蒙Next中,开发者可以通过javax.crypto
包实现对称加密,确保数据在传输和存储中的安全性。对称加密适用于大量数据的加密场景,但需妥善管理密钥,防止泄露。