在移动端企业应用中,传统“账号+密码”认证带来了密码疲劳、撞库、中间人劫持等风险,即使引入短信验证码也无法彻底解决通道成本和延迟问题。随着零信任架构的普及,FIDO2无密码认证与通行密钥(Passkeys)成为更优解。HarmonyOS 7.0(API 26)大幅升级了Online Authentication Kit,不仅完整接入FIDO2免密协议,还深度打通TrustZone(TEE),实现了生物特征(指纹/3D人脸)与硬件密钥的强认证双向加密隧道。
1. 核心架构:TEE隔离与椭圆曲线非对称加密
通行密钥本质是一对secp256r1椭圆曲线密钥对。私钥永久锚定在TEE内部,不可提取;公钥由应用上传至业务服务器(RP)。认证时,RP下发随机挑战值(Challenge),用户通过生物识别解锁TEE私钥,签名挑战值后返回给RP验签。TEE拥有独立微内核和加密存储,通过SMC通道与普通世界交互,即使系统被攻破也无法窃取FIDO私钥。
2. 实战:通行密钥注册(Registration)
注册流程需要精确处理参数类型和数据格式。以下为Fido2Client.ets的核心代码片段:- import { authentication } from '@kit.OnlineAuthenticationKit';
- import { AuthNetworkClient } from '../network/AuthNetworkClient';
- import { util } from '@kit.ArkTS';
- export class Fido2Client {
- private static readonly RP_ID = "auth.enterprise.com";
- public static async registerPasskey(username: string): Promise<boolean> {
- try {
- let optionsResponse = await AuthNetworkClient.getRegisterOptions(username);
- let base64Helper = new util.Base64Helper();
- let challengeArray = base64Helper.decodeSync(optionsResponse.challenge);
- let userIdArray = base64Helper.decodeSync(optionsResponse.userId);
- let creationOptions: authentication.PublicKeyCredentialCreationOptions = {
- rp: { id: Fido2Client.RP_ID, name: "Enterprise OA System" },
- user: { id: userIdArray, name: username, displayName: username },
- challenge: challengeArray,
- pubKeyCredParams: [{ type: authentication.PublicKeyCredentialType.PUBLIC_KEY, alg: -7 }],
- authenticatorSelection: {
- authenticatorAttachment: authentication.AuthenticatorAttachment.PLATFORM,
- requireResidentKey: true,
- userVerification: authentication.UserVerificationRequirement.REQUIRED
- },
- timeout: 60000
- };
- let authManager = authentication.getFidoAuthenticationManager();
- let createResult = await authManager.createCredential(creationOptions);
- if (createResult && createResult.response) {
- let clientDataJSONBase64 = base64Helper.encodeToStringSync(createResult.response.clientDataJSON);
- let attestationBase64 = base64Helper.encodeToStringSync(createResult.response.attestationObject);
- let credentialIdBase64 = base64Helper.encodeToStringSync(createResult.id);
- let isSuccess = await AuthNetworkClient.verifyRegistration(
- username, credentialIdBase64, clientDataJSONBase64, attestationBase64
- );
- return isSuccess;
- }
- return false;
- } catch (error) {
- console.error(`[Fido2Client] Passkey 注册异常: ${JSON.stringify(error)}`);
- return false;
- }
- }
- }
复制代码 注意:challenge和user.id必须转为Uint8Array;alg:-7代表ES256算法;authenticatorAttachment设为PLATFORM强制使用内置认证器。
3. 实战:通行密钥登录(Assertion)
登录时利用Discoverable Credentials特性,客户端无需提前传入用户名,用户通过生物识别选择本地凭据后,系统返回userHandle供RP识别用户:- public static async loginWithPasskey(): Promise<boolean> {
- try {
- let assertionResponse = await AuthNetworkClient.getLoginOptions();
- let base64Helper = new util.Base64Helper();
- let challengeArray = base64Helper.decodeSync(assertionResponse.challenge);
- let requestOptions: authentication.PublicKeyCredentialRequestOptions = {
- challenge: challengeArray,
- rpId: Fido2Client.RP_ID,
- userVerification: authentication.UserVerificationRequirement.REQUIRED,
- timeout: 60000
- };
- let authManager = authentication.getFidoAuthenticationManager();
- let assertionResult = await authManager.getAssertion(requestOptions);
- if (assertionResult && assertionResult.response) {
- let clientDataJSONBase64 = base64Helper.encodeToStringSync(assertionResult.response.clientDataJSON);
- let authenticatorDataBase64 = base64Helper.encodeToStringSync(assertionResult.response.authenticatorData);
- let signatureBase64 = base64Helper.encodeToStringSync(assertionResult.response.signature);
- let credentialIdBase64 = base64Helper.encodeToStringSync(assertionResult.id);
- let userHandleBase64 = assertionResult.response.userHandle ?
- base64Helper.encodeToStringSync(assertionResult.response.userHandle) : "";
- let loginSuccess = await AuthNetworkClient.verifyLogin(
- credentialIdBase64, clientDataJSONBase64, authenticatorDataBase64,
- signatureBase64, userHandleBase64
- );
- return loginSuccess;
- }
- return false;
- } catch (error) {
- console.error(`[Fido2Client] Passkey 登录异常: ${JSON.stringify(error)}`);
- return false;
- }
- }
复制代码
4. 二次强认证:BiometricHelper + TeeCryptoManager
对于敏感操作(如大额转账),可调用UserAuthKit进行硬件绑定的活体认证,并利用AuthToken在TEE内对关键数据签名:- import { userAuth } from '@kit.UserAuthenticationKit';
- import { TeeCryptoManager } from './TeeCryptoManager';
- export class BiometricHelper {
- public static async executeStrongAuth(criticalData: string): Promise<string | null> {
- let authParam: userAuth.AuthParam = {
- challenge: TeeCryptoManager.generateLocalChallenge(),
- authType: [userAuth.UserAuthType.FINGERPRINT, userAuth.UserAuthType.FACE],
- authTrustLevel: userAuth.AuthTrustLevel.ATL3
- };
- let widgetParam: userAuth.WidgetParam = {
- title: "大额资金划拨确认",
- navigationButtonText: "取消"
- };
- try {
- let userAuthInstance = userAuth.getUserAuthInstance(authParam, widgetParam);
- return new Promise((resolve, reject) => {
- userAuthInstance.on('result', {
- onResult(result) {
- if (result.result === userAuth.UserAuthResultCode.SUCCESS) {
- let signature = TeeCryptoManager.signWithAuthToken(result.token, criticalData);
- resolve(signature);
- } else {
- resolve(null);
- }
- }
- });
- userAuthInstance.start();
- });
- } catch (err) {
- console.error(`[BiometricHelper] 强认证初始化异常: ${JSON.stringify(err)}`);
- return null;
- }
- }
- }
复制代码
5. 企业级避坑指南
- RP ID绑定失败:必须确保代码中的rp.id、服务端域名以及AGC后台的app-linking配置三者一致,否则系统拒绝生成凭据。
- Base64URL编码陷阱:WebAuthn要求无填充的Base64URL(替换+和/为-和_),后端生成挑战值时必须遵守,否则在util.Base64Helper().decodeSync时会出错。
- 凭据撤销同步:用户手动删除Passkey后,RP服务器无法实时感知。客户端需捕获凭据不存在异常,引导用户进入恢复流程,并标记后端凭据失效。
总结:Online Authentication Kit将TEE、ECC加密和生物特征封装为易用的API,通过FIDO2标准实现零信任基础的免密体验。开发者需严格遵循参数格式和域名绑定规则,才能构建高安全、丝滑的认证体系。 |