赞
踩
目录
对称/分组密码一般分为流加密(如OFB、CFB等)和块加密(如ECB、CBC等)。对于流加密,需要将分组密码转化为流模式工作。对于块加密(或称分组加密),如果要加密超过块大小的数据,就需要涉及填充和链加密模式。
ECB(Electronic Code Book电子密码本)模式
ECB模式是最早采用和最简单的模式,它将加密的数据分成若干组,每组的大小跟加密密钥长度相同,然后每组都用相同的密钥进行加密
官方文档中使用方法:
- // 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加密
- 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解密
- 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加密原文
日志中可以看出,中文加密的密文也能被完整的解密出来,至此这篇文章页到了尾声,希望我的文章能够帮到你。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。