# HUKS Development (ArkTS) ## Key Generation The OpenHarmony Universal KeyStore (HUKS) provides key management and cryptography operations for services. For a key generated by the HUKS, the plaintext will never be exposed outside throughout the lifecycle. No one can obtain the key in plaintext. Even the service itself can call APIs provided by the HUKS to perform operations on the key and obtain the operation result, but cannot access the key. **How to Develop** Use [huks.generateKeyItem(keyAlias,options,callback)](../reference/apis/js-apis-huks.md#huksgeneratekeyitem9) to generate a key. You need to pass in the key alias in **keyAlias**, key property set in **options**, and **callback** to return the result asynchronously. For details about the APIs, see [HUKS](../reference/apis/js-apis-huks.md). **Procedure** 1. Set the key alias. 2. Initialize the key property set.
Use [HuksParam](../reference/apis/js-apis-huks.md#huksparam) to encapsulate key properties and use a **HuksParam** array to assign values to the **properties** field of [HuksOptions](../reference/apis/js-apis-huks.md#huksoptions). The parameters [HuksKeyAlg](../reference/apis/js-apis-huks.md#hukskeyalg), [HuksKeySize](../reference/apis/js-apis-huks.md#hukskeysize), and [HuksKeyPurpose](../reference/apis/js-apis-huks.md#hukskeypurpose) are mandatory. 3. Pass in the key alias and key parameter set to generate a key. > **NOTE** > > The key alias cannot exceed 64 bytes. **Sample Code** ```ts /* * Generate a DH key and return the result by a callback. */ import huks from '@ohos.security.huks'; /* * Set the key alias and encapsulate the key properties. */ let keyAlias = 'dh_key'; let properties = new Array(); properties[0] = { tag: huks.HuksTag.HUKS_TAG_ALGORITHM, value: huks.HuksKeyAlg.HUKS_ALG_DH } properties[1] = { tag: huks.HuksTag.HUKS_TAG_PURPOSE, value: huks.HuksKeyPurpose.HUKS_KEY_PURPOSE_AGREE } properties[2] = { tag: huks.HuksTag.HUKS_TAG_KEY_SIZE, value: huks.HuksKeySize.HUKS_DH_KEY_SIZE_2048 } properties[3] = { tag: huks.HuksTag.HUKS_TAG_DIGEST, value: huks.HuksKeyDigest.HUKS_DIGEST_SHA256 } let huksOptions = { properties: properties, inData: new Uint8Array(new Array()) } /* * Generate a key. */ function generateKeyItem(keyAlias: string, huksOptions: huks.HuksOptions) { return new Promise((resolve, reject) => { try { huks.generateKeyItem(keyAlias, huksOptions, function (error, data) { if (error) { reject(error); } else { resolve(data); } }); } catch (error) { throw (error); } }); } async function publicGenKeyFunc(keyAlias: string, huksOptions: huks.HuksOptions) { console.info(`enter callback generateKeyItem`); try { await generateKeyItem(keyAlias, huksOptions) .then((data) => { console.info(`callback: generateKeyItem success, data = ${JSON.stringify(data)}`); }) .catch(error => { console.error(`callback: generateKeyItem failed, code: ${error.code}, msg: ${error.message}`); }); } catch (error) { console.error(`callback: generateKeyItem input arg invalid, code: ${error.code}, msg: ${error.message}`); } } async function TestGenKey() { await publicGenKeyFunc(keyAlias, huksOptions); } ``` ## Key Import A key generated outside the HUKS (for example, generated through key agreement or by a server) can be imported to the HUKS for management. The HUKS supports import of keys in plaintext. However, if a key is imported in plaintext, the key is exposed in the Rich Execution Environment (REE) memory. This type of import applies to lightweight devices or security-insensitive services. For security-sensitive services, use the secure import feature provided by the HUKS. Secure import allows the keys generated for services to be transferred to the HUKS through an end-to-end encrypted transmission channel. Once a key is imported to the HUKS, its plaintext will never be exposed outside the HUKS throughout the lifecycle of the key. ### Importing a Key in Plaintext Use [huks.importKeyItem(keyAlias,options,callback)](../reference/apis/js-apis-huks.md#huksimportkeyitem9) to import a key in plaintext. You need to pass in the key alias in **keyAlias**, key material and property set in **options**, and **callback** to return the result asynchronously. For details about the APIs, see [HUKS](../reference/apis/js-apis-huks.md). 1. Set the key alias. 2. Encapsulate the key material and key property set.
The key material must comply with [HUKS key material formats](./huks-appendix.md#key-material-formats). The **inData** value of [HuksOptions](../reference/apis/js-apis-huks.md#huksoptions) must be in the Uint8Array format. Encapsulate key properties in [HuksParam](../reference/apis/js-apis-huks.md#huksparam), and use a **HuksParam** array to assign values to the **properties** field. The key properties must contain [HuksKeyAlg](../reference/apis/js-apis-huks.md#hukskeyalg), [HuksKeySize](../reference/apis/js-apis-huks.md#hukskeysize), and [HuksKeyPurpose](../reference/apis/js-apis-huks.md#hukskeypurpose). 3. Import the key. **Sample Code** ```ts /* /* Import an AES key of 256 bits. */ */ /* Key */ let plainTextSize32 = new Uint8Array([ 0xfb, 0x8b, 0x9f, 0x12, 0xa0, 0x83, 0x19, 0xbe, 0x6a, 0x6f, 0x63, 0x2a, 0x7c, 0x86, 0xba, 0xca, 0x64, 0x0b, 0x88, 0x96, 0xe2, 0xfa, 0x77, 0xbc, 0x71, 0xe3, 0x0f, 0x0f, 0x9e, 0x3c, 0xe5, 0xf9 ]); /* * Set the key alias. */ let keyAlias = 'AES256Alias_sample'; /* * Encapsulate the key properties and key material. */ let properties = new Array(); properties[0] = { tag: huks.HuksTag.HUKS_TAG_ALGORITHM, value: huks.HuksKeyAlg.HUKS_ALG_AES }; properties[1] = { tag: huks.HuksTag.HUKS_TAG_KEY_SIZE, value: huks.HuksKeySize.HUKS_AES_KEY_SIZE_256 }; properties[2] = { tag: huks.HuksTag.HUKS_TAG_PURPOSE, value: huks.HuksKeyPurpose.HUKS_KEY_PURPOSE_ENCRYPT | huks.HuksKeyPurpose.HUKS_KEY_PURPOSE_DECRYPT }; let options = { properties: properties, inData: plainTextSize32 }; /* * Import the key. */ try { huks.importKeyItem(keyAlias, options, function (error, data) { if (error) { console.error(`callback: importKeyItem failed, code: ${error.code}, msg: ${error.message}`); } else { console.info(`callback: importKeyItem success`); } }); } catch (error) { console.error(`callback: importKeyItem input arg invalid, code: ${error.code}, msg: ${error.message}`); } ``` **Verification** Check whether the key exists. If yes, the key is imported successfully. **Sample Code** ```ts import huks from '@ohos.security.huks'; let keyAlias = 'AES256Alias_sample'; let isKeyExist; let keyProperties = new Array(); keyProperties[0] = { tag: huks.HuksTag.HUKS_TAG_ALGORITHM, value: huks.HuksKeyAlg.HUKS_ALG_AES, } let huksOptions = { properties: keyProperties, // It cannot be empty. inData: new Uint8Array(new Array()) // It cannot be empty. } try { huks.isKeyItemExist(keyAlias, huksOptions, function (error, data) { if (error) { console.error(`callback: isKeyItemExist failed, code: ${error.code}, msg: ${error.message}`); } else { if (data !== null && data.valueOf() !== null) { isKeyExist = data.valueOf(); console.info(`callback: isKeyItemExist success, isKeyExist = ${isKeyExist}`); } } }); } catch (error) { console.error(`callback: isKeyItemExist input arg invalid, code: ${error.code}, msg: ${error.message}`); } ``` ### Importing a Key Securely Compared with import of a key in plaintext, secure import involves more complex key material and operations. The following figure illustrates the basic development process of secure import. **Figure 1** Development process of secure import ![huks_import_wrapped_key](figures/huks_import_wrapped_key.png) **Available APIs** You need to use the APIs listed in the following table in sequence. **Table 1** APIs for importing a key securely | API | Description | | -------------------------------------- | ----------------------------| |generateKeyItem(keyAlias: string, options: HuksOptions, callback: AsyncCallback\) : void| Generates a key.| |exportKeyItem(keyAlias: string, options: HuksOptions, callback: AsyncCallback\) : void| Exports the public key of a key pair.| |importWrappedKeyItem(keyAlias: string, wrappingKeyAlias: string, options: HuksOptions, callback: AsyncCallback\) : void|Imports a encrypted key.| |deleteKeyItem(keyAlias: string, options: HuksOptions, callback: AsyncCallback\) : void|Deletes a key.| >**NOTE** > >The public key plaintext material returned by **exportKeyItem()** is encapsulated in X.509 format, and the key material to be imported by **importWrappedKeyItem()** must be encapsulated in **LengthData-Data** format. Specifically, the application needs to request a Uint8Array and encapsulate the Uint8Array in the sequence listed in the following table. **Table 2** Format of the encrypted key material | Content| Public Key Length (Lpk2)| Public Key (pk2)| k2 AAD Length (LAAD2)| k2 AAD (AAD2)| k2 Nonce Length (LNonce2)| k2 Nonce (Nonce2)| | :--: |:----:|:----: |:----: | :----: | :----:|:----:| |Length| 4 bytes|Lpk2 bytes| 4 bytes| LAAD2 bytes| 4 bytes| LNonce2 bytes| | Content| k2 AEAD Length (LAEAD2)| k2 AEAD (AEAD2)| k3 Ciphertext Length (Lk3_enc)| k3 Ciphertext (k3_enc)| k3 AAD Length (LAAD3)| k3 AAD (AAD3)| |Length| 4 bytes|LAEAD2 bytes| 4 bytes| Lk3_enc bytes| 4 bytes| LAAD3 bytes| | Content| k3 Nonce Length (LNonce3)| k3 Nonce (Nonce3)| k3 AEAD Length (LAEAD3)| k3 AEAD (AEAD3)| Length of **k1'_size** (Lk1'_size)| Key Plaintext Material Length (k1'_size)| |Length| 4 bytes|LNonce3 bytes| 4 bytes| LAEAD3 bytes| 4 bytes| Lk1'_size bytes| |Content|k1' Ciphertext Length (Lk1'_enc)| k1' ciphertext (k1'_enc) | | | | | |Length| 4 bytes|Lk1'_enc bytes| | | | | **How to Develop** The following example presents the development involving HUKS APIs (using the ECDH key agreement suite). The operations performed by the service are not included. 1. Convert the key material into the HUKS format. 2. Generate a key for secure import. 3. Export the public key material. 4. Encapsulate (encrypt) the key material to be imported. 5. Import the encapsulated key material. 6. Delete the key used for secure import. **Sample Code** ```ts /* * Import an SM2 key and return the result by a callback. */ import huks from '@ohos.security.huks'; /* * Set the key alias. */ let importAlias = "importAlias"; let wrapAlias = "wrappingKeyAlias"; let exportKey; /* * Convert the key material into a HUKS ECC-P-256 key pair for secure import. */ let inputEccPair = new Uint8Array([ 0x02, 0x00, 0x00, 0x00, // key algorithm: huks.HuksKeyAlg.HUKS_ALG_ECC = 2 0x00, 0x01, 0x00, 0x00, // key size: 256 bits 0x20, 0x00, 0x00, 0x00, // Coordinate x length: 32 bytes 0x20, 0x00, 0x00, 0x00, // Coordinate y length: 32 bytes 0x20, 0x00, 0x00, 0x00, // Coordinate z length: 32 bytes // Coordinate x 0xa5, 0xb8, 0xa3, 0x78, 0x1d, 0x6d, 0x76, 0xe0, 0xb3, 0xf5, 0x6f, 0x43, 0x9d, 0xcf, 0x60, 0xf6, 0x0b, 0x3f, 0x64, 0x45, 0xa8, 0x3f, 0x1a, 0x96, 0xf1, 0xa1, 0xa4, 0x5d, 0x3e, 0x2c, 0x3f, 0x13, // Coordinate y 0xd7, 0x81, 0xf7, 0x2a, 0xb5, 0x8d, 0x19, 0x3d, 0x9b, 0x96, 0xc7, 0x6a, 0x10, 0xf0, 0xaa, 0xbc, 0x91, 0x6f, 0x4d, 0xa7, 0x09, 0xb3, 0x57, 0x88, 0x19, 0x6f, 0x00, 0x4b, 0xad, 0xee, 0x34, 0x35, // Coordinate z 0xfb, 0x8b, 0x9f, 0x12, 0xa0, 0x83, 0x19, 0xbe, 0x6a, 0x6f, 0x63, 0x2a, 0x7c, 0x86, 0xba, 0xca, 0x64, 0x0b, 0x88, 0x96, 0xe2, 0xfa, 0x77, 0xbc, 0x71, 0xe3, 0x0f, 0x0f, 0x9e, 0x3c, 0xe5, 0xf9 ]); /* * Encapsulate the key properties. */ // Property set for the key used for secure import. let properties = new Array(); properties[0] = { tag: huks.HuksTag.HUKS_TAG_ALGORITHM, value: huks.HuksKeyAlg.HUKS_ALG_ECC }; properties[1] = { tag: huks.HuksTag.HUKS_TAG_KEY_SIZE, value: huks.HuksKeySize.HUKS_ECC_KEY_SIZE_256 }; properties[2] = { tag: huks.HuksTag.HUKS_TAG_PURPOSE, value: huks.HuksKeyPurpose.HUKS_KEY_PURPOSE_UNWRAP }; properties[3] = { tag: huks.HuksTag.HUKS_TAG_DIGEST, value: huks.HuksKeyDigest.HUKS_DIGEST_SHA256 }; properties[4] = { tag: huks.HuksTag.HUKS_TAG_IMPORT_KEY_TYPE, value: huks.HuksImportKeyType.HUKS_KEY_TYPE_KEY_PAIR, }; let huksOptions = { properties: properties, inData: inputEccPair }; // Property set of the key to be imported: AES256 let importProperties = new Array(); importProperties[0] = { tag: huks.HuksTag.HUKS_TAG_ALGORITHM, value: huks.HuksKeyAlg.HUKS_ALG_AES }; importProperties[1] = { tag: huks.HuksTag.HUKS_TAG_KEY_SIZE, value: huks.HuksKeySize.HUKS_AES_KEY_SIZE_256 }; importProperties[2] = { tag: huks.HuksTag.HUKS_TAG_PURPOSE, value: huks.HuksKeyPurpose.HUKS_KEY_PURPOSE_ENCRYPT | huks.HuksKeyPurpose.HUKS_KEY_PURPOSE_DECRYPT }; importProperties[3] = { tag: huks.HuksTag.HUKS_TAG_BLOCK_MODE, value: huks.HuksCipherMode.HUKS_MODE_CBC }; importProperties[4] = { tag: huks.HuksTag.HUKS_TAG_PADDING, value: huks.HuksKeyPadding.HUKS_PADDING_NONE }; importProperties[5] = { tag: huks.HuksTag.HUKS_TAG_UNWRAP_ALGORITHM_SUITE, value: huks.HuksUnwrapSuite.HUKS_UNWRAP_SUITE_ECDH_AES_256_GCM_NOPADDING // Use the ECDH+AES256GCM suite. }; let importOptions = { properties: importProperties, inData: new Uint8Array(new Array()) }; // Export the public key of the key pair. function exportKeyItem(keyAlias:string, huksOptions:huks.HuksOptions, throwObject) : Promise { return new Promise((resolve, reject) => { try { huks.exportKeyItem(keyAlias, huksOptions, function (error, data) { if (error) { reject(error); } else { resolve(data); } }); } catch (error) { throwObject.isThrow = true; throw(error); } }); } async function publicExportKeyFunc(keyAlias:string, huksOptions:huks.HuksOptions) { console.info(`enter callback export`); let throwObject = {isThrow: false}; try { await exportKeyItem(keyAlias, huksOptions, throwObject) .then ((data) => { console.info(`callback: exportKeyItem success, data = ${JSON.stringify(data)}`); exportKey = data.outData; }) .catch(error => { if (throwObject.isThrow) { throw(error); } else { console.error(`callback: exportKeyItem failed, code: ${error.code}, msg: ${error.message}`); } }); } catch (error) { console.error(`callback: exportKeyItem input arg invalid, code: ${error.code}, msg: ${error.message}`); } } // Generate a key used for secure import (the key is imported here). function importKeyItem(keyAlias:string, huksOptions:huks.HuksOptions, throwObject) { return new Promise((resolve, reject) => { try { huks.importKeyItem(keyAlias, huksOptions, function (error, data) { if (error) { reject(error); } else { resolve(data); } }); } catch (error) { throwObject.isThrow = true; throw(error); } }); } async function publicImportKeyFunc(keyAlias:string, huksOptions:huks.HuksOptions) { console.info(`enter promise importKeyItem`); let throwObject = {isThrow: false}; try { await importKeyItem(keyAlias, huksOptions, throwObject) .then ((data) => { console.info(`callback: importKeyItem success, data = ${JSON.stringify(data)}`); }) .catch(error => { if (throwObject.isThrow) { throw(error); } else { console.error(`callback: importKeyItem failed, code: ${error.code}, msg: ${error.message}`); } }); } catch (error) { console.error(`callback: importKeyItem input arg invalid, code: ${error.code}, msg: ${error.message}`); } } // Perform secure import. async function publicImportWrappedKey(keyAlias:string, wrappingKeyAlias:string, huksOptions:huks.HuksOptions) { console.info(`enter callback importWrappedKeyItem`); var throwObject = {isThrow: false}; try { await importWrappedKeyItem(keyAlias, wrappingKeyAlias, huksOptions, throwObject) .then ((data) => { console.info(`callback: importWrappedKeyItem success, data = ${JSON.stringify(data)}`); }) .catch(error => { if (throwObject.isThrow) { throw(error); } else { console.error(`callback: importWrappedKeyItem failed, code: ${error.code}, msg: ${error.message}`); } }); } catch (error) { console.error(`callback: importWrappedKeyItem input arg invalid, code: ${error.code}, msg: ${error.message}`); } } function importWrappedKeyItem(keyAlias:string, wrappingKeyAlias:string, huksOptions:huks.HuksOptions, throwObject) { return new Promise((resolve, reject) => { try { huks.importWrappedKeyItem(keyAlias, wrappingKeyAlias, huksOptions, function (error, data) { if (error) { reject(error); } else { resolve(data); } }); } catch (error) { throwObject.isThrow = true; throw(error); } }); } // Delete the key used for secure import. function deleteKeyItem(keyAlias:string, huksOptions:huks.HuksOptions, throwObject) { return new Promise((resolve, reject) => { try { huks.deleteKeyItem(keyAlias, huksOptions, function (error, data) { if (error) { reject(error); } else { resolve(data); } }); } catch (error) { throwObject.isThrow = true; throw(error); } }); } async function publicDeleteKeyFunc(keyAlias:string, huksOptions:huks.HuksOptions) { console.info(`enter callback deleteKeyItem`); let throwObject = {isThrow: false}; try { await deleteKeyItem(keyAlias, huksOptions, throwObject) .then ((data) => { console.info(`callback: deleteKeyItem key success, data = ${JSON.stringify(data)}`); }) .catch(error => { if (throwObject.isThrow) { throw(error); } else { console.error(`callback: deleteKeyItem failed, code: ${error.code}, msg: ${error.message}`); } }); } catch (error) { console.error(`callback: deletKeeyItem input arg invalid, code: ${error.code}, msg: ${error.message}`); } } async function ImportWrappedKeyNormalTest() { console.info(`enter ImportWrapKey test`); /* * Generate a key for secure import (the key is imported here). */ await publicImportKeyFunc(wrapAlias, huksOptions); /* * Export the public key material of the key used for secure import. */ await publicExportKeyFunc(wrapAlias, huksOptions); /*---------------------------------------------------------------------------------------------- * The processes of generating an ECC key pair, performing ECDH key agreement, generating a k3, and encrypting k1' and k3 on the service side are omitted. *----------------------------------------------------------------------------------------------*/ /* Encapsulate the key material to be imported. * Create the importOptions.inData field in the following format: * pk2 length (4 bytes) + pk2 + AAD2 length (4 bytes) + AAD2 + * Nonce2 length (4 bytes) + Nonce2 + AEAD2 length (4 bytes) + AEAD2 + * k3 ciphertext length (4 bytes) + k3 ciphertext + AAD3 length (4 bytes) + AAD3 + * Nonce3 length (4 bytes) +Nonce3 + AEAD3 length (4 bytes) + AEAD3 + * k1'_size length (4 bytes) + k1'_size + k1'_enc length (4 bytes) + k1'_enc */ let inputKey = new Uint8Array([ 0x5b, 0x00, 0x00, 0x00, // ECC-P-256 public key length (DER format defined in X.509): 91 // ECC-P-256 public key 0x30, 0x59, 0x30, 0x13, 0x06, 0x07, 0x2a, 0x86, 0x48, 0xce, 0x3d, 0x02, 0x01, 0x06, 0x08, 0x2a, 0x86, 0x48, 0xce, 0x3d, 0x03, 0x01, 0x07, 0x03, 0x42, 0x00, 0x04, 0xc0, 0xfe, 0x1c, 0x67, 0xde, 0x86, 0x0e, 0xfb, 0xaf, 0xb5, 0x85, 0x52, 0xb4, 0x0e, 0x1f, 0x6c, 0x6c, 0xaa, 0xc5, 0xd9, 0xd2, 0x4d, 0xb0, 0x8a, 0x72, 0x24, 0xa1, 0x99, 0xaf, 0xfc, 0x3e, 0x55, 0x5a, 0xac, 0x99, 0x3d, 0xe8, 0x34, 0x72, 0xb9, 0x47, 0x9c, 0xa6, 0xd8, 0xfb, 0x00, 0xa0, 0x1f, 0x9f, 0x7a, 0x41, 0xe5, 0x44, 0x3e, 0xb2, 0x76, 0x08, 0xa2, 0xbd, 0xe9, 0x41, 0xd5, 0x2b, 0x9e, 0x10, 0x00, 0x00, 0x00, // AAD2 length: 16 // AAD2 0xbf, 0xf9, 0x69, 0x41, 0xf5, 0x49, 0x85, 0x31, 0x35, 0x14, 0x69, 0x12, 0x57, 0x9c, 0xc8, 0xb7, 0x10, 0x00, 0x00, 0x00, // Nonce2 length: 16 // Nonce2 0x2d, 0xb7, 0xf1, 0x5a, 0x0f, 0xb8, 0x20, 0xc5, 0x90, 0xe5, 0xca, 0x45, 0x84, 0x5c, 0x08, 0x08, 0x10, 0x00, 0x00, 0x00, // AEAD2 length: 16 // AEAD2 0x43, 0x25, 0x1b, 0x2f, 0x5b, 0x86, 0xd8, 0x87, 0x04, 0x4d, 0x38, 0xc2, 0x65, 0xcc, 0x9e, 0xb7, 0x20, 0x00, 0x00, 0x00, // k3 ciphertext length: 32 // k3 ciphertext 0xf4, 0xe8, 0x93, 0x28, 0x0c, 0xfa, 0x4e, 0x11, 0x6b, 0xe8, 0xbd, 0xa8, 0xe9, 0x3f, 0xa7, 0x8f, 0x2f, 0xe3, 0xb3, 0xbf, 0xaf, 0xce, 0xe5, 0x06, 0x2d, 0xe6, 0x45, 0x5d, 0x19, 0x26, 0x09, 0xe7, 0x10, 0x00, 0x00, 0x00, // AAD3 length: 16 // AAD3 0xf4, 0x1e, 0x7b, 0x01, 0x7a, 0x84, 0x36, 0xa4, 0xa8, 0x1c, 0x0d, 0x3d, 0xde, 0x57, 0x66, 0x73, 0x10, 0x00, 0x00, 0x00, // Nonce3 length: 16 // Nonce3 0xe3, 0xff, 0x29, 0x97, 0xad, 0xb3, 0x4a, 0x2c, 0x50, 0x08, 0xb5, 0x68, 0xe1, 0x90, 0x5a, 0xdc, 0x10, 0x00, 0x00, 0x00, // AEAD3 length: 16 // AEAD3 0x26, 0xae, 0xdc, 0x4e, 0xa5, 0x6e, 0xb1, 0x38, 0x14, 0x24, 0x47, 0x1c, 0x41, 0x89, 0x63, 0x11, 0x04, 0x00, 0x00, 0x00, // Length of k1_size: 4 bytes // k1_size: 32 bytes 0x20, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, // Ciphertext length of the key to be imported: 32 bytes // Ciphertext of the key to be imported 0x0b, 0xcb, 0xa9, 0xa8, 0x5f, 0x5a, 0x9d, 0xbf, 0xa1, 0xfc, 0x72, 0x74, 0x87, 0x79, 0xf2, 0xf4, 0x22, 0x0c, 0x8a, 0x4d, 0xd8, 0x7e, 0x10, 0xc8, 0x44, 0x17, 0x95, 0xab, 0x3b, 0xd2, 0x8f, 0x0a ]); importOptions.inData = inputKey; /* * Import the encapsulated key material. */ await publicImportWrappedKey(importAlias, wrapAlias, importOptions); /* * Delete the key used for secure import. */ await publicDeleteKeyFunc(wrapAlias, huksOptions); } ``` **Verification** Check whether the key exists. If yes, the key is imported successfully. **Sample Code** ```ts import huks from '@ohos.security.huks'; /* * Set the key alias and encapsulate the key properties. */ let keyAlias = 'importAlias'; let isKeyExist; let keyProperties = new Array(); keyProperties[0] = { tag: huks.HuksTag.HUKS_TAG_ALGORITHM, value: huks.HuksKeyAlg.HUKS_ALG_AES, } let huksOptions = { properties: keyProperties, // It cannot be empty. inData: new Uint8Array(new Array()) // It cannot be empty. } try { huks.isKeyItemExist(keyAlias, huksOptions, function (error, data) { if (error) { console.error(`callback: isKeyItemExist failed, code: ${error.code}, msg: ${error.message}`); } else { if (data !== null && data.valueOf() !== null) { isKeyExist = data.valueOf(); console.info(`callback: isKeyItemExist success, isKeyExist = ${isKeyExist}`); } } }); } catch (error) { console.error(`callback: isKeyItemExist input arg invalid, code: ${error.code}, msg: ${error.message}`); } ``` ## Common Key Operations **When to Use** To ensure data confidentiality and integrity, you may need to encrypt or decrypt data, generate or verify a signature, perform key agreement, and derive a key. The following describes common key operations. The following examples do not involve secondary identity access control. If secondary identity access control is involved, see [Key Access Control](#key-access-control). **General Development Process** The HUKS operates data based on key sessions. The general process is as follows: 1. (Mandatory) Use [huks.initSession()](../reference/apis/js-apis-huks.md#huksinitsession9) to initialize a key session.
You need to pass in the key alias and key operation parameters to initialize a key session, and obtain the session handle. The key operation parameters must contain the parameters required by the cipher algorithm, including the cipher algorithm, key size, key purpose, working mode, padding mode, hash mode, IV, nonce, and AAD. If access control is set for the key, other parameters are required. For details, see [Key Access Control](#key-access-control). 2. (Optional) Use [huks.updateSession()](../reference/apis/js-apis-huks.md#huksupdatesession9) to pass in data by segment. Perform this step only if the data exceeds 100 KB or the cryptographic algorithm requires operations by data segment. Otherwise, skip this step. 3. (Mandatory) Use [huks.finishSession()](../reference/apis/js-apis-huks.md#huksfinishsession9) to complete the key session operation.
Pass in the last data segment and complete the key session operation. If an error occurs during the process or the data passed in is not required, use [huks.abortSession()](../reference/apis/js-apis-huks.md#huksabortsession9) to abort the session. ### Encryption and Decryption ```ts /* * The following uses an AES 128-bit key and callback-based APIs as an example. */ import huks from '@ohos.security.huks'; import promptAction from '@ohos.promptAction'; let aesKeyAlias = 'test_aesKeyAlias'; let handle; let plainText = '123456'; let IV = '001122334455'; let cipherData:Uint8Array; let plainData:Uint8Array; function StringToUint8Array(str) { let arr = []; for (let i = 0, j = str.length; i < j; ++i) { arr.push(str.charCodeAt(i)); } return new Uint8Array(arr); } function Uint8ArrayToString(fileData) { let dataString = ''; for (let i = 0; i < fileData.length; i++) { dataString += String.fromCharCode(fileData[i]); } return dataString; } function GetAesGenerateProperties() { var properties = new Array(); var index = 0; properties[index++] = { tag: huks.HuksTag.HUKS_TAG_ALGORITHM, value: huks.HuksKeyAlg.HUKS_ALG_AES }; properties[index++] = { tag: huks.HuksTag.HUKS_TAG_KEY_SIZE, value: huks.HuksKeySize.HUKS_AES_KEY_SIZE_128 }; properties[index++] = { tag: huks.HuksTag.HUKS_TAG_PURPOSE, value: huks.HuksKeyPurpose.HUKS_KEY_PURPOSE_ENCRYPT | huks.HuksKeyPurpose.HUKS_KEY_PURPOSE_DECRYPT } return properties; } function GetAesEncryptProperties() { var properties = new Array(); var index = 0; properties[index++] = { tag: huks.HuksTag.HUKS_TAG_ALGORITHM, value: huks.HuksKeyAlg.HUKS_ALG_AES }; properties[index++] = { tag: huks.HuksTag.HUKS_TAG_KEY_SIZE, value: huks.HuksKeySize.HUKS_AES_KEY_SIZE_128 }; properties[index++] = { tag: huks.HuksTag.HUKS_TAG_PURPOSE, value: huks.HuksKeyPurpose.HUKS_KEY_PURPOSE_ENCRYPT } properties[index++] = { tag: huks.HuksTag.HUKS_TAG_PADDING, value: huks.HuksKeyPadding.HUKS_PADDING_PKCS7 } properties[index++] = { tag: huks.HuksTag.HUKS_TAG_BLOCK_MODE, value: huks.HuksCipherMode.HUKS_MODE_CBC } properties[index++] = { tag: huks.HuksTag.HUKS_TAG_IV, value: StringToUint8Array(IV) } return properties; } function GetAesDecryptProperties() { var properties = new Array(); var index = 0; properties[index++] = { tag: huks.HuksTag.HUKS_TAG_ALGORITHM, value: huks.HuksKeyAlg.HUKS_ALG_AES }; properties[index++] = { tag: huks.HuksTag.HUKS_TAG_KEY_SIZE, value: huks.HuksKeySize.HUKS_AES_KEY_SIZE_128 }; properties[index++] = { tag: huks.HuksTag.HUKS_TAG_PURPOSE, value: huks.HuksKeyPurpose.HUKS_KEY_PURPOSE_DECRYPT } properties[index++] = { tag: huks.HuksTag.HUKS_TAG_PADDING, value: huks.HuksKeyPadding.HUKS_PADDING_PKCS7 } properties[index++] = { tag: huks.HuksTag.HUKS_TAG_BLOCK_MODE, value: huks.HuksCipherMode.HUKS_MODE_CBC } properties[index++] = { tag: huks.HuksTag.HUKS_TAG_IV, value: StringToUint8Array(IV) } return properties; } async function GenerateAesKey() { var genProperties = GetAesGenerateProperties(); var options = { properties: genProperties } await huks.generateKeyItem(aesKeyAlias, options).then((data) => { promptAction.showToast({ message: "An AES key is generated.", duration: 2500, }) }).catch((err)=>{ promptAction.showToast({ message: "Failed to generate the key. Error code: "+ err.code +" Error message: "+ err.message, duration: 6500, }) }) } async function EncryptData() { var encryptProperties = GetAesEncryptProperties(); var options = { properties:encryptProperties, inData: StringToUint8Array(plainText) } await huks.initSession(aesKeyAlias, options).then((data) => { handle = data.handle; }).catch((err)=>{ promptAction.showToast({ message: "Failed to initialize the key operation. Error code: "+ err.code +" Error message: "+ err.message, duration: 6500, }) }) await huks.finishSession(handle, options).then((data) => { promptAction.showToast({ message: "Data encrypted successfully. Ciphertext: " + Uint8ArrayToString(data.outData), duration: 6500, }) cipherData = data.outData }).catch((err)=>{ promptAction.showToast({ message: "An exception is captured in the encryption process. Error code: " + err.code +" Error message: "+ err.message, duration: 6500, }) }) } async function DecryptData() { var decryptOptions = GetAesDecryptProperties() var options = { properties:decryptOptions, inData: cipherData } await huks.initSession(aesKeyAlias, options).then((data) => { handle = data.handle; }).catch((err)=>{ promptAction.showToast({ message: "Failed to initialize the key operation. Error code: "+ err.code +" Error message: "+ err.message, duration: 6500, }) }) await huks.finishSession(handle, options).then((data) => { promptAction.showToast({ message: "Data is decrypted. Plaintext: " + Uint8ArrayToString(data.outData), duration: 6500, }) }).catch((err)=>{ promptAction.showToast({ message: "An exception is captured in the decryption process. Error code: " + err.code +" Error message: "+ err.message, duration: 6500, }) }) } async function DeleteKey() { let emptyOptions = { properties:[] } await huks.deleteKeyItem(aesKeyAlias, emptyOptions).then((data) => { promptAction.showToast({ message: "Key is deleted.", duration: 6500, }) }).catch((err)=>{ promptAction.showToast({ message: "Failed to delete the key. Error code: "+ err.code +" Error message: "+ err.message, duration: 6500, }) }) } @Entry @Component struct Index { @State message: string = 'Hello Huks' controller: TextInputController = new TextInputController(); build() { Column() { Row() { Text('Enter the content to be encrypted.').fontSize(20).margin({ left: 2, top: 10 }) } Row() { TextInput({placeholder: 'Encrypt 123456 by default', controller: this.controller }) .placeholderColor(Color.Grey) .placeholderFont({ size: 14, weight: 400 }) .caretColor(Color.Blue) .width(400) .height(40) .margin(20) .fontSize(14) .fontColor(Color.Black) .type(InputType.Normal) .onChange((value: string) => { This.message +='Plaintext entered: ' + value + '\n' plainText = value }) .margin({ top: 10 }) } Row() { Button({ type: ButtonType.Normal, stateEffect: true }) { Text('generateAesKey') .fontColor(Color.White) .fontSize(20) } .borderRadius(8) .width('45%') .height('5%') .backgroundColor(0x317aff) .onClick(() => { GenerateAesKey() }) .margin(10) Button({ type: ButtonType.Normal, stateEffect: true }) { Text('deleteAesKey') .fontColor(Color.White) .fontSize(20) } .borderRadius(8) .width('45%') .height('5%') .backgroundColor(0x317aff) .onClick(() => { DeleteKey() }) .margin(10) } Row() { Button({ type: ButtonType.Normal, stateEffect: true }) { Text('EncryptData') .fontColor(Color.White) .fontSize(20) } .borderRadius(8) .width('45%') .height('5%') .backgroundColor(0x317aff) .onClick(() => { EncryptData() }) .margin(10) Button({ type: ButtonType.Normal, stateEffect: true }) { Text('DecryptData') .fontColor(Color.White) .fontSize(20) } .borderRadius(8) .width('45%') .height('5%') .backgroundColor(0x317aff) .onClick(() => { DecryptData() }) .margin(10) } } } } ``` ### Key Agreement You are advised to pass in [HuksKeyStorageType](../reference/apis/js-apis-huks.md#hukskeystoragetype) to specify the storage type in key agreement. From API version 10, only **HUKS_STORAGE_ONLY_USED_IN_HUKS** or **HUKS_STORAGE_KEY_EXPORT_ALLOWED** can be used. If **HuksKeyStorageType** is not passed in, the key can be stored or exported by default, which poses security risks. ```ts /* * Perform key agreement using an X25519 256-bit TEMP key and return the result in a callback. */ import huks from '@ohos.security.huks'; /* * Set the key alias and encapsulate the key properties. */ let srcKeyAliasFirst = "AgreeX25519KeyFirstAlias"; let srcKeyAliasSecond = "AgreeX25519KeySecondAlias"; let agreeX25519InData = 'AgreeX25519TestIndata'; let finishOutData; let handle; let exportKey; let exportKeyFrist; let exportKeySecond; /* Configure the parameter set used for generating the key. */ let properties = new Array(); properties[0] = { tag: huks.HuksTag.HUKS_TAG_ALGORITHM, value: huks.HuksKeyAlg.HUKS_ALG_X25519, } properties[1] = { tag: huks.HuksTag.HUKS_TAG_PURPOSE, value: huks.HuksKeyPurpose.HUKS_KEY_PURPOSE_AGREE, } properties[2] = { tag: huks.HuksTag.HUKS_TAG_KEY_SIZE, value: huks.HuksKeySize.HUKS_CURVE25519_KEY_SIZE_256, } properties[3] = { tag: huks.HuksTag.HUKS_TAG_DIGEST, value: huks.HuksKeyDigest.HUKS_DIGEST_NONE, } properties[4] = { tag: huks.HuksTag.HUKS_TAG_PADDING, value: huks.HuksKeyPadding.HUKS_PADDING_NONE, } properties[5] = { tag: huks.HuksTag.HUKS_TAG_BLOCK_MODE, value: huks.HuksCipherMode.HUKS_MODE_CBC, } properties[6] = { tag: huks.HuksTag.HUKS_TAG_DERIVED_AGREED_KEY_STORAGE_FLAG, value: huks.HuksKeyStorageType.HUKS_STORAGE_ONLY_USED_IN_HUKS, } let HuksOptions = { properties: properties, inData: new Uint8Array(new Array()) } /* Configure parameters for the first key agreement. */ let finishProperties = new Array(); finishProperties[0] = { tag: huks.HuksTag.HUKS_TAG_DERIVED_AGREED_KEY_STORAGE_FLAG, value: huks.HuksKeyStorageType.HUKS_STORAGE_ONLY_USED_IN_HUKS, } finishProperties[1] = { tag: huks.HuksTag.HUKS_TAG_IS_KEY_ALIAS, value: true } finishProperties[2] = { tag: huks.HuksTag.HUKS_TAG_ALGORITHM, value: huks.HuksKeyAlg.HUKS_ALG_AES, } finishProperties[3] = { tag: huks.HuksTag.HUKS_TAG_KEY_SIZE, value: huks.HuksKeySize.HUKS_AES_KEY_SIZE_256, } finishProperties[4] = { tag: huks.HuksTag.HUKS_TAG_PURPOSE, value: huks.HuksKeyPurpose.HUKS_KEY_PURPOSE_ENCRYPT | huks.HuksKeyPurpose.HUKS_KEY_PURPOSE_DECRYPT, } finishProperties[5] = { tag: huks.HuksTag.HUKS_TAG_DIGEST, value: huks.HuksKeyDigest.HUKS_DIGEST_NONE, } finishProperties[6] = { tag: huks.HuksTag.HUKS_TAG_KEY_ALIAS, value: StringToUint8Array(srcKeyAliasFirst+ 'final'), } finishProperties[7] = { tag: huks.HuksTag.HUKS_TAG_PADDING, value: huks.HuksKeyPadding.HUKS_PADDING_NONE, } finishProperties[8] = { tag: huks.HuksTag.HUKS_TAG_BLOCK_MODE, value: huks.HuksCipherMode.HUKS_MODE_ECB, } let finishOptionsFrist = { properties: finishProperties, inData: StringToUint8Array(agreeX25519InData) } /* Configure parameters for the second key agreement. */ let finishOptionsSecond = { properties: finishProperties, inData: StringToUint8Array(agreeX25519InData) } finishOptionsSecond.properties.splice(6, 1, { tag: huks.HuksTag.HUKS_TAG_KEY_ALIAS, value: StringToUint8Array(srcKeyAliasSecond + 'final'), }) function StringToUint8Array(str) { let arr = []; for (let i = 0, j = str.length; i < j; ++i) { arr.push(str.charCodeAt(i)); } return new Uint8Array(arr); } function generateKeyItem(keyAlias:string, huksOptions:huks.HuksOptions, throwObject) { return new Promise((resolve, reject) => { try { huks.generateKeyItem(keyAlias, huksOptions, function (error, data) { if (error) { reject(error); } else { resolve(data); } }); } catch (error) { throwObject.isThrow = true; throw(error); } }); } async function publicGenKeyFunc(keyAlias:string, huksOptions:huks.HuksOptions) { console.info(`enter callback generateKeyItem`); let throwObject = {isThrow: false}; try { await generateKeyItem(keyAlias, huksOptions, throwObject) .then((data) => { console.info(`callback: generateKeyItem success, data = ${JSON.stringify(data)}`); }) .catch(error => { if (throwObject.isThrow) { throw(error); } else { console.error(`callback: generateKeyItem failed, code: ${error.code}, msg: ${error.message}`); } }); } catch (error) { console.error(`callback: generateKeyItem input arg invalid, code: ${error.code}, msg: ${error.message}`); } } function initSession(keyAlias:string, huksOptions:huks.HuksOptions, throwObject) : Promise { return new Promise((resolve, reject) => { try { huks.initSession(keyAlias, huksOptions, function (error, data) { if (error) { reject(error); } else { resolve(data); } }); } catch (error) { throwObject.isThrow = true; throw(error); } }); } async function publicInitFunc(keyAlias:string, huksOptions:huks.HuksOptions) { console.info(`enter callback doInit`); let throwObject = {isThrow: false}; try { await initSession(keyAlias, huksOptions, throwObject) .then ((data) => { console.info(`callback: doInit success, data = ${JSON.stringify(data)}`); handle = data.handle; }) .catch((error) => { if (throwObject.isThrow) { throw(error); } else { console.error(`callback: doInit failed, code: ${error.code}, msg: ${error.message}`); } }); } catch (error) { console.error(`callback: doInit input arg invalid, code: ${error.code}, msg: ${error.message}`); } } function updateSession(handle:number, huksOptions:huks.HuksOptions, throwObject) : Promise { return new Promise((resolve, reject) => { try { huks.updateSession(handle, huksOptions, function (error, data) { if (error) { reject(error); } else { resolve(data); } }); } catch (error) { throwObject.isThrow = true; throw(error); } }); } async function publicUpdateFunc(handle:number, huksOptions:huks.HuksOptions) { console.info(`enter callback doUpdate`); let throwObject = {isThrow: false}; try { await updateSession(handle, huksOptions, throwObject) .then ((data) => { console.info(`callback: doUpdate success, data = ${JSON.stringify(data)}`); }) .catch(error => { if (throwObject.isThrow) { throw(error); } else { console.error(`callback: doUpdate failed, code: ${error.code}, msg: ${error.message}`); } }); } catch (error) { console.error(`callback: doUpdate input arg invalid, code: ${error.code}, msg: ${error.message}`); } } function finishSession(handle:number, huksOptions:huks.HuksOptions, throwObject) : Promise { return new Promise((resolve, reject) => { try { huks.finishSession(handle, huksOptions, function (error, data) { if (error) { reject(error); } else { resolve(data); } }); } catch (error) { throwObject.isThrow = true; throw(error); } }); } async function publicFinishFunc(handle:number, huksOptions:huks.HuksOptions) { console.info(`enter callback doFinish`); let throwObject = {isThrow: false}; try { await finishSession(handle, huksOptions, throwObject) .then ((data) => { finishOutData = data.outData; console.info(`callback: doFinish success, data = ${JSON.stringify(data)}`); }) .catch(error => { if (throwObject.isThrow) { throw(error); } else { console.error(`callback: doFinish failed, code: ${error.code}, msg: ${error.message}`); } }); } catch (error) { console.error(`callback: doFinish input arg invalid, code: ${error.code}, msg: ${error.message}`); } } function exportKeyItem(keyAlias:string, huksOptions:huks.HuksOptions, throwObject) : Promise { return new Promise((resolve, reject) => { try { huks.exportKeyItem(keyAlias, huksOptions, function (error, data) { if (error) { reject(error); } else { resolve(data); } }); } catch (error) { throwObject.isThrow = true; throw(error); } }); } async function publicExportKeyFunc(keyAlias:string, huksOptions:huks.HuksOptions) { console.info(`enter callback export`); let throwObject = {isThrow: false}; try { await exportKeyItem(keyAlias, huksOptions, throwObject) .then ((data) => { console.info(`callback: exportKeyItem success, data = ${JSON.stringify(data)}`); exportKey = data.outData; }) .catch(error => { if (throwObject.isThrow) { throw(error); } else { console.error(`callback: exportKeyItem failed, code: ${error.code}, msg: ${error.message}`); } }); } catch (error) { console.error(`callback: exportKeyItem input arg invalid, code: ${error.code}, msg: ${error.message}`); } } function deleteKeyItem(keyAlias:string, huksOptions:huks.HuksOptions, throwObject) { return new Promise((resolve, reject) => { try { huks.deleteKeyItem(keyAlias, huksOptions, function (error, data) { if (error) { reject(error); } else { resolve(data); } }); } catch (error) { throwObject.isThrow = true; throw(error); } }); } async function publicDeleteKeyFunc(keyAlias:string, huksOptions:huks.HuksOptions) { console.info(`enter callback deleteKeyItem`); let throwObject = {isThrow: false}; try { await deleteKeyItem(keyAlias, huksOptions, throwObject) .then ((data) => { console.info(`callback: deleteKeyItem key success, data = ${JSON.stringify(data)}`); }) .catch(error => { if (throwObject.isThrow) { throw(error); } else { console.error(`callback: deleteKeyItem failed, code: ${error.code}, msg: ${error.message}`); } }); } catch (error) { console.error(`callback: deletKeeyItem input arg invalid, code: ${error.code}, msg: ${error.message}`); } } async function testAgree() { /* 1. Generate two keys and export them. */ await publicGenKeyFunc(srcKeyAliasFirst, HuksOptions); await publicGenKeyFunc(srcKeyAliasSecond, HuksOptions); await publicExportKeyFunc(srcKeyAliasFirst, HuksOptions); exportKeyFrist = exportKey; await publicExportKeyFunc(srcKeyAliasFirst, HuksOptions); exportKeySecond = exportKey; /* Perform key agreement for the first. */ await publicInitFunc(srcKeyAliasFirst, HuksOptions); HuksOptions.inData = exportKeySecond; await publicUpdateFunc(handle, HuksOptions); await publicFinishFunc(handle, finishOptionsFrist); /* Perform key agreement for the second key. */ await publicInitFunc(srcKeyAliasSecond, HuksOptions); HuksOptions.inData = exportKeyFrist; await publicUpdateFunc(handle, HuksOptions); await publicFinishFunc(handle, finishOptionsSecond); await publicDeleteKeyFunc(srcKeyAliasFirst, HuksOptions); await publicDeleteKeyFunc(srcKeyAliasSecond, HuksOptions); } ``` ### Key Derivation You are advised to pass in [HuksKeyStorageType](../reference/apis/js-apis-huks.md#hukskeystoragetype) to specify the storage type in key derivation. From API version 10, only **HUKS_STORAGE_ONLY_USED_IN_HUKS** or **HUKS_STORAGE_KEY_EXPORT_ALLOWED** can be used. If **HuksKeyStorageType** is not passed in, the key can be stored or exported by default, which poses security risks. ```ts /* * The following uses an HKDF256 key and promise-based APIs as an example. */ import huks from '@ohos.security.huks'; /* * Set the key alias and encapsulate the key properties. */ let srcKeyAlias = "hkdf_Key"; let deriveHkdfInData = "deriveHkdfTestIndata"; let handle; let finishOutData; let HuksKeyDeriveKeySize = 32; /* Configure the parameter set used for generating the key. */ let properties = new Array(); properties[0] = { tag: huks.HuksTag.HUKS_TAG_ALGORITHM, value: huks.HuksKeyAlg.HUKS_ALG_AES, } properties[1] = { tag: huks.HuksTag.HUKS_TAG_PURPOSE, value: huks.HuksKeyPurpose.HUKS_KEY_PURPOSE_DERIVE, } properties[2] = { tag: huks.HuksTag.HUKS_TAG_DIGEST, value: huks.HuksKeyDigest.HUKS_DIGEST_SHA256, } properties[3] = { tag: huks.HuksTag.HUKS_TAG_KEY_SIZE, value: huks.HuksKeySize.HUKS_AES_KEY_SIZE_128, } properties[4] = { tag: huks.HuksTag.HUKS_TAG_DERIVED_AGREED_KEY_STORAGE_FLAG, value: huks.HuksKeyStorageType.HUKS_STORAGE_ONLY_USED_IN_HUKS, } let huksOptions = { properties: properties, inData: new Uint8Array(new Array()) } /* Configure the parameter set used for init(). */ let initProperties = new Array(); initProperties[0] = { tag: huks.HuksTag.HUKS_TAG_ALGORITHM, value: huks.HuksKeyAlg.HUKS_ALG_HKDF, } initProperties[1] = { tag: huks.HuksTag.HUKS_TAG_PURPOSE, value: huks.HuksKeyPurpose.HUKS_KEY_PURPOSE_DERIVE, } initProperties[2] = { tag: huks.HuksTag.HUKS_TAG_DIGEST, value: huks.HuksKeyDigest.HUKS_DIGEST_SHA256, } initProperties[3] = { tag: huks.HuksTag.HUKS_TAG_DERIVE_KEY_SIZE, value: HuksKeyDeriveKeySize, } let initOptions = { properties: initProperties, inData: new Uint8Array(new Array()) } /* Configure the parameter set used for finish(). */ let finishProperties = new Array(); finishProperties[0] = { tag: huks.HuksTag.HUKS_TAG_DERIVED_AGREED_KEY_STORAGE_FLAG, value: huks.HuksKeyStorageType.HUKS_STORAGE_ONLY_USED_IN_HUKS, } finishProperties[1] = { tag: huks.HuksTag.HUKS_TAG_IS_KEY_ALIAS, value: true, } finishProperties[2] = { tag: huks.HuksTag.HUKS_TAG_ALGORITHM, value: huks.HuksKeyAlg.HUKS_ALG_AES, } finishProperties[3] = { tag: huks.HuksTag.HUKS_TAG_KEY_SIZE, value: huks.HuksKeySize.HUKS_AES_KEY_SIZE_256, } finishProperties[4] = { tag: huks.HuksTag.HUKS_TAG_PURPOSE, value: huks.HuksKeyPurpose.HUKS_KEY_PURPOSE_ENCRYPT | huks.HuksKeyPurpose.HUKS_KEY_PURPOSE_DECRYPT, } finishProperties[5] = { tag: huks.HuksTag.HUKS_TAG_DIGEST, value: huks.HuksKeyDigest.HUKS_DIGEST_NONE, } finishProperties[6] = { tag: huks.HuksTag.HUKS_TAG_KEY_ALIAS, value: StringToUint8Array(srcKeyAlias), } finishProperties[7] = { tag: huks.HuksTag.HUKS_TAG_PADDING, value: huks.HuksKeyPadding.HUKS_PADDING_NONE, } finishProperties[8] = { tag: huks.HuksTag.HUKS_TAG_BLOCK_MODE, value: huks.HuksCipherMode.HUKS_MODE_ECB, } let finishOptions = { properties: finishProperties, inData: new Uint8Array(new Array()) } function StringToUint8Array(str) { let arr = []; for (let i = 0, j = str.length; i < j; ++i) { arr.push(str.charCodeAt(i)); } return new Uint8Array(arr); } function generateKeyItem(keyAlias:string, huksOptions:huks.HuksOptions, throwObject) { return new Promise((resolve, reject) => { try { huks.generateKeyItem(keyAlias, huksOptions, function (error, data) { if (error) { reject(error); } else { resolve(data); } }); } catch (error) { throwObject.isThrow = true; throw(error); } }); } async function publicGenKeyFunc(keyAlias:string, huksOptions:huks.HuksOptions) { console.info(`enter callback generateKeyItem`); let throwObject = {isThrow: false}; try { await generateKeyItem(keyAlias, huksOptions, throwObject) .then((data) => { console.info(`callback: generateKeyItem success, data = ${JSON.stringify(data)}`); }) .catch(error => { if (throwObject.isThrow) { throw(error); } else { console.error(`callback: generateKeyItem failed, code: ${error.code}, msg: ${error.message}`); } }); } catch (error) { console.error(`callback: generateKeyItem input arg invalid, code: ${error.code}, msg: ${error.message}`); } } function initSession(keyAlias:string, huksOptions:huks.HuksOptions, throwObject) : Promise { return new Promise((resolve, reject) => { try { huks.initSession(keyAlias, huksOptions, function (error, data) { if (error) { reject(error); } else { resolve(data); } }); } catch (error) { throwObject.isThrow = true; throw(error); } }); } async function publicInitFunc(keyAlias:string, huksOptions:huks.HuksOptions) { console.info(`enter callback doInit`); let throwObject = {isThrow: false}; try { await initSession(keyAlias, huksOptions, throwObject) .then ((data) => { console.info(`callback: doInit success, data = ${JSON.stringify(data)}`); handle = data.handle; }) .catch((error) => { if (throwObject.isThrow) { throw(error); } else { console.error(`callback: doInit failed, code: ${error.code}, msg: ${error.message}`); } }); } catch (error) { console.error(`callback: doInit input arg invalid, code: ${error.code}, msg: ${error.message}`); } } function updateSession(handle:number, huksOptions:huks.HuksOptions, throwObject) : Promise { return new Promise((resolve, reject) => { try { huks.updateSession(handle, huksOptions, function (error, data) { if (error) { reject(error); } else { resolve(data); } }); } catch (error) { throwObject.isThrow = true; throw(error); } }); } async function publicUpdateFunc(handle:number, huksOptions:huks.HuksOptions) { console.info(`enter callback doUpdate`); let throwObject = {isThrow: false}; try { await updateSession(handle, huksOptions, throwObject) .then ((data) => { console.info(`callback: doUpdate success, data = ${JSON.stringify(data)}`); }) .catch(error => { if (throwObject.isThrow) { throw(error); } else { console.error(`callback: doUpdate failed, code: ${error.code}, msg: ${error.message}`); } }); } catch (error) { console.error(`callback: doUpdate input arg invalid, code: ${error.code}, msg: ${error.message}`); } } function finishSession(handle:number, huksOptions:huks.HuksOptions, throwObject) : Promise { return new Promise((resolve, reject) => { try { huks.finishSession(handle, huksOptions, function (error, data) { if (error) { reject(error); } else { resolve(data); } }); } catch (error) { throwObject.isThrow = true; throw(error); } }); } async function publicFinishFunc(handle:number, huksOptions:huks.HuksOptions) { console.info(`enter callback doFinish`); let throwObject = {isThrow: false}; try { await finishSession(handle, huksOptions, throwObject) .then ((data) => { finishOutData = data.outData; console.info(`callback: doFinish success, data = ${JSON.stringify(data)}`); }) .catch(error => { if (throwObject.isThrow) { throw(error); } else { console.error(`callback: doFinish failed, code: ${error.code}, msg: ${error.message}`); } }); } catch (error) { console.error(`callback: doFinish input arg invalid, code: ${error.code}, msg: ${error.message}`); } } function deleteKeyItem(keyAlias:string, huksOptions:huks.HuksOptions, throwObject) { return new Promise((resolve, reject) => { try { huks.deleteKeyItem(keyAlias, huksOptions, function (error, data) { if (error) { reject(error); } else { resolve(data); } }); } catch (error) { throwObject.isThrow = true; throw(error); } }); } async function publicDeleteKeyFunc(keyAlias:string, huksOptions:huks.HuksOptions) { console.info(`enter callback deleteKeyItem`); let throwObject = {isThrow: false}; try { await deleteKeyItem(keyAlias, huksOptions, throwObject) .then ((data) => { console.info(`callback: deleteKeyItem key success, data = ${JSON.stringify(data)}`); }) .catch(error => { if (throwObject.isThrow) { throw(error); } else { console.error(`callback: deleteKeyItem failed, code: ${error.code}, msg: ${error.message}`); } }); } catch (error) { console.error(`callback: deletKeeyItem input arg invalid, code: ${error.code}, msg: ${error.message}`); } } async function testDerive() { /* Generate a key. */ await publicGenKeyFunc(srcKeyAlias, huksOptions); /* Derive a key. */ await publicInitFunc(srcKeyAlias, initOptions); initOptions.inData = StringToUint8Array(deriveHkdfInData); await publicUpdateFunc(handle, initOptions); await publicFinishFunc(handle, finishOptions); await publicDeleteKeyFunc(srcKeyAlias, huksOptions); } ``` ## Key Access Control The HUKS provides comprehensive key access control to prevent unauthorized access. Services can access only their own keys, that is, the keys generated or imported through HUKS. In addition, the HUKS supports user identity authentication for security-sensitive services. Users can use the service keys only after the authentication (PIN or biometric authentication) is successful. The HUKS also restricts the key usage. For example, the AES keys can only be used for encryption and decryption, and the RSA keys can only be used for signing and signature verification. ### User Identity Authentication During the key generation or import process, user identity authentication can be enabled to allow the use of the key only after the authentication is successful. You can specify a subset of credentials (lock screen password, fingerprint, and face) for user identity authentication. After a key is generated or imported, unauthorized key access can be prevented even if the application process is attacked. Key access control applies to security-sensitive scenarios, such as password-free login, password-free payment, and automatic password filling. In addition to user identity authentication, the HUKS provides the following modes for automatically invalidating a key: - Invalidate the key when the screen lock password is cleared.
This mode takes effect only when a screen lock password has been set. If the screen lock password is cleared, the key becomes invalid permanently. The key will not be invalidated if the screen lock password is modified. This mode applies to user-related data protection or access based on screen lock passwords. - Invalidate the key when new biometric enrollments are added.
This mode takes effect only when at least one biometric feature (such as fingerprint) has been enrolled. The key becomes invalid permanently once a new biometric feature is enrolled. The key will not be invalidated if the biometric feature is deleted. This mode applies to scenarios, such as password-free login or payment. To ensure the validity of the user authentication result, the HUKS supports challenge verification. Before user identity authentication, obtain the challenge (in [HuksSessionHandle](../reference/apis/js-apis-huks.md#hukssessionhandle9) returned by [huks.initSession()](../reference/apis/js-apis-huks.md#huksinitsession9)) from the HUKS and pass in the challenge in **userIAM_userAuth.getAuthInstance**. The challenge of the authentication token is then verified during key operations. **How to Develop** If secondary user identity authentication is enabled for a key, initialize the key session and obtain the challenge. Then, pass the challenge to **userIAM_userAuth.getAuthInstance()** for user identity authentication. After the authentication is successful, an authentication token is obtained. The authentication token can be used to perform key operations. ![huks_key_user_auth_work_flow](./figures/huks_key_user_auth_work_flow.png) **Available APIs** When a key is generated or imported, [HuksUserAuthType](../reference/apis/js-apis-huks.md#huksuserauthtype9), [HuksAuthAccessType](../reference/apis/js-apis-huks.md#huksauthaccesstype9), and [HuksChallengeType](../reference/apis/js-apis-huks.md#hukschallengetype9) in the key properties are mandatory. **Table 3** User authentication types | Name | Value | Description | | ------------------------------- | ------ | ------------------------------------------------------------ | | HUKS_USER_AUTH_TYPE_FINGERPRINT | 0x0001 | Fingerprint authentication, which can be enabled with facial authentication and PIN authentication at the same time. | | HUKS_USER_AUTH_TYPE_FACE | 0x0002 | Facial authentication, whch can be enabled with fingerprint authentication and PIN authentication at the same time. | | HUKS_USER_AUTH_TYPE_PIN | 0x0004 | PIN authentication, which can be enabled with fingerprint authentication and facial authenticationat the same time. | **Table 4** Secure access types | Name | Value | Description | | --------------------------------------- | ----- | ------------------------------------------------------------ | | HUKS_AUTH_ACCESS_INVALID_CLEAR_PASSWORD | 1 | Invalidates the key after the screen lock password is cleared. | | HUKS_AUTH_ACCESS_INVALID_NEW_BIO_ENROLL | 2 | Invalidates the key after a biometric enrollment is added. The user authentication types must include the biometric authentication. | **Table 5** Challenge types | Name | Value | Description | | -------------------------- | ----- | ------------------------------------------------------------ | | HUKS_CHALLENGE_TYPE_NORMAL | 0 | Normal challenge, which requires an independent user authentication for each use of the key. | | HUKS_CHALLENGE_TYPE_CUSTOM | 1 | Custom challenge, which supports only one user authentication for multiple keys. | | HUKS_CHALLENGE_TYPE_NONE | 2 | No challenge is required during user authentication. | > **NOTICE** > > If the challenge type is **HUKS_CHALLENGE_TYPE_NONE**, no challenge is required. However, the key can be accessed within a specified time period (set by **HUKS_TAG_AUTH_TIMEOUT**) after a successful authentication. The maximum value of **HUKS_TAG_AUTH_TIMEOUT** is 60 seconds. To use a key, initialize the key session, and determine whether a challenge is required based on the challenge type specified when the key is generated or imported. **Table 6** APIs for using a key | API | Description | | -------------------------------------- | ----------------------------| |initSession(keyAlias: string, options: HuksOptions, callback: AsyncCallback\) : void| Initializes the key session and obtains the challenge.| |updateSession(handle: number, options: HuksOptions, token: Uint8Array, callback: AsyncCallback\) : void| Operates data by segment and passes the authentication token.| |finishSession(handle: number, options: HuksOptions, token: Uint8Array, callback: AsyncCallback\) : void| Finishes the key session.| **How to Develop** 1. Generate a key and specify user authentication properties. ```ts import huks from '@ohos.security.huks'; /* * Set the key alias and encapsulate the key properties. */ let keyAlias = 'dh_key_fingerprint_access'; let properties = new Array(); properties[0] = { tag: huks.HuksTag.HUKS_TAG_ALGORITHM, value: huks.HuksKeyAlg.HUKS_ALG_SM4, } properties[1] = { tag: huks.HuksTag.HUKS_TAG_PURPOSE, value: huks.HuksKeyPurpose.HUKS_KEY_PURPOSE_ENCRYPT | huks.HuksKeyPurpose.HUKS_KEY_PURPOSE_DECRYPT, } properties[2] = { tag: huks.HuksTag.HUKS_TAG_KEY_SIZE, value: huks.HuksKeySize.HUKS_SM4_KEY_SIZE_128, } properties[3] = { tag: huks.HuksTag.HUKS_TAG_BLOCK_MODE, value: huks.HuksCipherMode.HUKS_MODE_CBC, } properties[4] = { tag: huks.HuksTag.HUKS_TAG_PADDING, value: huks.HuksKeyPadding.HUKS_PADDING_NONE, } // Enable fingerprint authentication. properties[5] = { tag: huks.HuksTag.HUKS_TAG_USER_AUTH_TYPE, value: huks.HuksUserAuthType.HUKS_USER_AUTH_TYPE_FINGERPRINT } // Set the key expiration type. Invalidate the key when a new biometric feature (fingerprint) is enrolled. properties[6] = { tag: huks.HuksTag.HUKS_TAG_KEY_AUTH_ACCESS_TYPE, value: huks.HuksAuthAccessType.HUKS_AUTH_ACCESS_INVALID_NEW_BIO_ENROLL } // Use the default challenge type. properties[7] = { tag: huks.HuksTag.HUKS_TAG_CHALLENGE_TYPE, value: huks.HuksChallengeType.HUKS_CHALLENGE_TYPE_NORMAL } let huksOptions = { properties: properties, inData: new Uint8Array(new Array()) } /* * Generate a key. */ function generateKeyItem(keyAlias:string, huksOptions:huks.HuksOptions, throwObject) { return new Promise((resolve, reject) => { try { huks.generateKeyItem(keyAlias, huksOptions, function (error, data) { if (error) { reject(error); } else { resolve(data); } }); } catch (error) { throwObject.isThrow = true; throw(error); } }); } async function publicGenKeyFunc(keyAlias:string, huksOptions:huks.HuksOptions) { console.info(`enter callback generateKeyItem`); let throwObject = {isThrow: false}; try { await generateKeyItem(keyAlias, huksOptions, throwObject) .then((data) => { console.info(`callback: generateKeyItem success, data = ${JSON.stringify(data)}`); }) .catch(error => { if (throwObject.isThrow) { throw(error); } else { console.error(`callback: generateKeyItem failed, code: ${error.code}, msg: ${error.message}`); } }); } catch (error) { console.error(`callback: generateKeyItem input arg invalid, code: ${error.code}, msg: ${error.message}`); } } async function TestGenKeyForFingerprintAccessControl() { await publicGenKeyFunc(keyAlias, huksOptions); } ``` 2. Initialize the key session to obtain a challenge, and initiate fingerprint authentication to obtain an authentication token. ```ts import huks from '@ohos.security.huks'; import userIAM_userAuth from '@ohos.userIAM.userAuth'; /* * Set the key alias and encapsulate the key properties. */ let srcKeyAlias = 'sm4_key_fingerprint_access'; let handle; let challenge; let fingerAuthToken; let authType = userIAM_userAuth.UserAuthType.FINGERPRINT; let authTrustLevel = userIAM_userAuth.AuthTrustLevel.ATL1; /* Configure the key generation parameter set and key encryption parameter set. */ let properties = new Array(); properties[0] = { tag: huks.HuksTag.HUKS_TAG_ALGORITHM, value: huks.HuksKeyAlg.HUKS_ALG_SM4, } properties[1] = { tag: huks.HuksTag.HUKS_TAG_PURPOSE, value: huks.HuksKeyPurpose.HUKS_KEY_PURPOSE_ENCRYPT | huks.HuksKeyPurpose.HUKS_KEY_PURPOSE_DECRYPT, } properties[2] = { tag: huks.HuksTag.HUKS_TAG_KEY_SIZE, value: huks.HuksKeySize.HUKS_SM4_KEY_SIZE_128, } properties[3] = { tag: huks.HuksTag.HUKS_TAG_BLOCK_MODE, value: huks.HuksCipherMode.HUKS_MODE_CBC, } properties[4] = { tag: huks.HuksTag.HUKS_TAG_PADDING, value: huks.HuksKeyPadding.HUKS_PADDING_NONE, } let huksOptions = { properties: properties, inData: new Uint8Array(new Array()) } function initSession(keyAlias:string, huksOptions:huks.HuksOptions, throwObject) : Promise { return new Promise((resolve, reject) => { try { huks.initSession(keyAlias, huksOptions, function (error, data) { if (error) { reject(error); } else { resolve(data); } }); } catch (error) { throwObject.isThrow = true; throw(error); } }); } async function publicInitFunc(keyAlias:string, huksOptions:huks.HuksOptions) { console.info(`enter callback doInit`); let throwObject = {isThrow: false}; try { await initSession(keyAlias, huksOptions, throwObject) .then ((data) => { console.info(`callback: doInit success, data = ${JSON.stringify(data)}`); handle = data.handle; challenge = data.challenge; }) .catch((error) => { if (throwObject.isThrow) { throw(error); } else { console.error(`callback: doInit failed, code: ${error.code}, msg: ${error.message}`); } }); } catch (error) { console.error(`callback: doInit input arg invalid, code: ${error.code}, msg: ${error.message}`); } } function userIAMAuthFinger(huksChallenge:Uint8Array) { // Obtain an authentication object. let auth; try { auth = userIAM_userAuth.getAuthInstance(huksChallenge, authType, authTrustLevel); console.log("get auth instance success"); } catch (error) { console.log("get auth instance failed" + error); } // Subscribe to the authentication result. try { auth.on("result", { callback: (result: userIAM_userAuth.AuthResultInfo) => { /* The authentication is successful, and the authentication token is obtained. */ fingerAuthToken = result.token; } }); console.log("subscribe authentication event success"); } catch (error) { console.log("subscribe authentication event failed " + error); } // Start user authentication. try { auth.start(); console.info("authV9 start auth success"); } catch (error) { console.info("authV9 start auth failed, error = " + error); } } async function testInitAndAuthFinger() { /* Initialize the key session to obtain a challenge. */ await publicInitFunc(srcKeyAlias, huksOptions); /* Invoke userIAM to perform user identity authentication. */ userIAMAuthFinger(challenge); } ``` 3. Pass in the authentication token to perform data operations. ```ts /* * Encrypt and decrypt data using an SM4 128-bit key and return the result in a callback. */ import huks from '@ohos.security.huks'; /* * Set the key alias and encapsulate the key properties. */ let srcKeyAlias = 'sm4_key_fingerprint_access'; let IV = '1234567890123456'; let cipherInData = 'Hks_SM4_Cipher_Test_101010101010101010110_string'; let handle; let fingerAuthToken; let updateResult = new Array(); let finishOutData; /* Configure the key generation parameter set and key encryption parameter set. */ let propertiesEncrypt = new Array(); propertiesEncrypt[0] = { tag: huks.HuksTag.HUKS_TAG_ALGORITHM, value: huks.HuksKeyAlg.HUKS_ALG_SM4, } propertiesEncrypt[1] = { tag: huks.HuksTag.HUKS_TAG_PURPOSE, value: huks.HuksKeyPurpose.HUKS_KEY_PURPOSE_ENCRYPT, } propertiesEncrypt[2] = { tag: huks.HuksTag.HUKS_TAG_KEY_SIZE, value: huks.HuksKeySize.HUKS_SM4_KEY_SIZE_128, } propertiesEncrypt[3] = { tag: huks.HuksTag.HUKS_TAG_PADDING, value: huks.HuksKeyPadding.HUKS_PADDING_NONE, } propertiesEncrypt[4] = { tag: huks.HuksTag.HUKS_TAG_BLOCK_MODE, value: huks.HuksCipherMode.HUKS_MODE_CBC, } propertiesEncrypt[5] = { tag: huks.HuksTag.HUKS_TAG_IV, value: StringToUint8Array(IV), } let encryptOptions = { properties: propertiesEncrypt, inData: new Uint8Array(new Array()) } function StringToUint8Array(str) { let arr = []; for (let i = 0, j = str.length; i < j; ++i) { arr.push(str.charCodeAt(i)); } return new Uint8Array(arr); } function updateSession(handle:number, huksOptions:huks.HuksOptions, token:Uint8Array, throwObject) : Promise { return new Promise((resolve, reject) => { try { huks.updateSession(handle, huksOptions, token, function (error, data) { if (error) { reject(error); } else { resolve(data); } }); } catch (error) { throwObject.isThrow = true; throw(error); } }); } async function publicUpdateFunc(handle:number, token:Uint8Array, huksOptions:huks.HuksOptions) { console.info(`enter callback doUpdate`); let throwObject = {isThrow: false}; try { await updateSession(handle, huksOptions, token, throwObject) .then ((data) => { console.info(`callback: doUpdate success, data = ${JSON.stringify(data)}`); }) .catch(error => { if (throwObject.isThrow) { throw(error); } else { console.error(`callback: doUpdate failed, code: ${error.code}, msg: ${error.message}`); } }); } catch (error) { console.error(`callback: doUpdate input arg invalid, code: ${error.code}, msg: ${error.message}`); } } function finishSession(handle:number, huksOptions:huks.HuksOptions, token:Uint8Array, throwObject) : Promise { return new Promise((resolve, reject) => { try { huks.finishSession(handle, huksOptions, token, function (error, data) { if (error) { reject(error); } else { resolve(data); } }); } catch (error) { throwObject.isThrow = true; throw(error); } }); } async function publicFinishFunc(handle:number, token:Uint8Array, huksOptions:huks.HuksOptions) { console.info(`enter callback doFinish`); let throwObject = {isThrow: false}; try { await finishSession(handle, huksOptions, token, throwObject) .then ((data) => { finishOutData = data.outData; console.info(`callback: doFinish success, data = ${JSON.stringify(data)}`); }) .catch(error => { if (throwObject.isThrow) { throw(error); } else { console.error(`callback: doFinish failed, code: ${error.code}, msg: ${error.message}`); } }); } catch (error) { console.error(`callback: doFinish input arg invalid, code: ${error.code}, msg: ${error.message}`); } } async function testSm4Cipher() { encryptOptions.inData = StringToUint8Array(cipherInData); /* Pass in the authentication token. */ await publicUpdateFunc(handle, fingerAuthToken, encryptOptions); encryptUpdateResult = updateResult; encryptOptions.inData = new Uint8Array(new Array()); /* Pass in the authentication token. */ await publicFinishFunc(handle, fingerAuthToken, encryptOptions); if (finishOutData === cipherInData) { console.info('test finish encrypt err '); } else { console.info('test finish encrypt success'); } } ``` ### Refined User Identity Authentication As an extension to [Key Access Control](#key-access-control), the refined access control allows secondary user identity authentication (biometric authentication and lock screen password) to be performed for key access in one or more scenarios, such as encryption, decryption, signing, signature verification, key agreement, and key derivation. For example, a service needs to use a HUKS key to encrypt the account password information. In this scenario, identity authentication is not required in encryption but required in decryption. To achieve this purpose, you can use the refined user identity authentication feature provided by the HUKS. **How to Develop** 1. Specify [HUKS_TAG_KEY_AUTH_PURPOSE](../reference/apis/js-apis-huks.md#hukstag) in key generation to allow refined user identity authentication to be performed when a specific algorithm is used. 2. The **HUKS_TAG_KEY_AUTH_PURPOSE** does not need to be specified for the key usage process. The development process is the same as that of the user identity authentication process. **Available APIs** You can use the [HUKS_TAG_KEY_AUTH_PURPOSE](../reference/apis/js-apis-huks.md#hukstag) tag to specify the scenarios, for which the refined user identity authentication is performed. The value range of this tag is [HuksKeyAlg](../reference/apis/js-apis-huks.md#hukskeyalg). **Table 7** HUKS_TAG_KEY_AUTH_PURPOSE | Name | Description | | -------------------------------------- | ----------------------------| |HUKS_TAG_KEY_AUTH_PURPOSE| Purpose of the user identity authentication, that is, perform the user identity authentication when a specific algorithm is used.| **NOTICE** - If [**HuksUserAuthType**](../reference/apis/js-apis-huks.md#huksuserauthtype9) is not specified, no user identity authentication is performed by default. In this case, the setting of **HUKS_TAG_KEY_AUTH_PURPOSE** is invalid by default. If **HuksUserAuthType** is specified and **HUKS_TAG_KEY_AUTH_PURPOSE** is not specified, user identity authentication will still be performed by default before the key is used with the algorithm that is specified in the key generation process. - If the AES or SM4 symmetric algorithm is used for encryption and decryption, only the CBC mode supports refined user identity authentication. **How to Develop** Scenario: When generating keys for encryption and decryption, enable user identity authentication only for decryption. Enable user identity authentication for decryption but not for encryption. 1. Generate a key, set fingerprint authentication for key access control and related properties, and set **HUKS_TAG_KEY_AUTH_PURPOSE**. ```ts import huks from '@ohos.security.huks'; /* * Set the key alias and encapsulate the key properties. */ let keyAlias = 'dh_key_fingerprint_access'; let properties = new Array(); properties[0] = { tag: huks.HuksTag.HUKS_TAG_ALGORITHM, value: huks.HuksKeyAlg.HUKS_ALG_SM4, } properties[1] = { tag: huks.HuksTag.HUKS_TAG_PURPOSE, value: huks.HuksKeyPurpose.HUKS_KEY_PURPOSE_ENCRYPT | huks.HuksKeyPurpose.HUKS_KEY_PURPOSE_DECRYPT, } properties[2] = { tag: huks.HuksTag.HUKS_TAG_KEY_SIZE, value: huks.HuksKeySize.HUKS_SM4_KEY_SIZE_128, } properties[3] = { tag: huks.HuksTag.HUKS_TAG_BLOCK_MODE, value: huks.HuksCipherMode.HUKS_MODE_CBC, } properties[4] = { tag: huks.HuksTag.HUKS_TAG_PADDING, value: huks.HuksKeyPadding.HUKS_PADDING_NONE, } // Enable fingerprint authentication. properties[5] = { tag: huks.HuksTag.HUKS_TAG_USER_AUTH_TYPE, value: huks.HuksUserAuthType.HUKS_USER_AUTH_TYPE_FINGERPRINT } // Set the key expiration type. Invalidate the key when a new biometric feature (fingerprint) is enrolled. properties[6] = { tag: huks.HuksTag.HUKS_TAG_KEY_AUTH_ACCESS_TYPE, value: huks.HuksAuthAccessType.HUKS_AUTH_ACCESS_INVALID_NEW_BIO_ENROLL } // Use the default challenge type. properties[7] = { tag: huks.HuksTag.HUKS_TAG_CHALLENGE_TYPE, value: huks.HuksChallengeType.HUKS_CHALLENGE_TYPE_NORMAL } // Perform user identity authentication when the key is used for decryption. properties[8] = { tag: huks.HuksTag.HUKS_TAG_KEY_AUTH_PURPOSE, value: huks.HuksKeyPurpose.HUKS_KEY_PURPOSE_DECRYPT } let huksOptions = { properties: properties, inData: new Uint8Array(new Array()) } /* * Generate a key. */ async function generateKeyItem(keyAlias:string, huksOptions:huks.HuksOptions, throwObject) { return new Promise((resolve, reject) => { try { huks.generateKeyItem(keyAlias, huksOptions, function (error, data) { if (error) { reject(error); } else { resolve(data); } }); } catch (error) { throwObject.isThrow = true; throw(error); } }); } async function publicGenKeyFunc(keyAlias:string, huksOptions:huks.HuksOptions) { console.info(`enter callback generateKeyItem`); let throwObject = {isThrow: false}; try { await generateKeyItem(keyAlias, huksOptions, throwObject) .then((data) => { console.info(`callback: generateKeyItem success, data = ${JSON.stringify(data)}`); }) .catch(error => { if (throwObject.isThrow) { throw(error); } else { console.error(`callback: generateKeyItem failed, code: ${error.code}, msg: ${error.message}`); } }); } catch (error) { console.error(`callback: generateKeyItem input arg invalid, code: ${error.code}, msg: ${error.message}`); } } async function TestGenKeyForFingerprintAccessControl() { await publicGenKeyFunc(keyAlias, huksOptions); } ``` 2. Disable user identity authentication when the key is used for encryption. ```ts import huks from '@ohos.security.huks'; /* * Set the key alias and encapsulate the key properties. */ let srcKeyAlias = 'sm4_key_fingerprint_access'; let cipherInData = 'Hks_SM4_Cipher_Test_101010101010101010110_string'; // Plaintext let IV = '1234567890123456'; let handle; let cipherText; // Ciphertext after encryption. function StringToUint8Array(str) { let arr = []; for (let i = 0, j = str.length; i < j; ++i) { arr.push(str.charCodeAt(i)); } return new Uint8Array(arr); } /* Configure the key generation parameter set and key encryption parameter set. */ let propertiesEncrypt = new Array(); propertiesEncrypt[0] = { tag: huks.HuksTag.HUKS_TAG_ALGORITHM, value: huks.HuksKeyAlg.HUKS_ALG_SM4, } propertiesEncrypt[1] = { tag: huks.HuksTag.HUKS_TAG_PURPOSE, value: huks.HuksKeyPurpose.HUKS_KEY_PURPOSE_ENCRYPT, } propertiesEncrypt[2] = { tag: huks.HuksTag.HUKS_TAG_KEY_SIZE, value: huks.HuksKeySize.HUKS_SM4_KEY_SIZE_128, } propertiesEncrypt[3] = { tag: huks.HuksTag.HUKS_TAG_PADDING, value: huks.HuksKeyPadding.HUKS_PADDING_NONE, } propertiesEncrypt[4] = { tag: huks.HuksTag.HUKS_TAG_BLOCK_MODE, value: huks.HuksCipherMode.HUKS_MODE_CBC, } propertiesEncrypt[5] = { tag: huks.HuksTag.HUKS_TAG_IV, value: StringToUint8Array(IV), } let encryptOptions = { properties: propertiesEncrypt, inData: new Uint8Array(new Array()) } function initSession(keyAlias:string, huksOptions:huks.HuksOptions, throwObject) : Promise { return new Promise((resolve, reject) => { try { huks.initSession(keyAlias, huksOptions, function (error, data) { if (error) { reject(error); } else { resolve(data); } }); } catch (error) { throwObject.isThrow = true; throw(error); } }); } async function publicInitFunc(keyAlias:string, huksOptions:huks.HuksOptions) { console.info(`enter callback doInit`); let throwObject = {isThrow: false}; try { await initSession(keyAlias, huksOptions, throwObject) .then ((data) => { console.info(`callback: doInit success, data = ${JSON.stringify(data)}`); handle = data.handle; }) .catch((error) => { if (throwObject.isThrow) { throw(error); } else { console.error(`callback: doInit failed, code: ${error.code}, msg: ${error.message}`); } }); } catch (error) { console.error(`callback: doInit input arg invalid, code: ${error.code}, msg: ${error.message}`); } } function finishSession(handle:number, huksOptions:huks.HuksOptions, throwObject) : Promise { return new Promise((resolve, reject) => { try { huks.finishSession(handle, huksOptions, function (error, data) { if (error) { reject(error); } else { resolve(data); } }); } catch (error) { throwObject.isThrow = true; throw(error); } }); } async function publicFinishFunc(handle:number, huksOptions:huks.HuksOptions) { console.info(`enter callback doFinish`); let throwObject = {isThrow: false}; try { await finishSession(handle, huksOptions, throwObject) .then ((data) => { cipherText = data.outData; console.info(`callback: doFinish success, data = ${JSON.stringify(data)}`); }) .catch(error => { if (throwObject.isThrow) { throw(error); } else { console.error(`callback: doFinish failed, code: ${error.code}, msg: ${error.message}`); } }); } catch (error) { console.error(`callback: doFinish input arg invalid, code: ${error.code}, msg: ${error.message}`); } } async function testSm4Cipher() { /* Initialize the key session to obtain a challenge. */ await publicInitFunc(srcKeyAlias, encryptOptions); /** Encryption */ encryptOptions.inData = StringToUint8Array(cipherInData); await publicFinishFunc(handle, encryptOptions); } ``` 3. Enable user identity authentication when the key is used for decryption. ```ts import huks from '@ohos.security.huks'; import userIAM_userAuth from '@ohos.userIAM.userAuth'; /* * Set the key alias and encapsulate the key properties. */ let srcKeyAlias = 'sm4_key_fingerprint_access'; let cipherText = 'r56ywtTJUQC6JFJ2VV2kZw=='; // Ciphertext obtained, which may vary in actual situation. let IV = '1234567890123456'; let handle; let finishOutData; // Plaintext after decryption. let fingerAuthToken; let authType = userIAM_userAuth.UserAuthType.FINGERPRINT; let authTrustLevel = userIAM_userAuth.AuthTrustLevel.ATL1; function StringToUint8Array(str) { let arr = []; for (let i = 0, j = str.length; i < j; ++i) { arr.push(str.charCodeAt(i)); } return new Uint8Array(arr); } /* Configure the key generation parameter set and key encryption parameter set. */ let propertiesDecrypt = new Array(); propertiesDecrypt[0] = { tag: huks.HuksTag.HUKS_TAG_ALGORITHM, value: huks.HuksKeyAlg.HUKS_ALG_SM4, } propertiesDecrypt[1] = { tag: huks.HuksTag.HUKS_TAG_PURPOSE, value: huks.HuksKeyPurpose.HUKS_KEY_PURPOSE_DECRYPT, } propertiesDecrypt[2] = { tag: huks.HuksTag.HUKS_TAG_KEY_SIZE, value: huks.HuksKeySize.HUKS_SM4_KEY_SIZE_128, } propertiesDecrypt[3] = { tag: huks.HuksTag.HUKS_TAG_PADDING, value: huks.HuksKeyPadding.HUKS_PADDING_NONE, } propertiesDecrypt[4] = { tag: huks.HuksTag.HUKS_TAG_BLOCK_MODE, value: huks.HuksCipherMode.HUKS_MODE_CBC, } propertiesDecrypt[5] = { tag: huks.HuksTag.HUKS_TAG_IV, value: StringToUint8Array(IV), } let decryptOptions = { properties: propertiesDecrypt, inData: new Uint8Array(new Array()) } function initSession(keyAlias:string, huksOptions:huks.HuksOptions, throwObject) : Promise { return new Promise((resolve, reject) => { try { huks.initSession(keyAlias, huksOptions, function (error, data) { if (error) { reject(error); } else { resolve(data); } }); } catch (error) { throwObject.isThrow = true; throw(error); } }); } async function publicInitFunc(keyAlias:string, huksOptions:huks.HuksOptions) { console.info(`enter callback doInit`); let throwObject = {isThrow: false}; try { await initSession(keyAlias, huksOptions, throwObject) .then ((data) => { console.info(`callback: doInit success, data = ${JSON.stringify(data)}`); handle = data.handle; challenge = data.challenge; }) .catch((error) => { if (throwObject.isThrow) { throw(error); } else { console.error(`callback: doInit failed, code: ${error.code}, msg: ${error.message}`); } }); } catch (error) { console.error(`callback: doInit input arg invalid, code: ${error.code}, msg: ${error.message}`); } } function userIAMAuthFinger(huksChallenge:Uint8Array) { // Obtain an authentication object. let auth; try { auth = userIAM_userAuth.getAuthInstance(huksChallenge, authType, authTrustLevel); console.log("get auth instance success"); } catch (error) { console.log("get auth instance failed" + error); } // Subscribe to the authentication result. try { auth.on("result", { callback: (result: userIAM_userAuth.AuthResultInfo) => { /* The authentication is successful, and the authentication token is obtained. */ fingerAuthToken = result.token; } }); console.log("subscribe authentication event success"); } catch (error) { console.log("subscribe authentication event failed " + error); } // Start user authentication. try { auth.start(); console.info("authV9 start auth success"); } catch (error) { console.info("authV9 start auth failed, error = " + error); } } function finishSession(handle:number, huksOptions:huks.HuksOptions, token:Uint8Array, throwObject) : Promise { return new Promise((resolve, reject) => { try { huks.finishSession(handle, huksOptions, token, function (error, data) { if (error) { reject(error); } else { resolve(data); } }); } catch (error) { throwObject.isThrow = true; throw(error); } }); } async function publicFinishFunc(handle:number, token:Uint8Array, huksOptions:huks.HuksOptions) { console.info(`enter callback doFinish`); let throwObject = {isThrow: false}; try { await finishSession(handle, huksOptions, token, throwObject) .then ((data) => { finishOutData = data.outData; console.info(`callback: doFinish success, data = ${JSON.stringify(data)}`); }) .catch(error => { if (throwObject.isThrow) { throw(error); } else { console.error(`callback: doFinish failed, code: ${error.code}, msg: ${error.message}`); } }); } catch (error) { console.error(`callback: doFinish input arg invalid, code: ${error.code}, msg: ${error.message}`); } } async function testSm4Cipher() { /* Initialize the key session to obtain a challenge. */ await publicInitFunc(srcKeyAlias, decryptOptions); /* Invoke userIAM to perform user identity authentication. */ userIAMAuthFinger(challenge); /* Perform decryption after the authentication is successful. The **authToken** value obtained by Auth needs to be passed in. */ decryptOptions.inData = StringToUint8Array(cipherText); await publicFinishFunc(handle, fingerAuthToken, decryptOptions); } ``` ## Key Attestation The HUKS provides attestation for the public keys of asymmetric key pairs. The HUKS can issue a certificate for the public key of an asymmetric key pair stored in the HUKS using the public key infrastructure (PKI) certificate chain technology. The certificate can prove the validity of the public key. The service can use the root CA certificate provided by the OpenHarmony to verify the key certificate issued by the HUKS level by level to ensure that the public key and private key in the certificate are from a trusted hardware device and stored in the HUKS. **How to Develop** 1. Pass in the key alias and the property tag of the key to be attested. 2. Call a HUKS API to generate an X.509 certificate chain, which consists of the root CA certificate, device CA certificate, device certificate, and key certificate in sequence, for the application. 3. Send the certificate chain to a trusted server. The server parses and verifies the validity of the certificate chain and whether a single certificate is revoked. **Available APIs** **Table 8** API for key attestation | API | Description | | -------------------------------------- | ----------------------------| |attestKeyItem(keyAlias: string, options: HuksOptions, callback: AsyncCallback\) : void| Attests a key.| **How to Develop** ```ts /* * Attest a key and return the result in a callback. */ import huks from '@ohos.security.huks'; /* * Set the key alias and encapsulate the key properties. */ let keyAliasString = "key attest"; let aliasString = keyAliasString; let aliasUint8 = StringToUint8Array(keyAliasString); let securityLevel = StringToUint8Array('sec_level'); let challenge = StringToUint8Array('challenge_data'); let versionInfo = StringToUint8Array('version_info'); let attestCertChain; let genKeyProperties = new Array(); genKeyProperties[0] = { tag: huks.HuksTag.HUKS_TAG_ALGORITHM, value: huks.HuksKeyAlg.HUKS_ALG_RSA }; genKeyProperties[1] = { tag: huks.HuksTag.HUKS_TAG_KEY_STORAGE_FLAG, value: huks.HuksKeyStorageType.HUKS_STORAGE_PERSISTENT }; genKeyProperties[2] = { tag: huks.HuksTag.HUKS_TAG_KEY_SIZE, value: huks.HuksKeySize.HUKS_RSA_KEY_SIZE_2048 }; genKeyProperties[3] = { tag: huks.HuksTag.HUKS_TAG_PURPOSE, value: huks.HuksKeyPurpose.HUKS_KEY_PURPOSE_VERIFY }; genKeyProperties[4] = { tag: huks.HuksTag.HUKS_TAG_DIGEST, value: huks.HuksKeyDigest.HUKS_DIGEST_SHA256 }; genKeyProperties[5] = { tag: huks.HuksTag.HUKS_TAG_PADDING, value: huks.HuksKeyPadding.HUKS_PADDING_PSS }; genKeyProperties[6] = { tag: huks.HuksTag.HUKS_TAG_KEY_GENERATE_TYPE, value: huks.HuksKeyGenerateType.HUKS_KEY_GENERATE_TYPE_DEFAULT }; genKeyProperties[7] = { tag: huks.HuksTag.HUKS_TAG_BLOCK_MODE, value: huks.HuksCipherMode.HUKS_MODE_ECB }; let genOptions = { properties: genKeyProperties }; let attestKeyproperties = new Array(); attestKeyproperties[0] = { tag: huks.HuksTag.HUKS_TAG_ATTESTATION_ID_SEC_LEVEL_INFO, value: securityLevel }; attestKeyproperties[1] = { tag: huks.HuksTag.HUKS_TAG_ATTESTATION_CHALLENGE, value: challenge }; attestKeyproperties[2] = { tag: huks.HuksTag.HUKS_TAG_ATTESTATION_ID_VERSION_INFO, value: versionInfo }; attestKeyproperties[3] = { tag: huks.HuksTag.HUKS_TAG_ATTESTATION_ID_ALIAS, value: aliasUint8 }; let huksOptions = { properties: attestKeyproperties }; function StringToUint8Array(str) { let arr = []; for (let i = 0, j = str.length; i < j; ++i) { arr.push(str.charCodeAt(i)); } return new Uint8Array(arr); } function generateKeyItem(keyAlias:string, huksOptions:huks.HuksOptions, throwObject) { return new Promise((resolve, reject) => { try { huks.generateKeyItem(keyAlias, huksOptions, function (error, data) { if (error) { reject(error); } else { resolve(data); } }); } catch (error) { throwObject.isThrow = true; throw(error); } }); } async function publicGenKeyFunc(keyAlias:string, huksOptions:huks.HuksOptions) { console.info(`enter callback generateKeyItem`); let throwObject = {isThrow: false}; try { await generateKeyItem(keyAlias, huksOptions, throwObject) .then((data) => { console.info(`callback: generateKeyItem success, data = ${JSON.stringify(data)}`); }) .catch(error => { if (throwObject.isThrow) { throw(error); } else { console.error(`callback: generateKeyItem failed, code: ${error.code}, msg: ${error.message}`); } }); } catch (error) { console.error(`callback: generateKeyItem input arg invalid, code: ${error.code}, msg: ${error.message}`); } } function attestKeyItem(keyAlias:string, huksOptions:huks.HuksOptions, throwObject) : Promise{ return new Promise((resolve, reject) => { try { huks.attestKeyItem(keyAlias, huksOptions, function (error, data) { if (error) { reject(error); } else { resolve(data); } }); } catch (error) { throwObject.isThrow = true; throw(error); } }); } async function publicAttestKey(keyAlias:string, huksOptions:huks.HuksOptions) { console.info(`enter callback attestKeyItem`); let throwObject = {isThrow: false}; try { await attestKeyItem(keyAlias, huksOptions, throwObject) .then ((data) => { console.info(`callback: attestKeyItem success, data = ${JSON.stringify(data)}`); if (data !== null && data.certChains !== null) { attestCertChain = data.certChains; } }) .catch(error => { if (throwObject.isThrow) { throw(error); } else { console.error(`callback: attestKeyItem failed, code: ${error.code}, msg: ${error.message}`); } }); } catch (error) { console.error(`callback: attestKeyItem input arg invalid, code: ${error.code}, msg: ${error.message}`); } } async function AttestKeyTest() { await publicGenKeyFunc(aliasString, genOptions); await publicAttestKey(aliasString, huksOptions); console.info('attest certChain data: ' + attestCertChain) } ``` ## FAQs ### Cannot find name 'huks'. **security.huks.d.ts** is not imported. To solve the problem, add **import huks from '@ohos.security.huks'**. ### Property 'finishSession' does not exist on type 'typeof huks'. Did you mean 'finish'? **finishSession()** is supported from API version 9. Update the SDK version or use the latest **security.huks.d.ts** file.