harmony 鸿蒙HUKS Development (ArkTS)

  • 2022-08-09
  • 浏览 (662)

HUKS Development (ArkTS)

NOTE

The SDK of API version 9 or later must be used.

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) 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.

Procedure 1. Set the key alias. 2. Initialize the key property set.
Use HuksParam to encapsulate key properties and use a HuksParam array to assign values to the properties field of HuksOptions. The parameters HuksKeyAlg, HuksKeySize, and 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

/*
 * Generate a DH key using callback-based APIs.
 */
import huks from '@ohos.security.huks';
import { BusinessError } from '@ohos.base';

class HuksProperties {
    tag: huks.HuksTag = huks.HuksTag.HUKS_TAG_ALGORITHM;
    value: huks.HuksKeyAlg|huks.HuksKeySize|huks.HuksKeyPurpose|huks.HuksKeyDigest = huks.HuksKeyAlg.HUKS_ALG_ECC;
}

/*
 * Set the key alias and encapsulate the key properties.
 */
let keyAlias = 'dh_key';
let properties1: HuksProperties[] = [
    {
        tag: huks.HuksTag.HUKS_TAG_ALGORITHM,
        value: huks.HuksKeyAlg.HUKS_ALG_DH
    },
    {
        tag: huks.HuksTag.HUKS_TAG_PURPOSE,
        value: huks.HuksKeyPurpose.HUKS_KEY_PURPOSE_AGREE
    },
    {
        tag: huks.HuksTag.HUKS_TAG_KEY_SIZE,
        value: huks.HuksKeySize.HUKS_DH_KEY_SIZE_2048
    },
    {
        tag: huks.HuksTag.HUKS_TAG_DIGEST,
        value: huks.HuksKeyDigest.HUKS_DIGEST_SHA256
    }
];

let huksOptions: huks.HuksOptions = {
    properties: properties1,
    inData: new Uint8Array(new Array())
}

/*
 * Generate a key.
 */
function generateKeyItem(keyAlias: string, huksOptions: huks.HuksOptions){
    return new Promise<void>((resolve, reject) => {
        try {
            huks.generateKeyItem(keyAlias, huksOptions, (error, data) => {
                if (error) {
                    reject(error);
                } else {
                    resolve(data);
                }
            });
        } catch (error) {
            throw (error as 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: BusinessError) => {
            console.error(`callback: generateKeyItem failed`);
        });
    } catch (error) {
        console.error(`callback: generateKeyItem input arg invalid`);
    }
}

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) 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.

  1. Set the key alias.
  2. Encapsulate the key material and key property set.
    The key material must comply with HUKS key material formats. The inData value of HuksOptions must be in the Uint8Array format. Encapsulate key properties in HuksParam, and use a HuksParam array to assign values to the properties field. The key properties must contain HuksKeyAlg, HuksKeySize, and HuksKeyPurpose.
  3. Import the key.

Sample code

import huks from '@ohos.security.huks'

/*
 /* 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.
 */
class propertyType {
    tag: huks.HuksTag = huks.HuksTag.HUKS_TAG_ALGORITHM;
    value: huks.HuksKeyAlg|huks.HuksKeySize|huks.HuksKeyPurpose = huks.HuksKeyAlg.HUKS_ALG_RSA;
}

let properties: propertyType[] = [
    {
        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_256
    },
    {
        tag: huks.HuksTag.HUKS_TAG_PURPOSE,
        value: huks.HuksKeyPurpose.HUKS_KEY_PURPOSE_ENCRYPT|huks.HuksKeyPurpose.HUKS_KEY_PURPOSE_DECRYPT
    },
]

let options: huks.HuksOptions = {
    properties: properties,
    inData: plainTextSize32
};

/*
 * Import the key.
 */
try {
    huks.importKeyItem(keyAlias, options, (error, data) => {
         if (error) {
            console.error(`callback: importKeyItem failed`);
        } else {
            console.info(`callback: importKeyItem success`);
        }
    });
} catch (error) {
    console.error(`callback: importKeyItem input arg invalid`);
}

Verification

Check whether the key exists. If yes, the key is imported successfully.

Sample code

import huks from '@ohos.security.huks';

let keyAlias = 'AES256Alias_sample';
let isKeyExist = false;

class keyPropertyType {
    tag: huks.HuksTag = huks.HuksTag.HUKS_TAG_ALGORITHM;
    value: huks.HuksKeyAlg = huks.HuksKeyAlg.HUKS_ALG_RSA;
}

let keyProperties: keyPropertyType[] = [
    {
        tag: huks.HuksTag.HUKS_TAG_ALGORITHM,
        value: huks.HuksKeyAlg.HUKS_ALG_AES
    },
]

