什么是AES对称加密
对称/分组密码一般分为流加密(如OFB、CFB等)和块加密(如ECB、CBC等)。对于流加密,需要将分组密码转化为流模式工作。对于块加密(或称分组加密),如果要加密超过块大小的数据,就需要涉及填充和链加密模式。
ECB(Electronic Code Book电子密码本)模式
ECB模式是最早采用和最简单的模式,它将加密的数据分成若干组,每组的大小跟加密密钥长度相同,然后每组都用相同的密钥进行加密
官方文档
加解密算法库框架开发指导
鸿蒙开发中如何使用AES加密
官方文档中使用方法:
// AES GCM模式示例,自动生成密钥(promise写法)
function testAesGcm() {
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve('testAesGcm');
}, 10)
}).then(() => {
// 生成对称密钥生成器
let symAlgName = 'AES128';
let symKeyGenerator = cryptoFramework.createSymKeyGenerator(symAlgName);
if (symKeyGenerator == null) {
console.error('createSymKeyGenerator failed');
return;
}
console.info(`symKeyGenerator algName: ${symKeyGenerator.algName}`);
// 通过密钥生成器随机生成128位长度的对称密钥
let promiseSymKey = symKeyGenerator.generateSymKey();
// 构造参数
globalGcmParams = genGcmParamsSpec();
// 生成加解密生成器
let cipherAlgName = 'AES128|GCM|PKCS7';
try {
globalCipher = cryptoFramework.createCipher(cipherAlgName);
console.info(`cipher algName: ${globalCipher.algName}`);
} catch (error) {
console.error(`createCipher failed, ${error.code}, ${error.message}`);
return;
}
return promiseSymKey;
}).then(key => {
let encodedKey = key.getEncoded();
console.info('key hex:' + uint8ArrayToShowStr(encodedKey.data));
globalKey = key;
return key;
}).then(key => {
// 初始化加解密操作环境:开始加密
let mode = cryptoFramework.CryptoMode.ENCRYPT_MODE;
let promiseInit = globalCipher.init(mode, key, globalGcmParams); // init
return promiseInit;
}).then(() => {
let plainText = {data : stringToUint8Array('this is test!')};
let promiseUpdate = globalCipher.update(plainText); // update
return promiseUpdate;
}).then(updateOutput => {
globalCipherText = updateOutput;
let promiseFinal = globalCipher.doFinal(null); // doFinal
return promiseFinal;
}).then(authTag => {
// GCM模式需要从doFinal的输出中取出加密后的认证信息并填入globalGcmParams,在解密时传入init()
globalGcmParams.authTag = authTag;
return;
}).then(() => {
// 初始化加解密操作环境:开始解密
let mode = cryptoFramework.CryptoMode.DECRYPT_MODE;
let promiseInit = globalCipher.init(mode, globalKey, globalGcmParams); // init
return promiseInit;
}).then(() => {
let promiseUpdate = globalCipher.update(globalCipherText); // update
return promiseUpdate;
}).then(updateOutput => {
console.info('decrypt plainText: ' + uint8ArrayToString(updateOutput.data));
let promiseFinal = globalCipher.doFinal(null); // doFinal
return promiseFinal;
}).then(finalOutput => {
if (finalOutput == null) { // 使用finalOutput.data前,先判断结果是否为null
console.info('GCM finalOutput is null');
}
}).catch(error => {
console.error(`catch error, ${error.code}, ${error.message}`);
})
}
AES加密:
//AES加密
function aesECBEncrypt(plaintext) {
let key = '12345678901234==';
let ivKey = '12345678901234==';
let cipherAlgName = 'AES128|ECB|PKCS7';
let symKeyGenerator = cryptoFramework.createSymKeyGenerator('AES128')
let ivParam: cryptoFramework.IvParamsSpec = {
algName: 'IvParamsSpec',
iv: {
data: stringToUint8Array(ivKey, 16)
}
}
var cipher;
// 生成密钥
return symKeyGenerator.convertKey({
data: stringToUint8Array(key)
}).then(symKey => {
// 创建加密器
try {
cipher = cryptoFramework.createCipher(cipherAlgName);
console.info(`xx cipher algName: ${cipher.algName}`);
} catch (error) {
console.error(`xx createCipher failed, ${error.code}, ${error.message}`);
return null
}
// 初始化加密器
return cipher.init(cryptoFramework.CryptoMode.ENCRYPT_MODE, symKey, ivParam)
.then(() => {
// 开始加密
return cipher.doFinal({
data: stringToUint8Array(plaintext)
})
})
.then(output => {
let base64 = new util.Base64Helper();
let result = base64.encodeToStringSync(output.data);
return new Promise((resolve) => {
resolve(result)
})
}).catch(err => {
return new Promise((_, reject) => {
reject(err)
})
})
}).catch(err => {
return new Promise((_, reject) => {
reject(err)
})
})
}
AES解密:
//AES解密
function aesECBDecrypt(encrypttext) {
let key = '12345678901234==';
let ivKey = '12345678901234==';
let cipherAlgName = 'AES128|ECB|PKCS7';
let symKeyGenerator = cryptoFramework.createSymKeyGenerator('AES128')
let ivParam: cryptoFramework.IvParamsSpec = {
algName: 'IvParamsSpec',
iv: {
data: stringToUint8Array(ivKey, 16)
}
}
var cipher;
return symKeyGenerator.convertKey({
data: stringToUint8Array(key)
}).then(symKey => {
try {
cipher = cryptoFramework.createCipher(cipherAlgName);
console.info(`xx cipher algName: ${cipher.algName}`);
} catch (error) {
console.error(`xx createCipher failed, ${error.code}, ${error.message}`);
return null
}
return cipher.init(cryptoFramework.CryptoMode.DECRYPT_MODE, symKey, ivParam)
.then(() => {
let base64 = new util.Base64Helper();
let result = base64.decodeSync(encrypttext);
return cipher.doFinal({
data: result
})
})
.then(output => {
let result = uint8ArrayToString(output.data)
return new Promise((resolve) => {
resolve(result)
})
}).catch(err => {
return new Promise((_, reject) => {
reject(err)
})
})
}).catch(err => {
return new Promise((_, reject) => {
reject(err)
})
})
}
使用加密观察结果
调用:
aboutToAppear() {
let xxx;
let globalPlainText = "This is a long plainTest! This is a long plainTest! This is a long plainTest!";
console.log('Aes加密===原文is :' + globalPlainText)
aesECBEncrypt(globalPlainText).then(res=>{
console.log('Aes加密===加密后的密文is :' + res)
xxx = res;
}).catch(err => {
console.log('Aes加密===catch :' + err)
})
setTimeout(function() {
console.log('Aes加密=================================== ' )
aesECBDecrypt(xxx).then(res=>{
console.log('Aes加密===解密后的原文is: ' + res)
}).catch(err => {
console.log('Aes加密===解密:catch: ' + err)
})
}, 3000);
}
结果:
I 0FEFE/JsApp: Aes加密===原文is :This is a long plainTest! This is a long plainTest! This is a long plainTest!
I 0FEFE/JsApp: Aes加密===加密后的密文is :zbusam9rOBPmJ072ENhEO9DCPD/DeSJOYvFGHOmYazQ7pFjUXUMW7aUfhyYtqVLQuflFtPW5Y8MG06n1C0stQJybjSA68it6TNVWP6GBkk8=
I 0FEFE/JsApp: Aes加密===================================
I 0FEFE/JsApp: Aes加密===解密后的原文is: This is a long plainTest! This is a long plainTest! This is a long plainTest!
可以看到打印出来是没有任何问题的,但是当我在加密原文中加入中文后出现了意想不到的结果,将 globalPlainText改为:This is a long plainTest! AES加密原文 ;观察打印结果
I 0FEFE/JsApp: Aes加密===原文is :This is a long plainTest! AES加密原文
I 0FEFE/JsApp: Aes加密===加密后的密文is :zbusam9rOBPmJ072ENhEO+8AvpCHJdNUxFsgzFxHMM55off1zCchwYL/C/3DHvPm
I 0FEFE/JsApp: Aes加密===================================
I 0FEFE/JsApp: Aes加密===解密后的原文is: This is a long plainTest! AES Æ
what ? 英文没问题,为啥换成中文就不行了?
解密过程出现中文乱码如何处理
百思不得其解,官方文档明明就是这么写的,难道是官方文档有问题?
uint8ArrayToString、 stringToUint8Array在文档中出现过多次,也有实例,文档中的使用:
// 字节流以16进制输出
function uint8ArrayToShowStr(uint8Array) {
return Array.prototype.map
.call(uint8Array, (x) => ('00' + x.toString(16)).slice(-2))
.join('');
}
// 字节流转成可理解的字符串
function uint8ArrayToString(array) {
let arrayString = '';
for (let i = 0; i < array.length; i++) {
arrayString += String.fromCharCode(array[i]);
}
return arrayString;
}
思考:会不会在字符串转字节流的时候出现了问题
String转16进制的原理是将字符串中每个字符转换为16进制的形式,
UTF-8编码:一个英文字符等于一个字节,一个中文(含繁体)等于三个字节。中文标点占三个字节,英文标点占一个字节。
使用Java进行16进制转换的时候我们都知道会将内容先统一编码,再使用StringBuilder转16进制
bytel] bytes = chinese.getBytes(Charset.forName("UTF-8"));
StringBuilder hex= new StringBuilder();
但是刚刚的AES加密uint8ArrayToShowStr函数中没有统一指定编码格式,那如果一个中文占用了3个字符,岂不是无法解码,果然问题出在这,在翻阅鸿蒙util文档中我发现了TextEncoder:
文档中对TextEncoder是这么介绍的:
该模块主要提供常用的工具函数,实现字符串编解码(TextEncoder,TextDecoder)这不就是我们想要的嘛
完美解决中文加密解密乱码问题
修改字符串转成字节流 互相转换的函数如下:
// 字符串转成字节流
function stringToUint8Array(str, len = null) {
let textEncoder = new util.TextEncoder();
//获取点流并发出 UTF-8 字节流 TextEncoder 的所有实例仅支持 UTF-8 编码
return textEncoder.encodeInto(str);
}
// 字节流转成可理解的字符串
function uint8ArrayToString(array) {
let textDecoder = util.TextDecoder.create("utf-8", { ignoreBOM: true })
return textDecoder.decodeWithStream(new Uint8Array(array), { stream: false });
}
继续执行代码观察日志
I 0FEFE/JsApp: Aes加密===原文is :This is a long plainTest! AES加密原文
I 0FEFE/JsApp: Aes加密===加密后的密文is :zbusam9rOBPmJ072ENhEO5JErEY5X7HP2L/WPwARmlMV0PoGgXX+6ZhohYdJLhKt
I 0FEFE/JsApp: Aes加密===================================
I 0FEFE/JsApp: Aes加密===解密后的原文is: This is a long plainTest!
AES加密原文
日志中可以看出,中文加密的密文也能被完整的解密出来,至此这篇文章页到了尾声,希望我的文章能够帮到你。
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。