HarmonyOS 加密解密实现(基于@kit.CryptoArchitectureKit实现AES256|CBC|PKCS7)

403 阅读4分钟

总述: 对github上的CryptLib(github.com/skavinvarna… )的加密解密逻辑进行ArkTS化改造,以及逻辑验证、使用示例。跨平台支持增加HarmoyOS平台!

背景:公司现有项目中Android、iOS、小程序、服务端在处理用户登录业务时,对用户密码进行了加密处理,使用了github上的CryptLib(github.com/skavinvarna… ),对Android、iOS、Javascript/NodeJS/Web各个端都有支持和示例代码,使用简单,跨平台支持。现在HarmoyOS平台来了,问题也来了!Harmony app怎么处理?

心路历程:首先想到的是使用现有工具的NodeJS代码使用示例进行操作,那就要在鸿蒙项目工程中引入CryptLib的Js依赖(npm install @skavinvarnan/cryptlib --save),但是鸿蒙工程仅支持ohpm引入,也搜索了如何在鸿蒙工程如何使用npm引入三方库,各种操作吧,还是无法使用(网上很多,读者可以自行查找,个人意见,既然鸿蒙只允许ohpm,那就不要走npm了)。鸿蒙官方ohpm也有加密解密的三方库,可能是我们现有的加密解密业务逻辑,或者我使用的不对,总之还是放弃了。无奈之下,只能基于CryptLib中的Android代码改造吧。

从ArkTS API9开始的加密解密算法框架都使用@kit.CryptoArchitectureKit,本文代码中也是基于该框架实现。同时要考虑到现有项目在使用CryptLib加密解密时使用的是AES256|CBC。

仿照CryptLib中的样式,贴出使用示例和逻辑代码

使用示例

import { OHCryptLib } from './OHCryptLib'

@Entry
@Component
struct Index {
  @State message: string = 'Hello World';

  build() {
    RelativeContainer() {
      Text(this.message)
        .id('HelloWorld')
        .fontSize(50)
        .fontWeight(FontWeight.Bold)
        .alignRules({
          center: { anchor: '__container__', align: VerticalAlign.Center },
          middle: { anchor: '__container__', align: HorizontalAlign.Center }
        })
        .onClick(async () => {
          testOHCryptLib()
        })
    }
    .height('100%')
    .width('100%')
  }
}

async function testOHCryptLib() {
  let plainText = "this is my plain text"
  let key = "1234567890" //your key
  let encrypt = await OHCryptLib.encryptPlainTextWithRandomIV(plainText, key)
  console.info("test OHCryptLib encrypt: " + encrypt);
  let decrypt = await OHCryptLib.decryptCipherTextWithRandomIV(encrypt, key)
  console.info("test OHCryptLib decrypt: " + decrypt);
}

运行结果

image.png

OHCryptLib源码

import { cryptoFramework } from "@kit.CryptoArchitectureKit";
import { buffer, util } from "@kit.ArkTS";
enum EncryptMode{
  ENCRYPT='ENCRYPT',
  DECRYPT='DECRYPT'
}
export class OHCryptLib {

  //加密
  public static async encryptPlainTextWithRandomIV(plainText: string, key: string) {
    console.info('OHCryptLib 加密参数 plainText:' + plainText + ',key:' + key);
    let randomIV16 = await OHCryptLib.generateRandomIV16()
    let initVector = await OHCryptLib.generateRandomIV16()
    let encryptionKey = OHCryptLib.SHA256(key, 32)
    let encrypt = await OHCryptLib.encryptDecrypt( randomIV16 + plainText,encryptionKey, EncryptMode.ENCRYPT, initVector);
    return encrypt
  }

  //解密
  public static async decryptCipherTextWithRandomIV(cipherText: string, key: string) {
    console.info('OHCryptLib 解密参数 cipherText:' + cipherText + ',key:' + key);
    let initVector = await OHCryptLib.generateRandomIV16()
    let encryptionKey = OHCryptLib.SHA256(key, 32)
    let decrypt = await OHCryptLib.encryptDecrypt( cipherText,encryptionKey, EncryptMode.DECRYPT, initVector);
    return decrypt
  }