let huksOptions: huks.HuksOptions = {
    properties: keyProperties, // It cannot be empty.
    inData: new Uint8Array(new Array()) // It cannot be empty.
}
try {
    huks.isKeyItemExist(keyAlias, huksOptions, (error, data) => {
        if (error) {
            console.error(`callback: isKeyItemExist failed`);
        } 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`);
}

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

Available APIs

You need to use the APIs listed in the following table in sequence. For details about the APIs, see HUKS.

Table 1 APIs for importing a key securely

API Description
generateKeyItem(keyAlias: string, options: HuksOptions, callback: AsyncCallback<void>) : void Generates a key.
exportKeyItem(keyAlias: string, options: HuksOptions, callback: AsyncCallback<HuksReturnResult>) : void Exports the public key of a key pair.
importWrappedKeyItem(keyAlias: string, wrappingKeyAlias: string, options: HuksOptions, callback: AsyncCallback<void>) : void Imports a wrapped key.
deleteKeyItem(keyAlias: string, options: HuksOptions, callback: AsyncCallback<void>) : 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. Generate the Caller_Key and Wrapping_Key asymmetric key pairs.
  2. Export the public key material.
  3. Generate a symmetric key Caller_Kek.
  4. Perform key agreement using the public keys of Caller_Key and Wrapping_Key to obtain the Agree_Key.
  5. Use Caller_Kek to encrypt the key to be imported.
  6. Use Agree_Key to encrypt Caller_Kek.
  7. Encapsulate the key material to be imported.
  8. Import the encapsulated key material.
  9. Delete the keys that are used for secure import.

Sample code

import huks from '@ohos.security.huks';
import { BusinessError } from '@ohos.base';

let IV = '0000000000000000';
let AAD = "abababababababab";
let NONCE = "hahahahahaha";
let TAG_SIZE = 16;
let FILED_LENGTH = 4;
let importedAes192PlainKey = "The aes192 key to import";
let callerAes256Kek = "The is kek to encrypt aes192 key";

let callerKeyAlias = "test_caller_key_ecdh_aes192";
let callerKekAliasAes256 = "test_caller_kek_ecdh_aes256";
let callerAgreeKeyAliasAes256 = "test_caller_agree_key_ecdh_aes256";
let importedKeyAliasAes192 = "test_import_key_ecdh_aes192";

let huksPubKey: Uint8Array;
let callerSelfPublicKey: Uint8Array;
let outSharedKey: Uint8Array;
let outPlainKeyEncData: Uint8Array;
let outKekEncData: Uint8Array;
let outKekEncTag: Uint8Array;
let outAgreeKeyEncTag: Uint8Array;

let mask = [0x000000FF, 0x0000FF00, 0x00FF0000, 0xFF000000];
function subUint8ArrayOf(arrayBuf: Uint8Array, start: number, end: number) {
    let arr: number[] = [];
    for (let i = start; i < end && i < arrayBuf.length; ++i) {
        arr.push(arrayBuf[i]);
    }
    return new Uint8Array(arr);
}

function stringToUint8Array(str: string) {
    let arr: number[] = [];
    for (let i = 0, j = str.length; i < j; ++i) {
        arr.push(str.charCodeAt(i));
    }
    return new Uint8Array(arr);
}

function assignLength(length: number, arrayBuf: Uint8Array, startIndex: number) {
    let index = startIndex;
    for (let i = 0; i < 4; i++) {
        arrayBuf[index++] = (length & mask[i]) >> (i * 8);
    }
    return 4;
}

function assignData(data: Uint8Array, arrayBuf: Uint8Array, startIndex: number) {
    let index = startIndex;
    for (let i = 0; i < data.length; i++) {
        arrayBuf[index++] = data[i];
    }
    return data.length;
}


let genWrappingKeyParams:huks.HuksOptions = {
    properties: new Array<huks.HuksParam>(
        {
            tag: huks.HuksTag.HUKS_TAG_ALGORITHM,
            value: huks.HuksKeyAlg.HUKS_ALG_ECC
        },
        {
            tag: huks.HuksTag.HUKS_TAG_PURPOSE,
            value: huks.HuksKeyPurpose.HUKS_KEY_PURPOSE_UNWRAP
        },
        {
            tag: huks.HuksTag.HUKS_TAG_KEY_SIZE,
            value: huks.HuksKeySize.HUKS_CURVE25519_KEY_SIZE_256
        },
        {
            tag: huks.HuksTag.HUKS_TAG_PADDING,
            value: huks.HuksKeyPadding.HUKS_PADDING_NONE
        }
    )
}

let genCallerEcdhParams:huks.HuksOptions = {
    properties: new Array<huks.HuksParam>(
        {
            tag: huks.HuksTag.HUKS_TAG_ALGORITHM,
            value: huks.HuksKeyAlg.HUKS_ALG_ECC
        },
        {
            tag: huks.HuksTag.HUKS_TAG_PURPOSE,
            value: huks.HuksKeyPurpose.HUKS_KEY_PURPOSE_AGREE
        },
        {
            tag: huks.HuksTag.HUKS_TAG_KEY_SIZE,
            value: huks.HuksKeySize.HUKS_CURVE25519_KEY_SIZE_256
        }
    )
}

let importParamsCallerKek: huks.HuksOptions = {
    properties: new Array<huks.HuksParam>(
        {
            tag: huks.HuksTag.HUKS_TAG_ALGORITHM,
            value: huks.HuksKeyAlg.HUKS_ALG_AES
        },
        {
            tag: huks.HuksTag.HUKS_TAG_PURPOSE,
            value: huks.HuksKeyPurpose.HUKS_KEY_PURPOSE_ENCRYPT
        },
        {
            tag: huks.HuksTag.HUKS_TAG_KEY_SIZE,
            value: huks.HuksKeySize.HUKS_AES_KEY_SIZE_256
        },
        {
            tag: huks.HuksTag.HUKS_TAG_PADDING,
            value: huks.HuksKeyPadding.HUKS_PADDING_NONE
        },
        {
            tag: huks.HuksTag.HUKS_TAG_BLOCK_MODE,
            value: huks.HuksCipherMode.HUKS_MODE_GCM
        },
        {
            tag: huks.HuksTag.HUKS_TAG_DIGEST,
            value: huks.HuksKeyDigest.HUKS_DIGEST_NONE
        },
        {
            tag: huks.HuksTag.HUKS_TAG_IV,
            value: stringToUint8Array(IV)
        }
    ),
    inData: stringToUint8Array(callerAes256Kek)
}

let importParamsAgreeKey: huks.HuksOptions = {
    properties: new Array<huks.HuksParam>(
        {
            tag: huks.HuksTag.HUKS_TAG_ALGORITHM,
            value: huks.HuksKeyAlg.HUKS_ALG_AES
        },
        {
            tag: huks.HuksTag.HUKS_TAG_PURPOSE,
            value: huks.HuksKeyPurpose.HUKS_KEY_PURPOSE_ENCRYPT
        },
        {
            tag: huks.HuksTag.HUKS_TAG_KEY_SIZE,
            value: huks.HuksKeySize.HUKS_AES_KEY_SIZE_256
        },
        {
            tag: huks.HuksTag.HUKS_TAG_PADDING,
            value: huks.HuksKeyPadding.HUKS_PADDING_NONE
        },
        {
            tag: huks.HuksTag.HUKS_TAG_BLOCK_MODE,
            value: huks.HuksCipherMode.HUKS_MODE_GCM
        },
        {
            tag: huks.HuksTag.HUKS_TAG_DIGEST,
            value: huks.HuksKeyDigest.HUKS_DIGEST_NONE
        },
        {
            tag: huks.HuksTag.HUKS_TAG_IV,
            value: stringToUint8Array(IV)
        }
    ),
}

let callerAgreeParams: huks.HuksOptions = {
    properties: new Array<huks.HuksParam>(
        {
            tag: huks.HuksTag.HUKS_TAG_ALGORITHM,
            value: huks.HuksKeyAlg.HUKS_ALG_ECDH
        },
        {
            tag: huks.HuksTag.HUKS_TAG_PURPOSE,
            value: huks.HuksKeyPurpose.HUKS_KEY_PURPOSE_AGREE
        },
        {
            tag: huks.HuksTag.HUKS_TAG_KEY_SIZE,
            value: huks.HuksKeySize.HUKS_CURVE25519_KEY_SIZE_256
        }
    )
}

let encryptKeyCommonParams: huks.HuksOptions = {
    properties: new Array<huks.HuksParam>(
        {
            tag: huks.HuksTag.HUKS_TAG_ALGORITHM,
            value: huks.HuksKeyAlg.HUKS_ALG_AES
        },
        {
            tag: huks.HuksTag.HUKS_TAG_PURPOSE,
            value: huks.HuksKeyPurpose.HUKS_KEY_PURPOSE_ENCRYPT
        },
        {
            tag: huks.HuksTag.HUKS_TAG_KEY_SIZE,
            value: huks.HuksKeySize.HUKS_AES_KEY_SIZE_256
        },
        {
            tag: huks.HuksTag.HUKS_TAG_PADDING,
            value: huks.HuksKeyPadding.HUKS_PADDING_NONE
        },
        {
            tag: huks.HuksTag.HUKS_TAG_BLOCK_MODE,
            value: huks.HuksCipherMode.HUKS_MODE_GCM
        },
        {
            tag: huks.HuksTag.HUKS_TAG_NONCE,
            value: stringToUint8Array(NONCE)
        },
        {
            tag: huks.HuksTag.HUKS_TAG_ASSOCIATED_DATA,
            value: stringToUint8Array(AAD)
        }
    ),
}

let importWrappedAes192Params: huks.HuksOptions = {
    properties: new Array<huks.HuksParam>(
        {
            tag: huks.HuksTag.HUKS_TAG_ALGORITHM,
            value: huks.HuksKeyAlg.HUKS_ALG_AES
        },
        {
            tag: huks.HuksTag.HUKS_TAG_PURPOSE,
            value: huks.HuksKeyPurpose.HUKS_KEY_PURPOSE_ENCRYPT|
            huks.HuksKeyPurpose.HUKS_KEY_PURPOSE_DECRYPT
        },
        {
            tag: huks.HuksTag.HUKS_TAG_KEY_SIZE,
            value: huks.HuksKeySize.HUKS_AES_KEY_SIZE_192
        },
        {
            tag: huks.HuksTag.HUKS_TAG_PADDING,
            value: huks.HuksKeyPadding.HUKS_PADDING_NONE
        },
        {
            tag: huks.HuksTag.HUKS_TAG_BLOCK_MODE,
            value: huks.HuksCipherMode.HUKS_MODE_CBC
        },
        {
            tag: huks.HuksTag.HUKS_TAG_DIGEST,
            value: huks.HuksKeyDigest.HUKS_DIGEST_NONE
        },
        {
            tag: huks.HuksTag.HUKS_TAG_UNWRAP_ALGORITHM_SUITE,
            value: huks.HuksUnwrapSuite.HUKS_UNWRAP_SUITE_ECDH_AES_256_GCM_NOPADDING
        },
        {
            tag: huks.HuksTag.HUKS_TAG_IV,
            value: stringToUint8Array(IV)
        }
    )
}

async function publicGenerateItemFunc(keyAlias: string, huksOptions: huks.HuksOptions) {
    console.info(`enter promise generateKeyItem`);
    try {
        await huks.generateKeyItem(keyAlias, huksOptions)
        .then(data => {
            console.info(`promise: generateKeyItem success, data = ${JSON.stringify(data)}`);
        })
        .catch((err: BusinessError) => {
            console.error(`callback: generateKeyItem failed`);
        })
    } catch (err) {
        console.error(`callback: generateKeyItem invalid`);
    }
}

async function publicImportKeyItemFunc(keyAlias: string, HuksOptions: huks.HuksOptions) {
    console.info(`enter promise importKeyItem`);
    try {
        await huks.importKeyItem(keyAlias, HuksOptions)
        .then(data => {
            console.info(`promise: importKeyItem success, data = ${JSON.stringify(data)}`);
        }).catch((err: BusinessError) => {
            console.error(`promise: importKeyItem failed`);
        })
    } catch (err) {
        console.error(`promise: importKeyItem input arg invalid`);
    }
}

async function publicDeleteKeyItemFunc(KeyAlias: string, HuksOptions: huks.HuksOptions) {
    console.info(`enter promise deleteKeyItem`);
    try {
        await huks.deleteKeyItem(KeyAlias, HuksOptions)
        .then(data => {
            console.info(`promise: deleteKeyItem key success, data = ${JSON.stringify(data)}`);
        })
        .catch((err: BusinessError) => {
            console.error(`promise: deleteKeyItem failed`);
        })
    } catch (err) {
        console.error(`promise: deleteKeyItem input arg invalid`);
    }
}

function importWrappedKeyItem(keyAlias: string, wrappingKeyAlias: string, huksOptions: huks.HuksOptions) {
    return new Promise<void>((resolve, reject) => {
        try {
            huks.importWrappedKeyItem(keyAlias, wrappingKeyAlias, huksOptions, (error, data) => {
                if (error) {
                    reject(error);
                } else {
                    resolve(data);
                }
            });
        } catch (error) {
        }
    });
}

async function publicImportWrappedKeyFunc(keyAlias: string, wrappingKeyAlias: string, huksOptions: huks.HuksOptions) {
    console.info(`enter callback importWrappedKeyItem`);
    for (let i = 0; i < huksOptions.inData!.length; i++) {
        console.error(`${i}: ${huksOptions.inData![i]}`);
    }
    try {
        await importWrappedKeyItem(keyAlias, wrappingKeyAlias, huksOptions)
        .then((data) => {
            console.info(`callback: importWrappedKeyItem success, data = ${JSON.stringify(data)}`);
        })
        .catch((error: BusinessError) => {
            console.error(`callback: importWrappedKeyItem failed`);
        });
    } catch (error) {
        console.error(`callback: importWrappedKeyItem input arg invalid`);
    }
}

async function publicImportWrappedKeyPromise(keyAlias: string, wrappingKeyAlias: string, huksOptions: huks.HuksOptions) {
    console.info(`enter callback importWrappedKeyItem`);
    try {
        await huks.importWrappedKeyItem(keyAlias, wrappingKeyAlias, huksOptions)
        .then((data) => {
            console.info(`callback: importWrappedKeyItem success, data = ${JSON.stringify(data)}`);
        })
        .catch((error: BusinessError) => {
            console.error(`callback: importWrappedKeyItem failed`);
        });
    } catch (error) {
        console.error(`callback: importWrappedKeyItem input arg invalid`);
    }
}

async function publicInitFunc(srcKeyAlias: string, HuksOptions: huks.HuksOptions) {
    let handle: number = 0;
    console.info(`enter promise doInit`);
    try {
        await huks.initSession(srcKeyAlias, HuksOptions)
        .then((data) => {
            console.info(`promise: doInit success, data = ${JSON.stringify(data)}`);
            handle = data.handle;
        })
        .catch((error: BusinessError) => {
            console.error(`promise: doInit key failed`);
        });
    } catch (error) {
        console.error(`promise: doInit input arg invalid`);
    }
    return handle;
}

async function publicUpdateSessionFunction(handle: number, HuksOptions: huks.HuksOptions) {
    const maxUpdateSize = 64;
    const inData = HuksOptions.inData!;
    const lastInDataPosition = inData.length - 1;
    let inDataSegSize = maxUpdateSize;
    let inDataSegPosition = 0;
    let isFinished = false;
    let outData: number[] = [];
    
    while (inDataSegPosition <= lastInDataPosition) {
        if (inDataSegPosition + maxUpdateSize > lastInDataPosition) {
            isFinished = true;
            inDataSegSize = lastInDataPosition - inDataSegPosition + 1;
            console.error(`enter promise doUpdate`);
            break;
        }
        HuksOptions.inData = new Uint8Array(
           Array.from(inData).slice(inDataSegPosition, inDataSegPosition + inDataSegSize)
        );
        console.error(`enter promise doUpdate`);
        try {
            await huks.updateSession(handle, HuksOptions)
            .then((data) => {
                console.error(`promise: doUpdate success, data = ${JSON.stringify(data)}`);
                outData = outData.concat(Array.from(data.outData!));
            })
            .catch((error: BusinessError) => {
                console.error(`promise: doUpdate failed`);
            });
        } catch (error) {
            console.error(`promise: doUpdate input arg invalid`);
        }
        if ((!isFinished) && (inDataSegPosition + maxUpdateSize > lastInDataPosition)) {
            console.log(`update size invalid isFinished = ${isFinished}`);
            console.log(`inDataSegPosition = ${inDataSegPosition}`);
            console.log(`lastInDataPosition = ${lastInDataPosition}`);
            return;
        }
        inDataSegPosition += maxUpdateSize;
    }
    return outData;
}

async function publicFinishSession(handle: number, HuksOptions: huks.HuksOptions, inData: number[]) {
    let outData: number[] = [];
    console.info(`enter promise doFinish`);
    try {
        await huks.finishSession(handle, HuksOptions)
        .then((data) => {
            console.info(`promise: doFinish success, data = ${JSON.stringify(data)}`);
            outData = inData.concat(Array.from(data.outData!));
        })
        .catch((error: BusinessError) => {
            console.error(`promise: doFinish key failed`);
        });
    } catch (error) {
        console.error(`promise: doFinish input arg invalid`);
    }
    return new Uint8Array(outData);
}

async function cipherFunction(keyAlias: string, HuksOptions: huks.HuksOptions) {
    let handle = await publicInitFunc(keyAlias, HuksOptions);
    let tmpData = await publicUpdateSessionFunction(handle, HuksOptions);
    let outData = await publicFinishSession(handle, HuksOptions, tmpData!);
    return outData;
}

async function agreeFunction(keyAlias: string, HuksOptions: huks.HuksOptions, huksPublicKey: Uint8Array) {
    let handle = await publicInitFunc(keyAlias, HuksOptions);
    let outSharedKey: Uint8Array = new Uint8Array;
    HuksOptions.inData = huksPublicKey;
    console.error(`enter promise doUpdate`);
    try {
        await huks.updateSession(handle, HuksOptions)
        .then((data) => {
            console.error(`promise: doUpdate success, data = ${JSON.stringify(data)}`);
        })
        .catch((error: BusinessError) => {
            console.error(`promise: doUpdate failed`);
        });
    } catch (error) {
        console.error(`promise: doUpdate input arg invalid`);
    }
    console.info(`enter promise doInit`);
    try {
        await huks.finishSession(handle, HuksOptions)
        .then((data) => {
            console.info(`promise: doInit success, data = ${JSON.stringify(data)}`);
            outSharedKey = data.outData as Uint8Array;
        })
        .catch((error: BusinessError) => {
            console.error(`promise: doInit key failed`);
        });
    } catch (error) {
        console.error(`promise: doInit input arg invalid`);
    }
    return outSharedKey;
}

async function ImportKekAndAgreeSharedSecret(callerKekAlias: string, importKekParams: huks.HuksOptions, callerKeyAlias: string, huksPublicKey: Uint8Array, agreeParams: huks.HuksOptions) {
    await publicImportKeyItemFunc(callerKekAlias, importKekParams);
    outSharedKey = await agreeFunction(callerKeyAlias, agreeParams, huksPublicKey);

    importParamsAgreeKey.inData = outSharedKey;
    await publicImportKeyItemFunc(callerAgreeKeyAliasAes256, importParamsAgreeKey);
}

async function generateAndExportPublicKey(keyAlias: string, HuksOptions: huks.HuksOptions, caller: Boolean) {
    await publicGenerateItemFunc(keyAlias, HuksOptions);
    try {
        await huks.exportKeyItem(keyAlias, HuksOptions)
        .then((data) => {
            console.info(`promise: exportKeyItem success, data = ${JSON.stringify(data)}`);
            if (caller) {
                callerSelfPublicKey = data.outData as Uint8Array;
            } else {
                huksPubKey = data.outData as Uint8Array;
            }
        })
        .catch((error: BusinessError) => {
            console.error(`promise: exportKeyItem failed`);
        });
    } catch (e) {
        console.error(`promise: generate pubKey failed`);
    }
}

async function EncryptImportedPlainKeyAndKek(keyAlias: string) {
    encryptKeyCommonParams.inData = stringToUint8Array(keyAlias)
    let plainKeyEncData = await cipherFunction(callerKekAliasAes256, encryptKeyCommonParams);
    outKekEncTag = subUint8ArrayOf(plainKeyEncData, plainKeyEncData.length - TAG_SIZE, plainKeyEncData.length)
    outPlainKeyEncData = subUint8ArrayOf(plainKeyEncData, 0, plainKeyEncData.length - TAG_SIZE)

    encryptKeyCommonParams.inData = stringToUint8Array(callerAes256Kek)
    let kekEncData = await cipherFunction(callerAgreeKeyAliasAes256, encryptKeyCommonParams)
    outAgreeKeyEncTag = subUint8ArrayOf(kekEncData, kekEncData.length - TAG_SIZE, kekEncData.length)
    outKekEncData = subUint8ArrayOf(kekEncData, 0, kekEncData.length - TAG_SIZE)
}

async function BuildWrappedDataAndImportWrappedKey(plainKey: string) {
    let plainKeySizeBuff = new Uint8Array(4);
    assignLength(plainKey.length, plainKeySizeBuff, 0);

    let wrappedData = new Uint8Array(
        FILED_LENGTH + huksPubKey.length +
        FILED_LENGTH + AAD.length +
        FILED_LENGTH + NONCE.length +
        FILED_LENGTH + TAG_SIZE +
        FILED_LENGTH + outKekEncData.length +
        FILED_LENGTH + AAD.length +
        FILED_LENGTH + NONCE.length +
        FILED_LENGTH + TAG_SIZE +
        FILED_LENGTH + plainKeySizeBuff.length +
        FILED_LENGTH + outPlainKeyEncData.length
    );
    let index = 0;
    let AADUint8Array = stringToUint8Array(AAD);
    let NonceArray = stringToUint8Array(NONCE);

    index += assignLength(callerSelfPublicKey.length, wrappedData, index);  // 4
    index += assignData(callerSelfPublicKey, wrappedData, index); // 91
    index += assignLength(AADUint8Array.length, wrappedData, index); // 4
    index += assignData(AADUint8Array, wrappedData, index); // 16
    index += assignLength(NonceArray.length, wrappedData, index); // 4
    index += assignData(NonceArray, wrappedData, index); // 12
    index += assignLength(outAgreeKeyEncTag.length, wrappedData, index); // 4
    index += assignData(outAgreeKeyEncTag, wrappedData, index); // 16
    index += assignLength(outKekEncData.length, wrappedData, index); // 4
    index += assignData(outKekEncData, wrappedData, index); // 32
    index += assignLength(AADUint8Array.length, wrappedData, index); // 4
    index += assignData(AADUint8Array, wrappedData, index); // 16
    index += assignLength(NonceArray.length, wrappedData, index); // 4
    index += assignData(NonceArray, wrappedData, index); // 12
    index += assignLength(outKekEncTag.length, wrappedData, index); // 4
    index += assignData(outKekEncTag, wrappedData, index); // 16
    index += assignLength(plainKeySizeBuff.length, wrappedData, index); // 4
    index += assignData(plainKeySizeBuff, wrappedData, index); // 4
    index += assignLength(outPlainKeyEncData.length, wrappedData, index); // 4
    index += assignData(outPlainKeyEncData, wrappedData, index); // 24

    return wrappedData;
}

async function ImportWrappedKey() {
    const srcKeyAliesWrap = 'HUKS_Basic_Capability_Import_0200';
    /*
     * Generate Caller_Key and Wrapping_Key, which are used to agree on a key for encrypting the key to be imported, and export the public key materials.
     */
    await generateAndExportPublicKey(srcKeyAliesWrap, genWrappingKeyParams, false);
    await generateAndExportPublicKey(callerKeyAlias, genCallerEcdhParams, true);

    /**
     * Generate a symmetric key **Caller_Kek**.
     * Perform key agreement using the public keys of **Caller_Key** and **Wrapping_Key** to obtain the **Agree_Key**.
     */
    await ImportKekAndAgreeSharedSecret(callerKekAliasAes256, importParamsCallerKek, callerKeyAlias, huksPubKey, callerAgreeParams);

    /**
     * Use **Caller_Kek** to encrypt the key to be imported.
     * Use **Agree_Key** to encrypt **Caller_Kek**.
     */
    await EncryptImportedPlainKeyAndKek(importedAes192PlainKey);

    /**
     * Encapsulate the key materials to be imported.
     */
    let wrappedData = await BuildWrappedDataAndImportWrappedKey(importedAes192PlainKey);
    importWrappedAes192Params.inData = wrappedData;

    /**
     * Import the encapsulated key material.
     */
    await publicImportWrappedKeyFunc(importedKeyAliasAes192, srcKeyAliesWrap, importWrappedAes192Params);

    /**
     * Delete the key used for secure import.
     */
    await publicDeleteKeyItemFunc(srcKeyAliesWrap, genWrappingKeyParams);
    await publicDeleteKeyItemFunc(callerKeyAlias, genCallerEcdhParams);
    await publicDeleteKeyItemFunc(importedKeyAliasAes192, importWrappedAes192Params);
    await publicDeleteKeyItemFunc(callerKekAliasAes256, callerAgreeParams);
}

Verification

Check whether the key exists. If yes, the key is imported successfully.

Sample code

import huks from '@ohos.security.huks';

/*
 * Set the key alias and encapsulate the key properties.
 */
let keyAlias = 'test_import_key_ecdh_aes192';
let isKeyExist:Boolean;

let keyProperties: Array<huks.HuksParam> = new Array();
keyProperties[0] = {
    tag: huks.HuksTag.HUKS_TAG_ALGORITHM,
    value: huks.HuksKeyAlg.HUKS_ALG_AES,
}
let huksOptions:huks.HuksOptions = {
    properties: keyProperties, // It cannot be empty.
    inData: new Uint8Array(new Array()) // It cannot be empty.
}
try {
    huks.isKeyItemExist(keyAlias, huksOptions, (error, data)=> {
        if (error) {
            console.error(`callback: isKeyItemExist failed`);
        } 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`);
}

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.

General Development Process

The HUKS operates data based on key sessions. The general process is as follows:

  1. (Mandatory) Use huks.initSession() 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.
  2. (Optional) Use huks.updateSession() 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() 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() to abort the session.

Encryption and Decryption

/*
 * The following uses an AES 128-bit key and callback-based APIs as an example.
 */
import huks from '@ohos.security.huks';
import { BusinessError } from '@ohos.base';

let aesKeyAlias = 'test_aesKeyAlias';
let handle:number;
let plainText = '123456';
let IV = '001122334455';
let cipherData:Uint8Array;

function StringToUint8Array(str: String) {
    let arr:number[]=new Array();
    for (let i = 0, j = str.length; i < j; ++i) {
        arr.push(str.charCodeAt(i));
    }
    return new Uint8Array(arr);
}

function Uint8ArrayToString(fileData:Uint8Array) {
    let dataString = '';
    for (let i = 0; i < fileData.length; i++) {
        dataString += String.fromCharCode(fileData[i]);
    }
    return dataString;
}

function GetAesGenerateProperties() {
    let properties: Array<huks.HuksParam> = new Array();
    let 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() {
    let properties: Array<huks.HuksParam> = new Array();
    let 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() {
    let properties: Array<huks.HuksParam> = new Array();
    let 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() {
    let genProperties = GetAesGenerateProperties();
    let options: huks.HuksOptions = {
        properties: genProperties
    }
    await huks.generateKeyItem(aesKeyAlias, options)
    .then((data) => {
        console.info(`callback: generate AES Key success, data = ${JSON.stringify(data)}`);
    }).catch((error: BusinessError)=>{
        console.error(`callback: generate AES Key failed`);
    })
}


async function EncryptData() {
    let encryptProperties = GetAesEncryptProperties();
    let options: huks.HuksOptions = {
        properties: encryptProperties,
        inData: StringToUint8Array(plainText)
    }
    await huks.initSession(aesKeyAlias, options)
    .then((data) => {
        handle = data.handle;
    }).catch((error: BusinessError)=>{
        console.error(`callback: init encryptdata failed`);
    })
    await huks.finishSession(handle, options)
    .then((data) => {
        console.info(`callback: encrypt data success, data is `+ Uint8ArrayToString(data.outData as Uint8Array));
        cipherData = data.outData as Uint8Array;
    }).catch((error: BusinessError)=>{
        console.error(`callback: encrypt data failed`);
    })
}

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: BusinessError)=>{
        console.error(`callback: init decryptdata failed`);
    })
    await huks.finishSession(handle, options)
    .then((data) => {
        console.info(`callback: decrypt data success, data is ` + Uint8ArrayToString(data.outData as Uint8Array));
    }).catch((error: BusinessError)=>{
        console.error(`callback: decrypt data failed`);
    })
}

async function DeleteKey() {
    let emptyOptions: huks.HuksOptions = {
        properties: []
    }
    await huks.deleteKeyItem(aesKeyAlias, emptyOptions)
    .then((data) => {
        console.info(`callback: delete data success`);
    }).catch((error: BusinessError)=>{
        console.error(`callback: delete data failed`);
    })
}

Key Agreement

You are advised to pass in 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.

/*
 * Perform key agreement using an X25519 256-bit TEMP key and return the result in a callback.
 */
import huks from '@ohos.security.huks';
import { BusinessError } from '@ohos.base';

/*
 * Set the key alias and encapsulate the key properties.
 */
let srcKeyAliasFirst = "AgreeX25519KeyFirstAlias";
let srcKeyAliasSecond = "AgreeX25519KeySecondAlias";
let agreeX25519InData = 'AgreeX25519TestIndata';
let finishOutData : Uint8Array;
let handle: number;
let exportKey : Uint8Array;
let exportKeyFrist: Uint8Array;
let exportKeySecond : Uint8Array;

/* Configure the parameter set used for generating the key. */
let properties : Array<huks.HuksParam> = 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 : huks.HuksOptions = {
    properties: properties,
    inData: new Uint8Array(new Array())
}

/* Configure parameters for the first key agreement. */
let finishProperties : Array<huks.HuksParam> = 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 : huks.HuksOptions = {
    properties: finishProperties,
    inData: StringToUint8Array(agreeX25519InData)
}
/* Configure parameters for the second key agreement. */
let finishOptionsSecond : huks.HuksOptions = {
    properties: finishProperties,
    inData: StringToUint8Array(agreeX25519InData)
}
finishOptionsSecond.properties!.splice(6, 1, {
    tag: huks.HuksTag.HUKS_TAG_KEY_ALIAS,
    value: StringToUint8Array(srcKeyAliasSecond + 'final'),
})

function StringToUint8Array(str:string) {
    let arr: number[] = new Array();
    for (let i = 0, j = str.length; i < j; ++i) {
        arr.push(str.charCodeAt(i));
    }
return new Uint8Array(arr);
}
class throwObject {
    isThrow: boolean = false
}
function generateKeyItem(keyAlias:string, huksOptions:huks.HuksOptions, throwObject:throwObject) {
    return new Promise<void>((resolve, reject) => {
        try {
            huks.generateKeyItem(keyAlias, huksOptions, (error, data) => {
                if (error) {
                    reject(error);
                } else {
                    resolve(data);
                }
            });
        } catch (error) {
            throwObject.isThrow = true;
            throw(error as Error);
        }
    });
}

async function publicGenKeyFunc(keyAlias:string, huksOptions:huks.HuksOptions) {
    console.info(`enter callback generateKeyItem`);
    let throwObject :throwObject = {isThrow: false};
    try {
        await generateKeyItem(keyAlias, huksOptions, throwObject)
        .then((data) => {
            console.info(`callback: generateKeyItem success, data = ${JSON.stringify(data)}`);
        })
        .catch((error : BusinessError) => {
            if (throwObject.isThrow) {
                throw(error as Error);
            } else {
                console.error(`callback: generateKeyItem failed`);
            }
        });
    } catch (error) {
        console.error(`callback: generateKeyItem input arg invalid`);
    }
}

function initSession(keyAlias:string, huksOptions:huks.HuksOptions, throwObject:throwObject) {
    return new Promise<huks.HuksSessionHandle>((resolve, reject) => {
        try {
            huks.initSession(keyAlias, huksOptions, (error, data) => {
                if (error) {
                    reject(error);
                } else {
                    resolve(data);
                }
            });
        } catch (error) {
            throwObject.isThrow = true;
            throw(error as Error);
        }
    });
}

async function publicInitFunc(keyAlias:string, huksOptions:huks.HuksOptions) {
    console.info(`enter callback doInit`);
    let throwObject: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 : BusinessError) => {
            if (throwObject.isThrow) {
                throw(error as Error);
            } else {
                console.error(`callback: doInit failed`);
            }
        });
    } catch (error) {
        console.error(`callback: doInit input arg invalid`);
    }
}

