鸿蒙如何实现指纹加密与解密
生成密钥的方法:使用 SM4 算法对数据进行加密的功能,并且在加密过程中加入了指纹认证环节,以此确保数据的安全性。实例,最后对数据库进行数据的存储和读取操作。,构建了从代码到数据的全生命周期防护体系。适用HarmonyOS NEXT / API12或以上版本 -----------------在万物互联时代,数据安全已成为操作系统的核心战场。那我们既然生成了密钥,将来读取这个文件的时候也是需要解密
引言
在万物互联时代,数据安全已成为操作系统的核心战场。鸿蒙系统(HarmonyOS)凭借其创新的文件指纹加密与分级文件加密机制,构建了从代码到数据的全生命周期防护体系。本文将从技术原理、实现方案、应用场景三个维度深度解析这两大安全特性。
原理参考:
使用@ohos.security.huks密钥管理服务与@ohos.userIAM.userAuth用户认证服务实现指纹识别后加解密的功能。
实现步骤:
-
初始化密钥会话获取挑战值与handle。
-
发起指纹识别获取token。
-
完成密钥会话实现加解密。
所需权限:
-
需要申请权限:ohos.permission.ACCESS_BIOMETRIC。
-
需要录入设备指纹。

代码实现与步骤:
首先需要在module.json5中配置指纹权限
"requestPermissions": [
{
"name": "ohos.permission.ACCESS_BIOMETRIC",
"reason": "$string:access_biometric_reason",
"usedScene": {
"abilities": [
"EntryAbility"
],
"when": "inuse"
}
},
],
再建立一个方法专门生成密钥,代码参考官方文档文档中心
需要有一个密钥别名和初始化密钥的属性参数集
/*
* 确定密钥别名和封装密钥属性参数集
*/
let keyAlias = 'test_sm4_key_alias';
class ThrowObject {
isThrow: boolean = false
}
let properties: Array<huks.HuksParam> = [{
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_PKCS7,
},
// 指定密钥身份认证的类型:指纹
{
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 | huks.HuksKeyPurpose.HUKS_KEY_PURPOSE_ENCRYPT
}];
let huksOptions: huks.HuksOptions = {
properties: properties,
inData: new Uint8Array(new Array())
}
生成密钥的方法:使用 SM4 算法对数据进行加密的功能,并且在加密过程中加入了指纹认证环节,以此确保数据的安全性。
- 参数设置:明确密钥别名、初始化向量(IV)、认证类型等参数,同时配置加密所需的属性参数集。
- 加密准备:调用
encrypt函数,把明文转换为字节数组,接着初始化加密会话以获取挑战值和会话句柄。 - 身份认证:调用
userIAMAuthFingerEncrypt函数,借助指纹认证工具包拉起指纹认证界面。 - 认证处理:订阅认证结果,若认证成功,获取认证令牌,然后调用
publicEncryptFinishFunc函数完成加密操作。 - 加密完成:加密结束后,可通过
getCipherText函数获取加密后的密文数据。
注意事项
- 此代码依赖
@kit.UniversalKeystoreKit和@kit.UserAuthenticationKit工具包,使用前需确保这些工具包已正确安装与配置。 - 代码中假定非分段加密,所以未调用
publicEncryptUpdateFunc函数。若为分段加密,需取消该函数的注释并调用。
function generateKeyItem(keyAlias: string, huksOptions: huks.HuksOptions, throwObject: ThrowObject) {
return new Promise<void>((resolve, reject) => {
try {
huks.generateKeyItem(keyAlias, huksOptions, (error, data) => {
if (error) {
promptAction.showToast({
message: '密钥生成失败,错误码是: ' + error.code + ' 错误吗信息: ' + error.message,
duration: 6500,
})
reject(error);
} else {
console.info(`成功生成了一个别名为:${keyAlias}的密钥`)
resolve(data);
}
});
} catch (error) {
throwObject.isThrow = true;
throw (error as Error);
}
});
}
async function publicGenKeyFunc(keyAlias: string, huksOptions: huks.HuksOptions) {
console.info(`enter promise generateKeyItem`);
let throwObject: ThrowObject = { isThrow: false };
try {
await generateKeyItem(keyAlias, huksOptions, throwObject)
.then((data) => {
console.info(`promise: generateKeyItem success, data = ${JSON.stringify(data)}`);
})
.catch((error: Error) => {
if (throwObject.isThrow) {
throw (error as Error);
} else {
console.error(`promise: generateKeyItem failed, ` + JSON.stringify(error));
}
});
} catch (error) {
console.error(`promise: generateKeyItem input arg invalid, ` + JSON.stringify(error));
}
}
// 生成密钥
export async function TestGenKeyForFingerprintAccessControl() {
await publicGenKeyFunc(keyAlias, huksOptions);
}
那我们既然生成了密钥,将来读取这个文件的时候也是需要解密,所以我们还要编写解密的方法。
-
参数设置:
- 设定密钥别名
keyAlias、初始化向量IV以及认证类型authType(这里是指纹认证)。 - 定义解密所需的属性参数集
propertiesDecrypt,包含算法、用途、密钥大小、填充模式、块模式、初始化向量等信息。 - 初始化解密选项
decryptOptions,将解密属性参数和输入数据(初始为空)传入。
- 设定密钥别名
-
解密准备:
- 调用
decrypt函数,传入密文cipherText。 - 将密文赋值给
decryptOptions.inData。 - 调用
publicInitDecryptFunc函数初始化解密会话,获取挑战值challenge和会话句柄handle。
- 调用
-
身份认证:
- 调用
userIAMAuthFingerDecrypt函数,传入挑战值challenge。 - 配置认证参数
authParam和界面参数widgetParam。 - 获取认证实例
auth,若获取失败则输出错误信息并返回。 - 订阅认证结果事件,若认证成功,获取认证令牌
fingerAuthToken,并调用publicDecryptFinishFunc函数完成解密操作。
- 调用
-
解密完成:
- 在
publicDecryptFinishFunc函数中,调用finishDecryptSession函数完成解密会话,获取解密后的明文plainTextOutput。 - 可以通过
getPlainTextOutput函数将解密后的字节数组转换为字符串并返回。
- 在
/*
* 解密
* */
// 引入通用密钥库工具包
import { huks } from '@kit.UniversalKeystoreKit';
// 引入类型转换工具
import { StringToUint8Array, TypeUtil } from './TypeUtil';
// 引入基础服务工具包中的业务错误类
import { BusinessError } from '@kit.BasicServicesKit';
// 引入用户认证工具包
import { userAuth } from '@kit.UserAuthenticationKit';
/*
* 确定封装密钥属性参数集
*/
// 密钥别名
let keyAlias = 'test_sm4_key_alias';
// 初始化向量
let IV = '1234567890123456';
// 会话句柄,初始化为 0
let handle = 0;
// 挑战值
let challenge: Uint8Array;
// 指纹认证令牌
let fingerAuthToken: Uint8Array;
// 解密后的明文数据
let plainTextOutput: Uint8Array;
// 认证类型为指纹认证
let authType = userAuth.UserAuthType.FINGERPRINT;
// 抛出异常对象
class ThrowObject {
// 是否抛出异常的标志
isThrow: boolean = false;
}
/* 集成生成密钥参数集 & 解密参数集 */
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,
// 算法为 SM4
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,
// 密钥大小为 128 位
value: huks.HuksKeySize.HUKS_SM4_KEY_SIZE_128,
},
{
// 填充模式标签
tag: huks.HuksTag.HUKS_TAG_PADDING,
// 填充模式为 PKCS7
value: huks.HuksKeyPadding.HUKS_PADDING_PKCS7,
},
{
// 块模式标签
tag: huks.HuksTag.HUKS_TAG_BLOCK_MODE,
// 加密模式为 CBC
value: huks.HuksCipherMode.HUKS_MODE_CBC,
},
{
// 初始化向量标签
tag: huks.HuksTag.HUKS_TAG_IV,
// 将初始化向量转换为字节数组
value: StringToUint8Array(IV),
},
{
// 密钥认证用途标签
tag: huks.HuksTag.HUKS_TAG_KEY_AUTH_PURPOSE,
// 密钥认证用途为解密
value: huks.HuksKeyPurpose.HUKS_KEY_PURPOSE_DECRYPT
}
]
// 解密选项
let decryptOptions: huks.HuksOptions = {
// 解密属性参数
properties: propertiesDecrypt,
// 输入数据
inData: new Uint8Array(new Array())
}
// 解密函数
export async function decrypt(cipherText: Uint8Array) {
/* 认证成功后进行解密, 需要传入Auth获取到的authToken值 */
// 将密文作为输入数据
decryptOptions.inData = cipherText;
/* 初始化密钥会话获取挑战值与handle */
// 初始化解密会话
await publicInitDecryptFunc(keyAlias, decryptOptions);
/* 调用userIAM进行身份认证 */
// 调用指纹认证
userIAMAuthFingerDecrypt(challenge);
}
/* 初始化HUKS中的会话,获取挑战值与handle */
async function publicInitDecryptFunc(keyAlias: string, huksOptions: huks.HuksOptions) {
console.info(`进入初始化会话函数`);
let throwObject: ThrowObject = { isThrow: false };
try {
await initDecryptSession(keyAlias, huksOptions, throwObject)
.then((data) => {
console.info(`初始化会话成功, 数据 = ${JSON.stringify(data)}`);
// 获取会话句柄
handle = data.handle;
// 获取挑战值
challenge = data.challenge as Uint8Array;
})
.catch((error: BusinessError) => {
if (throwObject.isThrow) {
throw (error as Error);
} else {
console.error(`初始化会话失败` + JSON.stringify(error));
}
});
} catch (error) {
console.error(`初始化会话输入参数无效` + JSON.stringify(error));
}
}
// 初始化解密会话函数
function initDecryptSession(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);
}
});
}
// 完成解密会话函数
export async function publicDecryptFinishFunc(handle: number, token: Uint8Array, huksOptions: huks.HuksOptions) {
console.info(`进入完成会话函数`);
let throwObject: ThrowObject = { isThrow: false };
try {
await finishDecryptSession(handle, huksOptions, token, throwObject)
.then((data) => {
// 获取解密后的明文
plainTextOutput = data.outData as Uint8Array;
console.info(`完成会话成功, 数据 = ${JSON.stringify(data)}`);
console.info(`完成会话成功, 原始数据 = ${JSON.stringify(data.outData)}`);
})
.catch((error: BusinessError) => {
if (throwObject.isThrow) {
throw (error as Error);
} else {
console.error(`完成会话失败` + JSON.stringify(error));
}
});
} catch (error) {
console.error(`完成会话输入参数无效` + JSON.stringify(error));
}
}
// 完成解密会话函数
function finishDecryptSession(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 publicDecryptUpdateFunc(handle: number, token: Uint8Array, huksOptions: huks.HuksOptions) {
console.info(`进入更新会话函数`);
let throwObject: ThrowObject = { isThrow: false };
try {
await updateSession(handle, huksOptions, token, throwObject)
.then((data) => {
console.info(`更新会话成功, 数据 = ${JSON.stringify(data)}`);
})
.catch((error: Error) => {
if (throwObject.isThrow) {
throw (error as Error);
} else {
console.error(`更新会话失败, ` + JSON.stringify(error));
}
});
} catch (error) {
console.error(`更新会话输入参数无效, ` + JSON.stringify(error));
}
}
// 更新会话函数
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);
}
});
}
/* 调用UserIAM拉起指纹认证,触发HUKS的访问控制流程 */
export function userIAMAuthFingerDecrypt(huksChallenge: Uint8Array) {
// 获取认证对象
let authTypeList: userAuth.UserAuthType[] = [authType];
const authParam: userAuth.AuthParam = {
// 挑战值
challenge: huksChallenge,
// 认证类型
authType: authTypeList,
// 认证信任级别
authTrustLevel: userAuth.AuthTrustLevel.ATL1
};
const widgetParam: userAuth.WidgetParam = {
// 认证提示标题
title: '请验证身份进行解密',
};
let auth: userAuth.UserAuthInstance;
try {
// 获取认证实例
auth = userAuth.getUserAuthInstance(authParam, widgetParam);
console.info('获取认证实例成功');
} catch (error) {
console.error('获取认证实例失败' + JSON.stringify(error));
return;
}
// 订阅认证结果
try {
auth.on('result', {
onResult(result) {
// 认证成功
console.info('[HUKS] -> [IAM] 用户认证实例回调结果 = ' + JSON.stringify(result));
// 获取指纹认证令牌
fingerAuthToken = result.token;
/*
* 传入认证令牌进行解密
* 非分段加密不需要执行publicDecryptUpdateFunc方法
* publicDecryptUpdateFunc(handle, result.token, decryptOptions);
* */
// 完成解密会话
publicDecryptFinishFunc(handle, result.token, decryptOptions);
}
});
console.log('订阅认证事件成功');
} catch (error) {
console.error('订阅认证事件失败, ' + JSON.stringify(error));
}
// 开始认证
try {
auth.start();
console.info('开始指纹认证成功');
} catch (error) {
console.error('开始指纹认证失败, 错误 = ' + JSON.stringify(error));
}
}
// 获取明文数据方法
export function getPlainTextOutput(): string {
return TypeUtil.arrayToString(plainTextOutput);
}
数据类型转换类
import { util } from '@kit.ArkTS';
export class TypeUtil {
// 字符串转为Uint8Array
static stringToArray(str: string) : Uint8Array {
let textEncoder = new util.TextEncoder('utf-8');
return textEncoder.encodeInto(str);
}
//Uint8Array转为字符串
static arrayToString(arr: Uint8Array): string {
let textDecoder = util.TextDecoder.create('utf-8', { ignoreBOM: true });
let str = textDecoder.decodeToString(arr, { stream: false })
return str;
}
}
// 字符串转为Uint8Array
// 遍历字符串的每个字符,获取其Unicode编码值并存储到数组中,最后转换为Uint8Array
export 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);
}
// 字符串转为Uint8Array
// 遍历字符串的每个字符,获取其Unicode编码值并存储到数组中,最后转换为Uint8Array
export function Uint8ArrayToString(fileData: Uint8Array) {
let dataString = '';
for (let i = 0; i < fileData.length; i++) {
dataString += String.fromCharCode(fileData[i]);
}
return dataString;
}
接着我们需要在UIability里的onCreate里创建分布式数据库,创建了 KVManager 实例,接着通过 KVManager 获取 KVStore 实例,最后对数据库进行数据的存储和读取操作。这样就能保证在应用程序启动时数据库就已准备好,为后续操作提供支持。
/*
* 分布式键值数据库:
* Select database is exists,
* Result type is true,
* Query dataset verification,
* */
let context = this.context;
const kvManagerConfig: distributedKVStore.KVManagerConfig = {
context: context,
bundleName: this.context.abilityInfo.bundleName,
};
try {
// 创建KVManager实例
kvManager = distributedKVStore.createKVManager(kvManagerConfig) as distributedKVStore.KVManager;
console.info('Succeeded in creating KVManager.');
} catch (e) {
let error = e as BusinessError;
console.error(`Failed to create KVManager. Code:${error.code},message:${error.message}`);
}
if (kvManager !== undefined) {
kvManager = kvManager as distributedKVStore.KVManager;
// 进行后续创建数据库等相关操作
try {
const options: distributedKVStore.Options = {
createIfMissing: true,
encrypt: false,
backup: false,
autoSync: false,
// kvStoreType不填时,默认创建多设备协同数据库
kvStoreType: distributedKVStore.KVStoreType.SINGLE_VERSION,
// 多设备协同数据库:kvStoreType: distributedKVStore.KVStoreType.DEVICE_COLLABORATION,
securityLevel: distributedKVStore.SecurityLevel.S3
};
kvManager.getKVStore<distributedKVStore.SingleKVStore>(this.storeId, options, (err, store: distributedKVStore.SingleKVStore) => {
if (err) {
console.error(`Failed to get KVStore: Code:${err.code},message:${err.message}`);
return;
}
console.info('Succeeded in getting KVStore.');
kvStore = store as distributedKVStore.SingleKVStore;
// 请确保获取到键值数据库实例后,再进行相关数据操作
if (kvStore !== undefined) {
kvStore = kvStore as distributedKVStore.SingleKVStore;
//进行后续操作
try {
const KEY_TEST_STRING_ELEMENT = 'key_test_string';
const VALUE_TEST_STRING_ELEMENT = 'value_test_string';
kvStore.put(KEY_TEST_STRING_ELEMENT, VALUE_TEST_STRING_ELEMENT, (err) => {
if (err !== undefined) {
console.error(`Failed to put data. Code:${err.code},message:${err.message}`);
return;
}
console.info('Succeeded in putting data.');
});
kvStore.get(KEY_TEST_STRING_ELEMENT, (err, data) => {
if (err !== undefined) {
console.error(`Failed to get data. Code:${err.code},message:${err.message}`);
return;
}
console.info(`Succeeded in getting data. Data:${data}`);
});
} catch (e) {
let error = e as BusinessError;
console.error(`An unexpected error occurred. Code:${error.code},message:${error.message}`);
}
}
});
} catch (e) {
let error = e as BusinessError;
console.error(`An unexpected error occurred. Code:${error.code},message:${error.message}`);
}
}
页面调用加密
.onClick(async () => {
// 加密时明文不能为空
if (this.plainTextInput === null || this.plainTextInput === '') {
showAlertDialog('请输入需要加密的日记')
} else {
// 加密
encrypt(this.plainTextInput).then(() => {
console.log('加密成功');
}).catch((err: BusinessError) => {
console.error('encrypt promise failed, because: ' + err.code);
});
setTimeout(() => {
console.log('等待加载');
// 密文存入数据库
let cipherText = getCipherText()
if (kvStore !== undefined) {
//kvStore = kvStore as distributedKVStore.SingleKVStore;
//进行后续操作
try {
let KEY_TEST_STRING_ELEMENT = this.days;
let VALUE_TEST_STRING_ELEMENT = Uint8ArrayToString(cipherText);
console.info('value size:' + VALUE_TEST_STRING_ELEMENT.length)
kvStore.put(KEY_TEST_STRING_ELEMENT, VALUE_TEST_STRING_ELEMENT, (err) => {
if (err !== undefined) {
console.error(`Failed to put data. Code:${err.code},message:${err.message}`);
return;
}
console.info('Succeeded in putting data.');
});
} catch (e) {
let error = e as BusinessError;
console.error(`An unexpected error occurred. Code:${error.code},message:${error.message}`);
}
}
},3000)
}
})
调用解密:
.onClick(async () => {
if (kvStore !== undefined) {
// kvStore = kvStore as distributedKVStore.SingleKVStore;
//进行后续操作
try {
let KEY_TEST_STRING_ELEMENT = this.days;
kvStore.get(KEY_TEST_STRING_ELEMENT, async (err, data) => {
if (err !== undefined) {
console.error(`Failed to get data. Code:${err.code},message:${err.message}`);
return;
}
console.info(`Succeeded in getting data. Data:${data}`);
let cipherText = StringToUint8Array(data as string);
await decrypt(cipherText as Uint8Array);
// 延迟1.5秒获取明文,等待密文解密成功
setTimeout(() => {
console.log('输出结果');
this.plainTextOutput = getPlainTextOutput()
console.log('result:' + this.plainTextOutput)
},3000)
});
} catch (e) {
let error = e as BusinessError;
console.error(`An unexpected error occurred. Code:${error.code},message:${error.message}`);
}
}
})
适用HarmonyOS NEXT / API12或以上版本 -----------------
更多推荐



所有评论(0)