  private static async encryptDecrypt(inputText: string, encryptionKey: string, mode: string, initVector: string){
    console.info('OHCryptLib encryptDecrypt inputText:' + inputText+",encryptionKey:"+encryptionKey+",mode:"+mode+",initVector:"+initVector);
    let _key = OHCryptLib.stringToUint8Array(encryptionKey)
    let symKey = await OHCryptLib.genSymKeyByData(_key);

    let ivBlobData = OHCryptLib.stringToUint8Array(initVector)
    let ivBlob: cryptoFramework.DataBlob = { data: ivBlobData };
    let ivParamsSpec: cryptoFramework.IvParamsSpec = {
      algName: "IvParamsSpec",
      iv: ivBlob
    }
    let cipher = cryptoFramework.createCipher('AES256|CBC|PKCS7');
    if (mode==EncryptMode.ENCRYPT) {
      await cipher.init(cryptoFramework.CryptoMode.ENCRYPT_MODE, symKey, ivParamsSpec);
      let uint8Array = OHCryptLib.stringToUint8Array(inputText);
      let dataBlobData: cryptoFramework.DataBlob = { data: uint8Array }
      let encryptData = await cipher.doFinal(dataBlobData);
      if (encryptData.data.toString()) {
        let encrypt = OHCryptLib.Base64().encodeToStringSync(encryptData.data)
        console.info('OHCryptLib encryptDecrypt encrypt:' + encrypt);
        return encrypt
      } else {
        console.info('OHCryptLib encryptDecrypt encrypt failed');
        return ''
      }
    }else if (mode==EncryptMode.DECRYPT){
      await cipher.init(cryptoFramework.CryptoMode.DECRYPT_MODE, symKey, ivParamsSpec);
      let decodedValue = OHCryptLib.Base64().decodeSync(inputText);
      let dataBlobData: cryptoFramework.DataBlob = { data: decodedValue }
      let decryptData = await cipher.doFinal(dataBlobData);
      console.info('OHCryptLib encryptDecrypt decryptData.data:' + decryptData.data);
      if (decryptData.data.toString()) {
        let decryptDataStr = buffer.from(decryptData.data).toString('utf-8')
        let decrypt = ""
        if (decryptDataStr.length>16) {
          decrypt = decryptDataStr.substring(16,decryptDataStr.length)
        }else {
          decrypt = decryptDataStr
        }
        console.info('OHCryptLib encryptDecrypt decrypt:' + decrypt);
        return decrypt
      } else {
        console.info('OHCryptLib encryptDecrypt decrypt failed');
        return ''
      }
    }else{
      console.info('OHCryptLib encryptDecrypt mode:'+mode);
      return ''
    }
  }

  private static SHA256(key: string,length:number): string {
    let md = cryptoFramework.createMd('SHA256');
    let firstArray = OHCryptLib.stringToUint8Array(key)
    md.updateSync({ data: firstArray });
    let mdResult = md.digestSync();
    let hex = buffer.from(mdResult.data).toString('hex')
    let result: string
    if (hex.length > length) {
      result = hex.substring(0, length)
    } else {
      result = hex
    }
    console.info('OHCryptLib SHA256 result:' + result);
    return result
  }

  private static async genSymKeyByData(symKeyData: Uint8Array) {
    let symKeyBlob: cryptoFramework.DataBlob = { data: symKeyData };
    let aesGenerator = cryptoFramework.createSymKeyGenerator('AES256');
    let symKey = await aesGenerator.convertKey(symKeyBlob);
    return symKey;
  }

  private static stringToUint8Array(str: string) {
    return new Uint8Array(buffer.from(str, 'utf-8').buffer)
  }
  private static async generateRandomIV16() {
    let rand = cryptoFramework.createRandom();
    let seed = new Uint8Array([0]);
    rand.setSeed({ data: seed });
    let len = 16;
    let randOutput = await rand.generateRandom(len);
    let hex = buffer.from(randOutput.data).toString('hex')
    let result = ""
    if (len < hex.length) {
      result = hex.substring(0, len)
    } else {
      result = hex
    }
    return result
  }

  private static Base64() {
    return new util.Base64Helper()
  }
}

进一步业务逻辑验证

本项目现有的用户密码加密业务逻辑:

1、客户端使用本地密钥A对从服务端获取的appSecret数据进行解密,从而得到结果B(对密码加密的密钥);

2、客户端使用B对用户密码password进行加密得到C,将C作为登录接口参数传递给服务端。

如果登录接口成功返回用户数据,则证明鸿蒙版密码加密逻辑成功适配现有加密逻辑;否则返回“用户不存在或密码错误”,则证明适配失败。

业务逻辑验证代码及接口返回结果截图:

image.png

鸿蒙版密码加密逻辑成功获取到用户数据。