function updateSession(handle:number, huksOptions:huks.HuksOptions, throwObject:throwObject)  {
    return new Promise<huks.HuksReturnResult>((resolve, reject) => {
        try {
            huks.updateSession(handle, huksOptions, (error, data) => {
                if (error) {
                    reject(error);
                } else {
                    resolve(data);
                }
            });
        } catch (error) {
            throwObject.isThrow = true;
            throw(error as Error);
        }
    });
}

async function publicUpdateFunc(handle:number, huksOptions:huks.HuksOptions) {
    console.info(`enter callback doUpdate`);
    let throwObject:throwObject = {isThrow: false};
    try {
        await updateSession(handle, huksOptions, throwObject)
        .then ((data) => {
            console.info(`callback: doUpdate success, data = ${JSON.stringify(data)}`);
        })
        .catch((error : BusinessError) => {
            if (throwObject.isThrow) {
                throw(error as Error);
            } else {
                console.error(`callback: doUpdate failed`);
            }
        });
    } catch (error) {
        console.error(`callback: doUpdate input arg invalid`);
    }
}

function finishSession(handle:number, huksOptions:huks.HuksOptions, throwObject:throwObject) {
    return new Promise<huks.HuksReturnResult>((resolve, reject) => {
        try {
            huks.finishSession(handle, huksOptions, (error, data) =>{
                if (error) {
                    reject(error);
                } else {
                    resolve(data);
                }
            });
        } catch (error) {
            throwObject.isThrow = true;
            throw(error as Error);
        }
    });
}

