动手学鸿蒙App开发 HarmonyOS(07)AES加解密

前言

客户端开发中存在一些需要在传输过程中进行保护的重要信息,比如用户通讯录信息,用户日志等信息。 这种情况下就需要采用适当的加密与解密方式。

常规的加密方式有AES,RSA 等。 本篇中介绍对称加密方式AES,这种对称式加密方式中加密与解密使用相同密钥。

鸿蒙加解密算法框架详细介绍了完整的使用方法供参考

鸿蒙官方加解密框架开发指导

效果展示

原理

  • AES加密

    AES的全称是Advanced Encryption Standard,是最常见的对称加密。AES为分组密码,分组密码也就是把明文分成一组一组的,每组长度相等,每次加密一组数据,直到加密完整个明文。在AES标准规范中,分组长度只能是128位,也就是说,每个分组为16个字节(每个字节8位)。密钥的长度可以使用128位、192位或256位。

  • 对称AES加解密

    算法库目前提供了AES加解密常用的7种加密模式:ECB、CBC、OFB、CFB、CTR、GCM和CCM。AES为分组加密算法,分组长度大小为128位。实际应用中明文最后一组可能不足128位,不足数据可以使用各种padding模式做数据填充。下文中描述了各个padding的区别:

    1. NoPadding:不带填充;
    2. PKCS5:填充字符由一个字节序列组成,每个字节填充该填充字节序列的长度,规定是8字节填充;
    3. PKCS7:填充字符和PKCS5填充方法一致,但是可以在1-255字节之间任意填充;
  • 相关概念

    1. 明文P(plainText):未经加密的数据
    2. 密钥K(key):用来加密明文的密码。在对称加密算法中,加密与解密的密钥是相同的,由双方协商产生,绝不可以泄漏
    3. 密文C(cipherText): 经过加密的数据
    4. 加密函数E(encrypt):C = E(K, P),即将明文和密钥作为参数,传入加密函数密文就可以获得密文
    5. 解密函数D(decrypt):P = D(K, C),即将密文和密钥作为参数,传入解密函数中,就可以获得明文
    6. 初始向量(IV,Initialization Vector):它的作用和MD5的"加盐"有些类似,目的是防止同样的明文块,始终加密成同样的密文块:
  • 实现

    • 本例中实现了AES对用户输入的明文内容进行AES加密并显示
    • 对加密的内容进行解密并显示
    • 密钥和初始向量iv写死了,实际项目中可以根据自己的需要来实现密钥和iv的生成。比如用md5来生成
  • 注意加密过程

由于aes加密后的字节数组,直接转String返回参数有可能存在乱码,所以对返回值做一下处理,将加密的结果转换为base64的字符串。同理,解密时候,需要把字符串先base64处理,再把解密后的内容进行aes解密操作。

rust 复制代码
加密实现
加密(从前往后):明文 -> 加密 -> base64处理 -> 密文

解密实现
解密(从后往前):明文 <- 解密 <- base64处理 <- 密文

代码

加密和解密方法

ts 复制代码
import util from '@ohos.util';
import cryptoFramework from '@ohos.security.cryptoFramework';
import Logger from './Logger';


const TAG: string = '[CipherModel]'
const AES128: string = 'AES128';
const AES128_CBC_PKCS5: string = 'AES128|CBC|PKCS5';
const AES128_PKCS7: string = 'AES128|PKCS7';

export class CipherModel {
  stringToUint8Array(str) {
    var arr = [];
    for (var i = 0, j = str.length; i < j; ++i) {
      arr.push(str.charCodeAt(i));
    }
    var tmpArray = new Uint8Array(arr);
    return tmpArray;
  }

  uint8ArrayToString(array: Uint8Array) {
    let arrayString = '';
    for (let i = 0; i < array.length; i++) {
      arrayString += String.fromCharCode(array[i]);
    }
    return arrayString;
  }

  aesEncrypt(message: string, key: string, iv: string, callback) {
    let that = new util.Base64Helper();

    let paramsSpec: cryptoFramework.IvParamsSpec = { iv: { data: this.stringToUint8Array(iv) }, algName: "IvParamsSpec" }
    let aesGenerator = cryptoFramework.createSymKeyGenerator(AES128);
    let cipher = cryptoFramework.createCipher(AES128_CBC_PKCS5);
    let pubKey = that.decodeSync(key);
    let pubKeyBlob: cryptoFramework.DataBlob = { data: pubKey };
    aesGenerator.convertKey(pubKeyBlob, (err, symKey) => {
      if (err) {
        console.error("convertKey: error.");
        return;
      }
      cipher.init(cryptoFramework.CryptoMode.ENCRYPT_MODE, symKey, paramsSpec, (err, data) => {
        let input: cryptoFramework.DataBlob = { data: this.stringToUint8Array(message) };
        cipher.doFinal(input, (err, data) => {
          Logger.info(TAG, "EncryptOutPut is " + data.data);
          let result = that.encodeToStringSync(data.data)
          Logger.info(TAG, "result is " + result);
          callback(result)
        })
      })
    })
  }

