HUKS如何分段加解密
===========

#### 问题现象

如何使用密钥管理服务进行分段加解密？

#### 背景知识

[加解密(ArkTS)](https://developer.huawei.com/consumer/cn/doc/harmonyos-guides/huks-encryption-decryption-arkts)：以AES128、RSA2048和SM2为例，完成加解密。

[huks.updateSession](https://developer.huawei.com/consumer/cn/doc/harmonyos-references/js-apis-huks#huksupdatesession9)：[huks.initSession](https://developer.huawei.com/consumer/cn/doc/harmonyos-references/js-apis-huks#huksinitsession9-1)，[huks.updateSession](https://developer.huawei.com/consumer/cn/doc/harmonyos-references/js-apis-huks#huksupdatesession9)，[huks.finishSession](https://developer.huawei.com/consumer/cn/doc/harmonyos-references/js-apis-huks#huksfinishsession9-2)为三段式接口，分段加解密时，传入finishSession的inData要设置为空。

#### 解决方案

分段加解密可通过将明文进行指定长度多段拆分的形式传入，以1024为例，实现AES/CBC/PKCS7分段加解密。完整代码如下：

import { hilog } from '@kit.PerformanceAnalysisKit';
import { huks } from '@kit.UniversalKeystoreKit';
import { BusinessError } from '@kit.BasicServicesKit';
import { buffer } from '@kit.ArkTS';

const MAX\_UPDATE\_SESSION\_LENGTH = 1024;

let aesKeyAlias = 'test\_aesKeyAlias';
let handle: number;
let plainText = '1234567890';
let result = '';
let IV = '001122334455';
let cipherData: Uint8Array;

@Entry
@Component
struct Index {
  build() {
    Column() {
      Button('测试加解密')
        .width('100%')
        .onClick(async () => {
          generatorData();
          await GenerateAesKey();
          await EncryptData();
          await DecryptData();
          check();
        });
    }
    .width('100%')
    .height('100%')
    .alignItems(HorizontalAlign.Center)
    .justifyContent(FlexAlign.Center);
  }
}

function StringToUint8Array(data: string): Uint8Array {
  return new Uint8Array(buffer.from(data, 'utf-8').buffer);
}

function Uint8ArrayToString(input: Uint8Array): string {
  let bufferStr = buffer.from(input).toString('utf-8');
  return bufferStr;
}

function generatorData() {
  let i = 0;
  plainText = '1234567890';
  while (i <= 10) {
    i++;
    plainText = plainText + plainText;
  }
  console.info('造数据成功');
}

_// 生成密钥_
async function GenerateAesKey() {
  let genProperties = GetAesGenerateProperties();
  let options: huks.HuksOptions = {
    properties: genProperties
  };

  await huks.generateKeyItem(aesKeyAlias, options)
    .then(() => {
      hilog.info(0x0000, 'AppDataSecurity', \`promise: generate AES Key success\`);
    }).catch((error: Error) => {
      console.error(\`promise: generate AES Key failed, ${error.message}\`);
    });
}

function GetAesGenerateProperties() {
  let properties: Array<huks.HuksParam> = \[{
    tag: huks.HuksTag.HUKS\_TAG\_ALGORITHM,
    value: huks.HuksKeyAlg.HUKS\_ALG\_AES
  }, {
    tag: huks.HuksTag.HUKS\_TAG\_KEY\_SIZE,
    value: huks.HuksKeySize.HUKS\_AES\_KEY\_SIZE\_128
  }, {
    tag: huks.HuksTag.HUKS\_TAG\_PURPOSE,
    value: huks.HuksKeyPurpose.HUKS\_KEY\_PURPOSE\_ENCRYPT |
    huks.HuksKeyPurpose.HUKS\_KEY\_PURPOSE\_DECRYPT
  }\];
  return properties;
}

function GetAesEncryptProperties() {
  let properties: Array<huks.HuksParam> = \[{
    tag: huks.HuksTag.HUKS\_TAG\_ALGORITHM,
    value: huks.HuksKeyAlg.HUKS\_ALG\_AES
  }, {
    tag: huks.HuksTag.HUKS\_TAG\_KEY\_SIZE,
    value: huks.HuksKeySize.HUKS\_AES\_KEY\_SIZE\_128
  }, {
    tag: huks.HuksTag.HUKS\_TAG\_PURPOSE,
    value: huks.HuksKeyPurpose.HUKS\_KEY\_PURPOSE\_ENCRYPT
  }, {
    tag: huks.HuksTag.HUKS\_TAG\_PADDING,
    value: huks.HuksKeyPadding.HUKS\_PADDING\_PKCS7
  }, {
    tag: huks.HuksTag.HUKS\_TAG\_BLOCK\_MODE,
    value: huks.HuksCipherMode.HUKS\_MODE\_CBC
  }, {
    tag: huks.HuksTag.HUKS\_TAG\_IV,
    value: StringToUint8Array(IV)
  }\];
  return properties;
}

function GetAesDecryptProperties() {
  let properties: Array<huks.HuksParam> = \[{
    tag: huks.HuksTag.HUKS\_TAG\_ALGORITHM,
    value: huks.HuksKeyAlg.HUKS\_ALG\_AES
  }, {
    tag: huks.HuksTag.HUKS\_TAG\_KEY\_SIZE,
    value: huks.HuksKeySize.HUKS\_AES\_KEY\_SIZE\_128
  }, {
    tag: huks.HuksTag.HUKS\_TAG\_PURPOSE,
    value: huks.HuksKeyPurpose.HUKS\_KEY\_PURPOSE\_DECRYPT
  }, {
    tag: huks.HuksTag.HUKS\_TAG\_PADDING,
    value: huks.HuksKeyPadding.HUKS\_PADDING\_PKCS7
  }, {
    tag: huks.HuksTag.HUKS\_TAG\_BLOCK\_MODE,
    value: huks.HuksCipherMode.HUKS\_MODE\_CBC
  }, {
    tag: huks.HuksTag.HUKS\_TAG\_IV,
    value: StringToUint8Array(IV)
  }\];
  return properties;
}

_// 分段加密_
async function EncryptData() {
  let encryptProperties = GetAesEncryptProperties();
  let options: huks.HuksOptions = {
    properties: encryptProperties,
    inData: new Uint8Array(0)
  };

  await huks.initSession(aesKeyAlias, options)
    .then((data) => {
      handle = data.handle;
    }).catch((error: Error) => {
      console.error(\`promise: init EncryptData failed, ${error.message}\`);
    });

  let resultTemp = new Uint8Array(0);
  let contentTemp = plainText;
  while (contentTemp.length > 0) {
    _// 超过长度会取最长_
    const contentCurr = contentTemp.substring(0, MAX\_UPDATE\_SESSION\_LENGTH);
    _// 起始点大于长度会返回""_
    contentTemp = contentTemp.substring(MAX\_UPDATE\_SESSION\_LENGTH, contentTemp.length);
    options.inData = StringToUint8Array(contentCurr);
    await huks.updateSession(handle, options).then((data) => {
      const mergeText = new Uint8Array(resultTemp.length + data.outData!.length);
      mergeText.set(resultTemp);
      mergeText.set(data.outData!, resultTemp.length);
      resultTemp = mergeText;
    }).catch(async (error: BusinessError) => {
      console.error(\`promise: encrypt updateSession failed, ${error.message}\`);
    });
  }

  _// 分段加解密时，传入finishSession的inData要设置为空的_
  options.inData = new Uint8Array(0);
  await huks.finishSession(handle, options).then((data) => {
    cipherData = new Uint8Array(resultTemp.length + data.outData!.length);
    cipherData.set(resultTemp);
    cipherData.set(data.outData!, resultTemp.length);
  }).catch((error: Error) => {
    console.error(\`promise: encrypt data failed, ${error.message}\`);
  });
}

_// 分段解密_
async function DecryptData() {
  let decryptOptions = GetAesDecryptProperties();
  let options: huks.HuksOptions = {
    properties: decryptOptions,
    inData: cipherData
  };

  await huks.initSession(aesKeyAlias, options)
    .then((data) => {
      handle = data.handle;
    }).catch((error: Error) => {
      console.error(\`promise: init DecryptData failed, ${error.message}\`);
    });

  let resultTemp = new Uint8Array(0);
  let contentTemp = cipherData;
  while (contentTemp.length > 0) {
    const contentCurr = contentTemp.slice(0, MAX\_UPDATE\_SESSION\_LENGTH); _// 超过长度会取最长_
    contentTemp = contentTemp.slice(MAX\_UPDATE\_SESSION\_LENGTH, contentTemp.length);
    options.inData = contentCurr;
    await huks.updateSession(handle, options).then((data) => {
      const mergeText = new Uint8Array(resultTemp.length + data.outData!.length);
      mergeText.set(resultTemp);
      mergeText.set(data.outData!, resultTemp.length);
      resultTemp = mergeText;
    }).catch((error: Error) => {
      console.error(\`promise: decrypt updateSession failed, ${error.message}\`);
    });
  }

  options.inData = new Uint8Array(0);
  await huks.finishSession(handle, options).then(() => {
    result = Uint8ArrayToString(resultTemp);
  }).catch((error: Error) => {
    console.error(\`promise: decrypt data failed, ${error.message}\`);
  });
}

_// 验证_
function check() {
  console.info('批量加解密结果：', result === plainText);
}