async function publicFinishFunc(handle:number, huksOptions:huks.HuksOptions) {
    console.info(`enter callback doFinish`);
    let throwObject:throwObject = {isThrow: false};
    try {
        await finishSession(handle, huksOptions, throwObject)
        .then ((data) => {
            finishOutData = data.outData as Uint8Array;
            console.info(`callback: doFinish success, data = ${JSON.stringify(data)}`);
        })
        .catch((error : BusinessError) => {
            if (throwObject.isThrow) {
                throw(error as Error);
            } else {
                console.error(`callback: doFinish failed`);
            }
        });
    } catch (error) {
        console.error(`callback: doFinish input arg invalid`);
    }
}

function exportKeyItem(keyAlias:string, huksOptions:huks.HuksOptions, throwObject:throwObject) {
    return new Promise<huks.HuksReturnResult>((resolve, reject) => {
        try {
            huks.exportKeyItem(keyAlias, huksOptions, (error, data) => {
                if (error) {
                    reject(error);
                } else {
                    resolve(data);
                }
            });
        } catch (error) {
            throwObject.isThrow = true;
            throw(error as Error);
        }
    });
}

async function publicExportKeyFunc(keyAlias:string, huksOptions:huks.HuksOptions) {
    console.info(`enter callback export`);
    let throwObject:throwObject = {isThrow: false};
    try {
        await exportKeyItem(keyAlias, huksOptions, throwObject)
        .then ((data) => {
            console.info(`callback: exportKeyItem success, data = ${JSON.stringify(data)}`);
            exportKey = data.outData as Uint8Array;
        })
        .catch((error : BusinessError) => {
            if (throwObject.isThrow) {
                throw(error as Error);
            } else {
                console.error(`callback: exportKeyItem failed`);
            }
        });
    } catch (error) {
        console.error(`callback: exportKeyItem input arg invalid`);
    }
}