  aesDecrypt(message: string, key: string, iv: string, callback) {
    let paramsSpec: cryptoFramework.IvParamsSpec = { iv: { data: this.stringToUint8Array(iv) }, algName: "IvParamsSpec" }

    let aesGenerator = cryptoFramework.createSymKeyGenerator(AES128);
    let cipher = cryptoFramework.createCipher(AES128_CBC_PKCS5);
    let that = new util.Base64Helper();
    let pubKey = that.decodeSync(key);
    let pubKeyBlob: cryptoFramework.DataBlob = { data: pubKey };
    aesGenerator.convertKey(pubKeyBlob, (err, symKey) => {
      if (err) {
        console.error("convertKey: error.");
        return;
      }
      cipher.init(cryptoFramework.CryptoMode.DECRYPT_MODE, symKey, paramsSpec, (err, data) => {
        let newMessage = that.decodeSync(message);
        let input: cryptoFramework.DataBlob = { data: newMessage };
        cipher.doFinal(input, (err, data) => {
          Logger.info(TAG, "DecryptOutPut is " + data.data);
          let result = this.uint8ArrayToString(data.data)
          Logger.info(TAG, "result is " + result);
          callback(result)
        })
      })
    })
  }
}

点击加密按钮

ts 复制代码
Row() {
  Button('aes加密').align(Alignment.Start)
}.width('100%').margin({ left: 12, top: 12 })
.onClick( () => {
  this.cipherModel.aesEncrypt(
    this.originText,
    this.AES_ENCRYPT_KEY,
    this.AES_IV_STRING,
    (result: string) => {
      Logger.info(this.TAG, `this result = ${JSON.stringify(result)}`);
      this.cipherText = result;
    })
}
)

// 点击解密按钮

ts 复制代码
Row() {
  Button('aes解密').align(Alignment.Start)
}.width('100%').margin({ left: 12, top: 12 })
.onClick(() => {
  if(this.cipherText.toString().length === 0) {
    return;
  }
  this.cipherModel.aesDecrypt(this.cipherText,
    this.AES_ENCRYPT_KEY,
    this.AES_IV_STRING,
    (result: string) => {
    Logger.info(this.TAG, `this result = ${JSON.stringify(result)}`);
    this.decodedText = result;
  })
})

总结

本文中仅涉及了AES加解密的简单使用,并未根据项目实际需要来调整加密格式参数。如果项目中需要加密的信息数量较多或者需要较强的加密强度。请到官网查看最新版的相关文档,选择最合适的加密方法。

demo代码已上传github

由于我还没找到鸿蒙工作,所以还没有拿到鸿蒙最新的sdk。此处使用的是官网的公开版本sdk 9。 有些朋友项目中已经拿到华为内部鸿蒙版本,api可能略有不同,请根据最新sdk自行调整变更的api。

给自己打个广告,本人性别男,坐标上海,之前在百度做安卓8年。准备看鸿蒙机会。有适合岗位的朋友私信我。谢谢,下期再见~

相关推荐
影子落人间3 分钟前
已解决npm ERR! request to https://registry.npm.taobao.org/@vant%2farea-data failed
前端·npm·node.js
世俗ˊ27 分钟前
CSS入门笔记
前端·css·笔记
子非鱼92128 分钟前
【前端】ES6:Set与Map
前端·javascript·es6
6230_32 分钟前
git使用“保姆级”教程1——简介及配置项设置
前端·git·学习·html·web3·学习方法·改行学it
想退休的搬砖人41 分钟前
vue选项式写法项目案例(购物车)
前端·javascript·vue.js
加勒比海涛1 小时前
HTML 揭秘:HTML 编码快速入门
前端·html
啥子花道1 小时前
Vue3.4 中 v-model 双向数据绑定新玩法详解
前端·javascript·vue.js
麒麟而非淇淋1 小时前
AJAX 入门 day3
前端·javascript·ajax
茶茶只知道学习1 小时前
通过鼠标移动来调整两个盒子的宽度(响应式)
前端·javascript·css
清汤饺子1 小时前
实践指南之网页转PDF
前端·javascript·react.js