function deleteKeyItem(keyAlias:string, huksOptions:huks.HuksOptions, throwObject:throwObject) {
    return new Promise<void>((resolve, reject) => {
        try {
            huks.deleteKeyItem(keyAlias, huksOptions, (error, data) => {
                if (error) {
                    reject(error);
                } else {
                    resolve(data);
                }
            });
        } catch (error) {
            throwObject.isThrow = true;
            throw(error as Error);
        }
    });
}

async function publicDeleteKeyFunc(keyAlias:string, huksOptions:huks.HuksOptions) {
    console.info(`enter callback deleteKeyItem`);
    let throwObject : throwObject = {isThrow: false};
    try {
        await deleteKeyItem(keyAlias, huksOptions, throwObject)
        .then ((data) => {
           console.info(`callback: deleteKeyItem key success, data = ${JSON.stringify(data)}`);
        })
        .catch((error :BusinessError) => {
            if (throwObject.isThrow) {
                throw(error as Error);
            } else {
                console.error(`callback: deleteKeyItem failed`);
            }
        });
    } catch (error) {
        console.error(`callback: deletKeeyItem input arg invalid`);
    }
}

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 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.

/*
 * The following uses an HKDF256 key and promise-based APIs as an example.
 */
import huks from '@ohos.security.huks';
import { BusinessError } from '@ohos.base';

/*
 * Set the key alias and encapsulate the key properties.
 */
let srcKeyAlias = "hkdf_Key";
let deriveHkdfInData = "deriveHkdfTestIndata";
let handle:number;
let finishOutData:Uint8Array;
let HuksKeyDeriveKeySize = 32;

/* Configure the parameter set used for generating the key. */
let properties:Array<huks.HuksParam> = 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:huks.HuksOptions = {
    properties: properties,
    inData: new Uint8Array(new Array())
}

/* Configure the parameter set used for init(). */
let initProperties:Array<huks.HuksParam> = 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:huks.HuksOptions = {
    properties: initProperties,
    inData: new Uint8Array(new Array())
}

/* Configure the parameter set used for finish(). */
let finishProperties:Array<huks.HuksParam> = 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:huks.HuksOptions = {
    properties: finishProperties,
    inData: new Uint8Array(new Array())
}

function StringToUint8Array(str:String) {
    let arr:number[]=new Array();
    for (let i = 0, j = str.length; i < j; ++i) {
        arr.push(str.charCodeAt(i));
    }
    return new Uint8Array(arr);
}

class throwObject{
    isThrow=false;
}

function generateKeyItem(keyAlias:string, huksOptions:huks.HuksOptions, throwObject:throwObject) {
    return new Promise<void>((resolve, reject) => {
        try {
            huks.generateKeyItem(keyAlias, huksOptions, (error, data)=> {
                if (error) {
                    reject(error);
                } else {
                    resolve(data);
                }
            });
        } catch (error) {
            throwObject.isThrow = true;
            throw(error as Error);
        }
    });
}

async function publicGenKeyFunc(keyAlias:string, huksOptions:huks.HuksOptions) {
    console.info(`enter callback generateKeyItem`);
    let throwObject:throwObject = {isThrow: false};
    try {
        await generateKeyItem(keyAlias, huksOptions, throwObject)
        .then((data) => {
            console.info(`callback: generateKeyItem success, data = ${JSON.stringify(data)}`);
        })
        .catch((error:BusinessError) => {
            if (throwObject.isThrow) {
                throw(error as Error);
            } else {
                console.error(`callback: generateKeyItem failed`);
            }
        });
    } catch (error) {
        console.error(`callback: generateKeyItem input arg invalid`);
    }
}

function initSession(keyAlias:string, huksOptions:huks.HuksOptions, throwObject:throwObject) {
    return new Promise<huks.HuksSessionHandle>((resolve, reject) => {
        try {
            huks.initSession(keyAlias, huksOptions, (error, data)=> {
                if (error) {
                    reject(error);
                } else {
                    resolve(data);
                }
            });
        } catch (error) {
            throwObject.isThrow = true;
            throw(error as Error);
        }
    });
}

async function publicInitFunc(keyAlias:string, huksOptions:huks.HuksOptions) {
    console.info(`enter callback doInit`);
    let throwObject: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:BusinessError) => {
            if (throwObject.isThrow) {
                throw(error as Error);
            } else {
                console.error(`callback: doInit failed`);
            }
        });
    } catch (error) {
        console.error(`callback: doInit input arg invalid`);
    }
}

function updateSession(handle:number, huksOptions:huks.HuksOptions, throwObject:throwObject) {
    return new Promise<huks.HuksOptions>((resolve, reject) => {
        try {
            huks.updateSession(handle, huksOptions, (error, data)=> {
                if (error) {
                    reject(error);
                } else {
                    resolve(data);
                }
            });
        } catch (error) {
            throwObject.isThrow = true;
            throw(error as Error);
        }
    });
}

async function publicUpdateFunc(handle:number, huksOptions:huks.HuksOptions) {
    console.info(`enter callback doUpdate`);
    let throwObject:throwObject = {isThrow: false};
    try {
        await updateSession(handle, huksOptions, throwObject)
        .then ((data) => {
            console.info(`callback: doUpdate success, data = ${JSON.stringify(data)}`);
        })
        .catch((error:BusinessError) => {
            if (throwObject.isThrow) {
                throw(error as Error);
            } else {
                console.error(`callback: doUpdate failed`);
            }
        });
    } catch (error) {
        console.error(`callback: doUpdate input arg invalid`);
    }
}

function finishSession(handle:number, huksOptions:huks.HuksOptions, throwObject:throwObject) {
    return new Promise<huks.HuksReturnResult>((resolve, reject) => {
        try {
            huks.finishSession(handle, huksOptions, (error, data)=> {
                if (error) {
                    reject(error);
                } else {
                    resolve(data);
                }
            });
        } catch (error) {
            throwObject.isThrow = true;
            throw(error as Error);
        }
    });
}

async function publicFinishFunc(handle:number, huksOptions:huks.HuksOptions) {
    console.info(`enter callback doFinish`);
    let throwObject:throwObject = {isThrow: false};
    try {
        await finishSession(handle, huksOptions, throwObject)
        .then ((data) => {
            finishOutData = data.outData as Uint8Array;
            console.info(`callback: doFinish success, data = ${JSON.stringify(data)}`);
        })
        .catch((error:BusinessError) => {
            if (throwObject.isThrow) {
                throw(error as Error);
            } else {
                console.error(`callback: doFinish failed`);
            }
        });
    } catch (error) {
        console.error(`callback: doFinish input arg invalid`);
    }
}

function deleteKeyItem(keyAlias:string, huksOptions:huks.HuksOptions, throwObject:throwObject) {
    return new Promise<void>((resolve, reject) => {
        try {
            huks.deleteKeyItem(keyAlias, huksOptions, (error, data)=> {
                if (error) {
                    reject(error);
                } else {
                    resolve(data);
                }
            });
        } catch (error) {
            throwObject.isThrow = true;
            throw(error as Error);
        }
    });
}

async function publicDeleteKeyFunc(keyAlias:string, huksOptions:huks.HuksOptions) {
    console.info(`enter callback deleteKeyItem`);
    let throwObject:throwObject = {isThrow: false};
    try {
        await deleteKeyItem(keyAlias, huksOptions, throwObject)
        .then ((data) => {
            console.info(`callback: deleteKeyItem key success, data = ${JSON.stringify(data)}`);
        })
        .catch((error:BusinessError) => {
            if (throwObject.isThrow) {
                throw(error as Error);
            } else {
                console.error(`callback: deleteKeyItem failed`);
            }
        });
    } catch (error) {
        console.error(`callback: deletKeeyItem input arg invalid`);
    }
}

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 returned by huks.initSession()) 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

Available APIs

When a key is generated or imported, HuksUserAuthType, HuksAuthAccessType, and HuksChallengeType 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<HuksSessionHandle>) : void|Initializes the key session and obtains the challenge.| |updateSession(handle: number, options: HuksOptions, token: Uint8Array, callback: AsyncCallback<HuksReturnResult>) : void|Operates data by segment and passes the authentication token.| |finishSession(handle: number, options: HuksOptions, token: Uint8Array, callback: AsyncCallback<HuksReturnResult>) : void|Finishes the key session.|

How to Develop

  1. Generate a key and specify user authentication properties.
   import huks from '@ohos.security.huks';
   import { BusinessError } from '@ohos.base';

   /*
    * Set the key alias and encapsulate the key properties.
    */
   let keyAlias = 'dh_key_fingerprint_access';
   let properties : Array<huks.HuksParam> = 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 : huks.HuksOptions = {
       properties: properties,
       inData: new Uint8Array(new Array())
   }

   /*
    * Generate a key.
    */
   class throwObject {
       isThrow:boolean = false
   }
   function generateKeyItem(keyAlias : string, huksOptions:huks.HuksOptions, throwObject:throwObject) {
       return new Promise<void>((resolve, reject) => {
           try {
               huks.generateKeyItem(keyAlias, huksOptions, (error, data) => {
                   if (error) {
                       reject(error);
                   } else {
                       resolve(data);
                   }
               });
           } catch (error) {
               throwObject.isThrow = true;
               throw(error as Error);
           }
       });
   }

   async function publicGenKeyFunc(keyAlias:string, huksOptions:huks.HuksOptions) {
       console.info(`enter callback generateKeyItem`);
       let throwObject : throwObject = {isThrow: false};
       try {
           await generateKeyItem(keyAlias, huksOptions, throwObject)
           .then((data) => {
               console.info(`callback: generateKeyItem success, data = ${JSON.stringify(data)}`);
           })
           .catch((error : BusinessError) => {
               if (throwObject.isThrow) {
                   throw(error as Error);
               } else {
                   console.error(`callback: generateKeyItem failed`);
               }
           });
       } catch (error) {
           console.error(`callback: generateKeyItem input arg invalid`);
       }
   }

   async function TestGenKeyForFingerprintAccessControl() {
       await publicGenKeyFunc(keyAlias, huksOptions);
   }
  1. Initialize the key session to obtain a challenge, and initiate fingerprint authentication to obtain an authentication token.
   import huks from '@ohos.security.huks';
   import userIAM_userAuth from '@ohos.userIAM.userAuth';
   import { BusinessError } from '@ohos.base';

   /*
    * Set the key alias and encapsulate the key properties.
    */
   let srcKeyAlias = 'sm4_key_fingerprint_access';
   let handle : number;
   let challenge : Uint8Array;
   let fingerAuthToken : Uint8Array;
   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 : Array<huks.HuksParam> = 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 : huks.HuksOptions = {
       properties: properties,
       inData: new Uint8Array(new Array())
   }

   class throwObject {
       isThrow:boolean=false
   }

   function initSession(keyAlias:string, huksOptions:huks.HuksOptions, throwObject:throwObject) {
       return new Promise<huks.HuksSessionHandle>((resolve, reject) => {
           try {
               huks.initSession(keyAlias, huksOptions, (error, data) =>{
                   if (error) {
                       reject(error);
                   } else {
                       resolve(data);
                   }
               });
           } catch (error) {
               throwObject.isThrow = true;
               throw(error as Error);
           }
       });
   }

   async function publicInitFunc(keyAlias:string, huksOptions:huks.HuksOptions) {
       console.info(`enter callback doInit`);
       let throwObject : 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 as Uint8Array;
           })
           .catch((error : BusinessError) => {
               if (throwObject.isThrow) {
                   throw(error as Error);
               } else {
                   console.error(`callback: doInit failed`);
               }
           });
       } catch (error) {
           console.error(`callback: doInit input arg invalid`);
       }
   }

   function userIAMAuthFinger(huksChallenge:Uint8Array) {
       // Obtain an authentication object.
       let auth : userIAM_userAuth.AuthInstance;
       try {
           auth = userIAM_userAuth.getAuthInstance(huksChallenge, authType, authTrustLevel);
           console.log("get auth instance success");
       } catch (error) {
           console.log("get auth instance failed" + error);
           return;
       }

       // 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 as Uint8Array;
           }
       });
           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);
   }
  1. Pass in the authentication token to perform data operations.
   /*
   * The following uses an SM4 128-bit key and callback-based APIs as an example.
   */
   import huks from '@ohos.security.huks';
   import { BusinessError } from '@ohos.base';
   
   /*
   * 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: number;
   let fingerAuthToken: Uint8Array;
   let finishOutData: Uint8Array;
   
   class throwObject {
       isThrow: boolean = false;
   }
   
   /* Configure the key generation parameter set and key encryption parameter set. */
   class propertyEncryptType {
       tag: huks.HuksTag = huks.HuksTag.HUKS_TAG_ALGORITHM;
       value: huks.HuksKeyAlg|huks.HuksKeyPurpose|huks.HuksKeySize|huks.HuksKeyPadding|huks.HuksCipherMode
         |Uint8Array = huks.HuksKeyAlg.HUKS_ALG_SM4;
   }
   
   let propertiesEncrypt: propertyEncryptType[] = [
       {
           tag: huks.HuksTag.HUKS_TAG_ALGORITHM,
           value: huks.HuksKeyAlg.HUKS_ALG_SM4,
       },
       {
           tag: huks.HuksTag.HUKS_TAG_PURPOSE,
           value: huks.HuksKeyPurpose.HUKS_KEY_PURPOSE_ENCRYPT,
       },
       {
           tag: huks.HuksTag.HUKS_TAG_KEY_SIZE,
           value: huks.HuksKeySize.HUKS_SM4_KEY_SIZE_128,
       },
       {
           tag: huks.HuksTag.HUKS_TAG_PADDING,
           value: huks.HuksKeyPadding.HUKS_PADDING_NONE,
       },
       {
           tag: huks.HuksTag.HUKS_TAG_BLOCK_MODE,
           value: huks.HuksCipherMode.HUKS_MODE_CBC,
       },
       {
           tag: huks.HuksTag.HUKS_TAG_IV,
           value: StringToUint8Array(IV),
       }
   ]
   
   let encryptOptions: huks.HuksOptions = {
       properties: propertiesEncrypt,
       inData: new Uint8Array(new Array())
   }
   
   function StringToUint8Array(str: string) {
       let arr: number[] = [];
       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: throwObject) {
       return new Promise<huks.HuksReturnResult>((resolve, reject) => {
           try {
               huks.updateSession(handle, huksOptions, token, (error, data) => {
                   if (error) {
                       reject(error);
                   } else {
                       resolve(data);
                   }
               });
           } catch (error) {
               throwObject.isThrow = true;
               throw(error as Error);
           }
       });
   }
   
   async function publicUpdateFunc(handle: number, token: Uint8Array, huksOptions: huks.HuksOptions) {
       console.info(`enter callback doUpdate`);
       let throwObject: throwObject = {isThrow: false};
       try {
           await updateSession(handle, huksOptions, token, throwObject)
           .then ((data) => {
               console.info(`callback: doUpdate success, data = ${JSON.stringify(data)}`);
           })
           .catch((error: BusinessError) => {
               if (throwObject.isThrow) {
                   throw(error as Error);
               } else {
                   console.error(`callback: doUpdate failed`);
               }
           });
       } catch (error) {
           console.error(`callback: doUpdate input arg invalid`);
       }
   }
   
   function finishSession(handle: number, huksOptions: huks.HuksOptions, token: Uint8Array, throwObject: throwObject) {
       return new Promise<huks.HuksReturnResult>((resolve, reject) => {
           try {
               huks.finishSession(handle, huksOptions, token, (error, data) => {
                   if (error) {
                       reject(error);
                   } else {
                       resolve(data);
                   }
               });
           } catch (error) {
               throwObject.isThrow = true;
               throw(error as Error);
           }
       });
   }
   
   async function publicFinishFunc(handle: number, token: Uint8Array, huksOptions: huks.HuksOptions) {
       console.info(`enter callback doFinish`);
       let throwObject: throwObject = {isThrow: false};
       try {
           await finishSession(handle, huksOptions, token, throwObject)
           .then ((data) => {
               finishOutData = data.outData as Uint8Array;
               console.info(`callback: doFinish success, data = ${JSON.stringify(data)}`);
           })
           .catch((error: BusinessError) => {
               if (throwObject.isThrow) {
                   throw(error as Error);
               } else {
                   console.error(`callback: doFinish failed`);
               }
           });
       } catch (error) {
           console.error(`callback: doFinish input arg invalid`);
       }
   }
   
   async function testSm4Cipher() {
       encryptOptions.inData = StringToUint8Array(cipherInData);
       /* Pass in the authentication token. */
       await publicUpdateFunc(handle, fingerAuthToken, encryptOptions);
       encryptOptions.inData = new Uint8Array(new Array());
       /* Pass in the authentication token. */
       await publicFinishFunc(handle, fingerAuthToken, encryptOptions);
       if (finishOutData === StringToUint8Array(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, 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 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 tag to specify the scenarios, for which the refined user identity authentication is performed. The value range of this tag is 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 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.
   import huks from '@ohos.security.huks';
   import { BusinessError } from '@ohos.base';

   /*
    * Set the key alias and encapsulate the key properties.
    */
   let keyAlias = 'dh_key_fingerprint_access';

   class throwObject {
       isThrow: boolean = false;
   }

   class propertyType {
       tag: huks.HuksTag = huks.HuksTag.HUKS_TAG_ALGORITHM;
       value: huks.HuksKeyAlg|huks.HuksKeyPurpose|huks.HuksKeySize|huks.HuksCipherMode|huks.HuksKeyPadding
         |huks.HuksUserAuthType|huks.HuksAuthAccessType|huks.HuksChallengeType = huks.HuksKeyAlg.HUKS_ALG_SM4
   }
   let properties: propertyType[] = [
       {
           tag: huks.HuksTag.HUKS_TAG_ALGORITHM,
           value: huks.HuksKeyAlg.HUKS_ALG_SM4,
       },
       {
           tag: huks.HuksTag.HUKS_TAG_PURPOSE,
           value: huks.HuksKeyPurpose.HUKS_KEY_PURPOSE_ENCRYPT|huks.HuksKeyPurpose.HUKS_KEY_PURPOSE_DECRYPT,
       },
       {
           tag: huks.HuksTag.HUKS_TAG_KEY_SIZE,
           value: huks.HuksKeySize.HUKS_SM4_KEY_SIZE_128,
       },
       {
           tag: huks.HuksTag.HUKS_TAG_BLOCK_MODE,
           value: huks.HuksCipherMode.HUKS_MODE_CBC,
       },
       {
           tag: huks.HuksTag.HUKS_TAG_PADDING,
           value: huks.HuksKeyPadding.HUKS_PADDING_NONE,
       },
       {
           tag: huks.HuksTag.HUKS_TAG_USER_AUTH_TYPE,
           value: huks.HuksUserAuthType.HUKS_USER_AUTH_TYPE_FINGERPRINT
       },
       {
           tag: huks.HuksTag.HUKS_TAG_KEY_AUTH_ACCESS_TYPE,
           value: huks.HuksAuthAccessType.HUKS_AUTH_ACCESS_INVALID_NEW_BIO_ENROLL
       },
       {
           tag: huks.HuksTag.HUKS_TAG_CHALLENGE_TYPE,
           value: huks.HuksChallengeType.HUKS_CHALLENGE_TYPE_NORMAL
       },
       {
           tag: huks.HuksTag.HUKS_TAG_KEY_AUTH_PURPOSE,
           value: huks.HuksKeyPurpose.HUKS_KEY_PURPOSE_DECRYPT
       }
   ]

   let huksOptions: huks.HuksOptions = {
       properties: properties,
       inData: new Uint8Array(new Array())
   }

   /*
    * Generate a key.
    */
   async function generateKeyItem(keyAlias: string, huksOptions: huks.HuksOptions, throwObject: throwObject) {
       return new Promise<void>((resolve, reject) => {
           try {
               huks.generateKeyItem(keyAlias, huksOptions, (error, data) => {
                   if (error) {
                       reject(error);
                   } else {
                       resolve(data);
                   }
               });
           } catch (error) {
               throwObject.isThrow = true;
               throw(error as Error);
           }
       });
   }

   async function publicGenKeyFunc(keyAlias: string, huksOptions: huks.HuksOptions) {
       console.info(`enter callback generateKeyItem`);
       let throwObject: throwObject = {isThrow: false};
       try {
           await generateKeyItem(keyAlias, huksOptions, throwObject)
           .then((data) => {
               console.info(`callback: generateKeyItem success, data = ${JSON.stringify(data)}`);
           })
           .catch((error: BusinessError) => {
               if (throwObject.isThrow) {
                   throw(error as Error);
               } else {
                   console.error(`callback: generateKeyItem failed`);
               }
           });
       } catch (error) {
           console.error(`callback: generateKeyItem input arg invalid`);
       }
   }

   async function TestGenKeyForFingerprintAccessControl() {
       await publicGenKeyFunc(keyAlias, huksOptions);
   }
  1. Disable user identity authentication when the key is used for encryption.
   import huks from '@ohos.security.huks';
   import { BusinessError } from '@ohos.base';

   class HuksProperties {
       tag: huks.HuksTag = huks.HuksTag.HUKS_TAG_ALGORITHM;
       value: huks.HuksKeyAlg|huks.HuksKeySize|huks.HuksKeyPurpose|huks.HuksKeyPadding|huks.HuksCipherMode
         |Uint8Array = huks.HuksKeyAlg.HUKS_ALG_ECC;
   }

   /*
    * 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 = 0;
   let cipherText: Uint8Array; // Ciphertext data after encryption.

   function StringToUint8Array(str: string) {
       let arr: number[] = [];
       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: HuksProperties[] = [
       {
           tag: huks.HuksTag.HUKS_TAG_ALGORITHM,
           value: huks.HuksKeyAlg.HUKS_ALG_SM4,
       },
       {
           tag: huks.HuksTag.HUKS_TAG_PURPOSE,
           value: huks.HuksKeyPurpose.HUKS_KEY_PURPOSE_ENCRYPT,
       },
       {
           tag: huks.HuksTag.HUKS_TAG_KEY_SIZE,
           value: huks.HuksKeySize.HUKS_SM4_KEY_SIZE_128,
       },
       {
           tag: huks.HuksTag.HUKS_TAG_PADDING,
           value: huks.HuksKeyPadding.HUKS_PADDING_NONE,
       },
       {
           tag: huks.HuksTag.HUKS_TAG_BLOCK_MODE,
           value: huks.HuksCipherMode.HUKS_MODE_CBC,
       },
       {
           tag: huks.HuksTag.HUKS_TAG_IV,
           value: StringToUint8Array(IV),
       }
   ];
   let encryptOptions: huks.HuksOptions = {
       properties: propertiesEncrypt,
       inData: new Uint8Array(new Array())
   }
   class throwObject1{
       isThrow: boolean = false;
   }
   function initSession(keyAlias: string, huksOptions: huks.HuksOptions, throwObject: throwObject1) {
       return new Promise<huks.HuksSessionHandle>((resolve, reject) => {
           try {
               huks.initSession(keyAlias, huksOptions, (error, data) => {
                   if (error) {
                       reject(error);
                   } else {
                       resolve(data);
                   }
               });
           } catch (error) {
               throwObject.isThrow = true;
               throw (error as Error);
           }
       });
   }

   async function publicInitFunc(keyAlias: string, huksOptions: huks.HuksOptions) {
       console.info(`enter callback doInit`);
       let throwObject: throwObject1 = { isThrow: false };
       try {
           await initSession(keyAlias, huksOptions, throwObject)
           .then((data) => {
               console.info(`callback: doInit success, data = ${JSON.stringify(data)}`);
               handle = data.handle as number;
           })
           .catch((error: BusinessError) => {
               if (throwObject.isThrow) {
                   throw (error as Error);
               } else {
                   console.error(`callback: doInit failed`);
               }
           });
       } catch (error) {
           console.error(`callback: doInit input arg invalid`);
       }
   }

   function finishSession(handle: number, huksOptions: huks.HuksOptions, throwObject: throwObject1) {
       return new Promise<huks.HuksReturnResult>((resolve, reject) => {
           try {
               huks.finishSession(handle, huksOptions, (error, data) => {
                   if (error) {
                       reject(error);
                   } else {
                       resolve(data);
                   }
               });
           } catch (error) {
               throwObject.isThrow = true;
               throw (error as Error);
           }
       });
   }

   async function publicFinishFunc(handle: number, huksOptions: huks.HuksOptions) {
       console.info(`enter callback doFinish`);
       let throwObject: throwObject1 = { isThrow: false };
       try {
           await finishSession(handle, huksOptions, throwObject)
           .then((data) => {
               cipherText = data.outData as Uint8Array;
               console.info(`callback: doFinish success, data = ${JSON.stringify(data)}`);
           })
           .catch((error: BusinessError) => {
               if (throwObject.isThrow) {
                   throw (error as Error);
               } else {
                   console.error(`callback: doFinish failed`);
               }
           });
       } catch (error) {
           console.error(`callback: doFinish input arg invalid`);
       }
   }

   async function testSm4Cipher() {
       /* Initialize the key session to obtain a challenge. */
       await publicInitFunc(srcKeyAlias, encryptOptions);
       
       /** Encryption */
       encryptOptions.inData = StringToUint8Array(cipherInData);
       await publicFinishFunc(handle, encryptOptions);
   }
  1. Enable user identity authentication when the key is used for decryption.
   import huks from '@ohos.security.huks';
   import userIAM_userAuth from '@ohos.userIAM.userAuth';
   import { BusinessError } from '@ohos.base';
   
   /*
    * 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: number;
   let finishOutData: Uint8Array; // Plaintext data after decryption.
   let fingerAuthToken: Uint8Array;
   let challenge: Uint8Array;
   let authType = userIAM_userAuth.UserAuthType.FINGERPRINT;
   let authTrustLevel = userIAM_userAuth.AuthTrustLevel.ATL1;
   
   class throwObject {
       isThrow: boolean = false;
   }
   
   function StringToUint8Array(str: string) {
       let arr: number[] = [];
       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. */
   class propertyDecryptType {
       tag: huks.HuksTag = huks.HuksTag.HUKS_TAG_ALGORITHM
       value: huks.HuksKeyAlg|huks.HuksKeyPurpose|huks.HuksKeySize|huks.HuksKeyPadding|huks.HuksCipherMode
         |Uint8Array = huks.HuksKeyAlg.HUKS_ALG_SM4
   }
   
   let propertiesDecrypt: propertyDecryptType[] = [
       {
           tag: huks.HuksTag.HUKS_TAG_ALGORITHM,
           value: huks.HuksKeyAlg.HUKS_ALG_SM4,
       },
       {
           tag: huks.HuksTag.HUKS_TAG_PURPOSE,
           value: huks.HuksKeyPurpose.HUKS_KEY_PURPOSE_DECRYPT,
       },
       {
           tag: huks.HuksTag.HUKS_TAG_KEY_SIZE,
           value: huks.HuksKeySize.HUKS_SM4_KEY_SIZE_128,
       },
       {
           tag: huks.HuksTag.HUKS_TAG_PADDING,
           value: huks.HuksKeyPadding.HUKS_PADDING_NONE,
       },
       {
           tag: huks.HuksTag.HUKS_TAG_BLOCK_MODE,
           value: huks.HuksCipherMode.HUKS_MODE_CBC,
       },
       {
           tag: huks.HuksTag.HUKS_TAG_IV,
           value: StringToUint8Array(IV),
       }
   ]
   
   let decryptOptions: huks.HuksOptions = {
       properties: propertiesDecrypt,
       inData: new Uint8Array(new Array())
   }
   
   function initSession(keyAlias: string, huksOptions: huks.HuksOptions, throwObject: throwObject) {
       return new Promise<huks.HuksSessionHandle>((resolve, reject) => {
           try {
               huks.initSession(keyAlias, huksOptions, (error, data) => {
                   if (error) {
                       reject(error);
                   } else {
                       resolve(data);
                   }
               });
           } catch (error) {
               throwObject.isThrow = true;
               throw(error as Error);
           }
       });
   }
   
   async function publicInitFunc(keyAlias: string, huksOptions: huks.HuksOptions) {
       console.info(`enter callback doInit`);
       let throwObject: 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 as Uint8Array;
           })
           .catch((error: BusinessError) => {
               if (throwObject.isThrow) {
                   throw(error as Error);
               } else {
                   console.error(`callback: doInit failed`);
               }
           });
       } catch (error) {
           console.error(`callback: doInit input arg invalid`);
       }
   }
   
   function userIAMAuthFinger(huksChallenge: Uint8Array) {
       // Obtain an authentication object.
       let auth: userIAM_userAuth.AuthInstance ;
       try {
           auth = userIAM_userAuth.getAuthInstance(huksChallenge, authType, authTrustLevel);
           console.log("get auth instance success");
       } catch (error) {
           console.log("get auth instance failed" + error);
           return;
       }
   
       // Subscribe to the authentication result.
       try {
           auth.on("result", {
               callback: (result) => {
                   /* The authentication is successful, and the authentication token is obtained. */
                   fingerAuthToken = (result as userIAM_userAuth.AuthResultInfo).token as Uint8Array;
               }
           });
           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: throwObject) {
       return new Promise<huks.HuksReturnResult>((resolve, reject) => {
           try {
               huks.finishSession(handle, huksOptions, token, (error, data) => {
                   if (error) {
                       reject(error);
                   } else {
                       resolve(data);
                   }
               });
           } catch (error) {
               throwObject.isThrow = true;
               throw(error as Error);
           }
       });
   }
   
   async function publicFinishFunc(handle: number, token: Uint8Array, huksOptions: huks.HuksOptions) {
       console.info(`enter callback doFinish`);
       let throwObject: throwObject = {isThrow: false};
       try {
           await finishSession(handle, huksOptions, token, throwObject)
           .then ((data) => {
               finishOutData = data.outData as Uint8Array;
               console.info(`callback: doFinish success, data = ${JSON.stringify(data)}`);
           })
           .catch((error: BusinessError) => {
               if (throwObject.isThrow) {
                   throw(error as Error);
               } else {
                   console.error(`callback: doFinish failed`);
               }
           });
       } catch (error) {
           console.error(`callback: doFinish input arg invalid`);
       }
   }
   
   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<HuksReturnResult>) : void|Attests a key.|

How to Develop

/*
 * Attest a key and return the result in a callback.
 */
import huks from '@ohos.security.huks';
import { BusinessError } from '@ohos.base';

/*
 * 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: Array<string>;

class throwObject {
    isThrow: boolean = false;
}

class genKeyPropertyType {
    tag: huks.HuksTag = huks.HuksTag.HUKS_TAG_ALGORITHM;
    value: huks.HuksKeyAlg|huks.HuksKeyStorageType|huks.HuksKeySize|huks.HuksKeyPurpose|huks.HuksKeyDigest
      |huks.HuksKeyPadding|huks.HuksKeyGenerateType|huks.HuksCipherMode = huks.HuksKeyAlg.HUKS_ALG_RSA
}

let genKeyProperties: genKeyPropertyType[] = [
    {
        tag: huks.HuksTag.HUKS_TAG_ALGORITHM,
        value: huks.HuksKeyAlg.HUKS_ALG_RSA
    },
    {
        tag: huks.HuksTag.HUKS_TAG_KEY_STORAGE_FLAG,
        value: huks.HuksKeyStorageType.HUKS_STORAGE_PERSISTENT
    },
    {
        tag: huks.HuksTag.HUKS_TAG_KEY_SIZE,
        value: huks.HuksKeySize.HUKS_RSA_KEY_SIZE_2048
    },
    {
        tag: huks.HuksTag.HUKS_TAG_PURPOSE,
        value: huks.HuksKeyPurpose.HUKS_KEY_PURPOSE_VERIFY
    },
    {
        tag: huks.HuksTag.HUKS_TAG_DIGEST,
        value: huks.HuksKeyDigest.HUKS_DIGEST_SHA256
    },
    {
        tag: huks.HuksTag.HUKS_TAG_PADDING,
        value: huks.HuksKeyPadding.HUKS_PADDING_PSS
    },
    {
        tag: huks.HuksTag.HUKS_TAG_KEY_GENERATE_TYPE,
        value: huks.HuksKeyGenerateType.HUKS_KEY_GENERATE_TYPE_DEFAULT
    },
    {
        tag: huks.HuksTag.HUKS_TAG_BLOCK_MODE,
        value: huks.HuksCipherMode.HUKS_MODE_ECB
    }
]

let genOptions: huks.HuksOptions = {
    properties: genKeyProperties
};

class attestKeypropertyType {
    tag: huks.HuksTag = huks.HuksTag.HUKS_TAG_ATTESTATION_ID_SEC_LEVEL_INFO;
    value: Uint8Array = securityLevel;
}

let attestKeyproperties: attestKeypropertyType[] = [
    {
        tag: huks.HuksTag.HUKS_TAG_ATTESTATION_ID_SEC_LEVEL_INFO,
        value: securityLevel
    },
    {
        tag: huks.HuksTag.HUKS_TAG_ATTESTATION_CHALLENGE,
        value: challenge
    },
    {
        tag: huks.HuksTag.HUKS_TAG_ATTESTATION_ID_VERSION_INFO,
        value: versionInfo
    },
    {
        tag: huks.HuksTag.HUKS_TAG_ATTESTATION_ID_ALIAS,
        value: aliasUint8
    }
]

let huksOptions: huks.HuksOptions = {
    properties: attestKeyproperties
};

function StringToUint8Array(str: string) {
    let arr: number[] = [];
    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: throwObject) {
    return new Promise<void>((resolve, reject) => {
        try {
            huks.generateKeyItem(keyAlias, huksOptions, (error, data) => {
                if (error) {
                    reject(error);
                } else {
                    resolve(data);
                }
            });
        } catch (error) {
            throwObject.isThrow = true;
            throw(error as Error);
        }
    });
}

async function publicGenKeyFunc(keyAlias: string, huksOptions: huks.HuksOptions) {
    console.info(`enter callback generateKeyItem`);
    let throwObject: throwObject = {isThrow: false};
    try {
        await generateKeyItem(keyAlias, huksOptions, throwObject)
        .then((data) => {
            console.info(`callback: generateKeyItem success, data = ${JSON.stringify(data)}`);
        })
        .catch((error: BusinessError) => {
            if (throwObject.isThrow) {
                throw(error as Error);
            } else {
                console.error(`callback: generateKeyItem failed`);
            }
        });
    } catch (error) {
        console.error(`callback: generateKeyItem input arg invalid`);
    }
}

function attestKeyItem(keyAlias: string, huksOptions: huks.HuksOptions, throwObject: throwObject) {
    return new Promise<huks.HuksReturnResult>((resolve, reject) => {
        try {
            huks.attestKeyItem(keyAlias, huksOptions, (error, data) => {
                if (error) {
                    reject(error);
                } else {
                    resolve(data);
                }
            });
        } catch (error) {
            throwObject.isThrow = true;
            throw(error as Error);
        }
    });
}

async function publicAttestKey(keyAlias: string, huksOptions: huks.HuksOptions) {
    console.info(`enter callback attestKeyItem`);
    let throwObject: 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 as string[];
            }
        })
        .catch((error: BusinessError) => {
            if (throwObject.isThrow) {
                throw(error as Error);
            } else {
                console.error(`callback: attestKeyItem failed`);
            }
        });
    } catch (error) {
        console.error(`callback: attestKeyItem input arg invalid`);
    }
}

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.

你可能感兴趣的鸿蒙文章

harmony 鸿蒙Security

harmony 鸿蒙Applying for Permissions

harmony 鸿蒙Access Control (Permission) Overview

harmony 鸿蒙HarmonyAppProvision Configuration File

harmony 鸿蒙Certificate Development

harmony 鸿蒙Certificate Overview

harmony 鸿蒙Crypto Framework Development

harmony 鸿蒙Crypto Framework Overview

harmony 鸿蒙DLP Development

harmony 鸿蒙DLP Overview

0  赞