简介

FIDO2提供基于WebAuthn标准的FIDO2 线上快速身份验证客户端实现,为应用及浏览器提供安卓Java API,支持使用USB、NFC、蓝牙漫游认证器,以及指纹和3D面容的平台认证器,进行安全认证。

您将建立什么

在这个Codelab中,您将使用已经创建好的Demo Project实现对华为FIDO2客户端API调用,通过DemoProject你可以体验到:

您将会学到什么

其他特性

本地生物特征认证

硬件要求

软件要求

集成HUAWEI HMS Core能力,需要完成以下准备工作

具体操作,请按照《HUAWEI HMS Core集成准备》中详细说明来完成。

打开服务开关

点击"管理中心"->"应用市场"->"我的应用"->"应用名"->"开发"->"概览"->"API 管理",进入服务管理菜单。

打开"FIDO"服务开关。

添加当前应用的AGC配置文件

将下载的"agconnect-services.json"文件移至Android Studio开发工程app的根目录下。

配置HMS SDK的maven仓地址

打开Android Studio项目级build.gradle文件

在allprojects ->repositories里面配置HMS SDK的maven仓地址

allprojects { repositories { google() jcenter() // Add the following line maven { url 'http://developer.huawei.com/repo/' } } }

在buildscript -> repositories里面配置HMS SDK的maven仓地址

buildscript { repositories { google() jcenter() // Add the following line maven { url 'http://developer.huawei.com/repo/' } } }

在buildscript ->dependencies里面增加配置

buildscript { dependencies { classpath 'com.android.tools.build:gradle:3.3.0' // Add the following line classpath 'com.huawei.agconnect:agcp:1.0.0.300' } }

添加编译依赖

打开应用级的build.gradle文件

在"dependencies"中添加编译依赖

dependencies { implementation 'com.huawei.hms:fido-fido2:4.0.3.300' }

在文件头添加配置

Apply plugin: 'com.huawei.agconnect'

在"android"中配置签名

将从能力接入准备中获取的JKS文件(如FIDO BioAuthn Android Sample.jks)拷贝到工程的app目录下。同时在build.gradle文件中配置签名。

android { signingConfigs { config { keyAlias 'FIDO BioAuthn Android Sample' keyPassword '123456' storeFile file('FIDO BioAuthn Android Sample.jks') storePassword '123456' } } buildTypes { debug { signingConfig signingConfigs.config } release { signingConfig signingConfigs.config minifyEnabled false proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' } } }

点击sync now 同步工程

FIDO2分成两个操作,注册和认证,处理流程类似。过程如下:

  1. 创建一个Activity。
  2. 从FIDO服务器获取挑战值及相关策略,并组装请求消息。(这里仅提供FIDO客户端的API,涉及与FIDO服务器的交互,请参考相关规范,并联系FIDO服务器供应商获取相关接口说明。)
  3. 调用Fido2Client.getRegistrationIntent()发起注册。或调用Fido2Client.getAuthenticationIntent()发起认证。
  4. 上一步调用成功后,在回调中调用Fido2Intent.launchFido2Activity(),启动注册(requestCode为Fido2Client.REGISTRATION_REQUEST)或认证(requestCode为Fido2Client.AUTHENTICATION_REQUEST)。该回调在主线程中执行。
  5. 在回调Activity.onActivityResult()中,调用Fido2Client.getFido2RegistrationResponse()或Fido2Client.getFido2AuthenticationResponse()获取注册或认证的结果。
  6. 把注册或认证的结果发送给FIDO服务器进行验证。(这里仅提供FIDO客户端的API,涉及与FIDO服务器的交互,请参考相关规范,并联系FIDO服务器供应商获取相关接口说明。)

FIDO2 相关API使用方法,如以下代码片段所示:

package com.huawei.hms.kit.fidocp; import android.app.Activity; import android.app.AlertDialog; import android.content.Intent; import android.os.Bundle; import android.view.View; import android.widget.TextView; import androidx.appcompat.app.AppCompatActivity; import com.huawei.hms.fidocp.R; import com.huawei.hms.support.api.fido.fido2.Algorithm; import com.huawei.hms.support.api.fido.fido2.AttestationConveyancePreference; import com.huawei.hms.support.api.fido.fido2.AuthenticatorSelectionCriteria; import com.huawei.hms.support.api.fido.fido2.Fido2; import com.huawei.hms.support.api.fido.fido2.Fido2AuthenticationRequest; import com.huawei.hms.support.api.fido.fido2.Fido2AuthenticationResponse; import com.huawei.hms.support.api.fido.fido2.Fido2Client; import com.huawei.hms.support.api.fido.fido2.Fido2Intent; import com.huawei.hms.support.api.fido.fido2.Fido2IntentCallback; import com.huawei.hms.support.api.fido.fido2.Fido2RegistrationRequest; import com.huawei.hms.support.api.fido.fido2.Fido2RegistrationResponse; import com.huawei.hms.support.api.fido.fido2.Fido2Response; import com.huawei.hms.support.api.fido.fido2.NativeFido2AuthenticationOptions; import com.huawei.hms.support.api.fido.fido2.NativeFido2RegistrationOptions; import com.huawei.hms.support.api.fido.fido2.PublicKeyCredentialCreationOptions; import com.huawei.hms.support.api.fido.fido2.PublicKeyCredentialDescriptor; import com.huawei.hms.support.api.fido.fido2.PublicKeyCredentialParameters; import com.huawei.hms.support.api.fido.fido2.PublicKeyCredentialRequestOptions; import com.huawei.hms.support.api.fido.fido2.PublicKeyCredentialRpEntity; import com.huawei.hms.support.api.fido.fido2.PublicKeyCredentialType; import com.huawei.hms.support.api.fido.fido2.PublicKeyCredentialUserEntity; import java.security.SecureRandom; import java.util.ArrayList; import java.util.Arrays; import java.util.List; import java.util.Locale; public class Fido2DemoActivity extends AppCompatActivity { private TextView reusltView; private Fido2Client fido2Client; private byte[] regCredentialId = null; private String rpId = "com.huawei.hms.fido2.test"; private String user = "fidoCp"; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_fido2_demo); reusltView = findViewById(R.id.reusltView); fido2Client = Fido2.getFido2Client(this); } // 从FIDO服务获取挑战值及相关策略,并组装Fido2RegistrationRequest请求消息。 private Fido2RegistrationRequest assembleFido2RegistrationRequest() { // TODO 从FIDO服务获取挑战值及相关策略。 byte[] challengeBytes = getChallege(); // 组装Fido2RegistrationRequest请求消息。 PublicKeyCredentialCreationOptions.Builder builder = new PublicKeyCredentialCreationOptions.Builder(); builder.setRp(new PublicKeyCredentialRpEntity(rpId, rpId, null)) .setUser(new PublicKeyCredentialUserEntity(user, user.getBytes())) .setChallenge(challengeBytes) .setAttestation(AttestationConveyancePreference.DIRECT) .setAuthenticatorSelection(new AuthenticatorSelectionCriteria(null, null, null)) .setPubKeyCredParams(new ArrayList<PublicKeyCredentialParameters>( Arrays.asList(new PublicKeyCredentialParameters(PublicKeyCredentialType.PUBLIC_KEY, Algorithm.ES256), new PublicKeyCredentialParameters(PublicKeyCredentialType.PUBLIC_KEY, Algorithm.RS256)))) .setTimeoutSeconds(60L); if (regCredentialId != null) { builder.setExcludeList(new ArrayList<PublicKeyCredentialDescriptor>( Arrays.asList(new PublicKeyCredentialDescriptor(PublicKeyCredentialType.PUBLIC_KEY, regCredentialId)))); } return new Fido2RegistrationRequest(builder.build(), null); } public void btnRegistrationClicked(View view) { if (!fido2Client.isSupported()) { showMsg("不支持FIDO2。"); return; } Fido2RegistrationRequest request = assembleFido2RegistrationRequest(); // 调用Fido2Client.getRegistrationIntent获取一个Fido2Intent实例,并启动FIDO客户端注册流程。 fido2Client.getRegistrationIntent(request, NativeFido2RegistrationOptions.DEFAULT_OPTIONS, new Fido2IntentCallback() { @Override public void onSuccess(Fido2Intent fido2Intent) { // 通过Fido2Client.REGISTRATION_REQUEST,启动FIDO客户端注册流程。 fido2Intent.launchFido2Activity(Fido2DemoActivity.this, Fido2Client.REGISTRATION_REQUEST); } @Override public void onFailure(int errorCode, CharSequence errString) { showError("注册失败。" + errorCode + "=" + errString); } }); } // 从FIDO服务获取挑战值及相关策略,并组装Fido2AuthenticationRequest请求消息。 private Fido2AuthenticationRequest assembleFido2AuthenticationRequest() { // TODO 从FIDO服务获取挑战值及相关策略。 byte[] challengeBytes = getChallege(); // 组装Fido2RegistrationRequest请求消息。 List<PublicKeyCredentialDescriptor> allowList = new ArrayList<>(); allowList.add(new PublicKeyCredentialDescriptor(PublicKeyCredentialType.PUBLIC_KEY, regCredentialId)); PublicKeyCredentialRequestOptions.Builder builder = new PublicKeyCredentialRequestOptions.Builder(); builder.setRpId(rpId).setChallenge(challengeBytes).setAllowList(allowList).setTimeoutSeconds(60L); return new Fido2AuthenticationRequest(builder.build(), null); } public void btnAuthenticationClicked(View view) { if (regCredentialId == null) { showMsg("请先进行注册。"); return; } if (!fido2Client.isSupported()) { showMsg("不支持FIDO2。"); return; } Fido2AuthenticationRequest request = assembleFido2AuthenticationRequest(); // 调用Fido2Client.getAuthenticationIntent获取一个Fido2Intent实例,并启动FIDO客户端认证流程。 fido2Client.getAuthenticationIntent(request, NativeFido2AuthenticationOptions.DEFAULT_OPTIONS, new Fido2IntentCallback() { @Override public void onSuccess(Fido2Intent fido2Intent) { // 通过Fido2Client.AUTHENTICATION_REQUEST,启动FIDO客户端注册流程。 fido2Intent.launchFido2Activity(Fido2DemoActivity.this, Fido2Client.AUTHENTICATION_REQUEST); } @Override public void onFailure(int errorCode, CharSequence errString) { showError("认证失败。" + errorCode + "=" + errString); } }); } @Override protected void onActivityResult(int requestCode, int resultCode, Intent data) { super.onActivityResult(requestCode, resultCode, data); if (resultCode != Activity.RESULT_OK) { showMsg("未知错误。"); return; } switch (requestCode) { // Receive registration response case Fido2Client.REGISTRATION_REQUEST: Fido2RegistrationResponse fido2RegistrationResponse = fido2Client.getFido2RegistrationResponse(data); if (fido2RegistrationResponse.isSuccess()) { reusltView.append("注册\n"); reusltView.append(fido2RegistrationResponse.getAuthenticatorAttestationResponse().toJson()); reusltView.append("\n"); // save the credentialId regCredentialId = fido2RegistrationResponse.getAuthenticatorAttestationResponse().getCredentialId(); showMsg("注册成功。"); } else { showError("注册失败。", fido2RegistrationResponse); } break; // Receive authentication response case Fido2Client.AUTHENTICATION_REQUEST: Fido2AuthenticationResponse fido2AuthenticationResponse = fido2Client.getFido2AuthenticationResponse(data); if (fido2AuthenticationResponse.isSuccess()) { reusltView.append("认证\n"); reusltView.append(fido2AuthenticationResponse.getAuthenticatorAssertionResponse().toJson()); reusltView.append("\n"); showMsg("认证成功。"); } else { showError("认证失败。", fido2AuthenticationResponse); } } } private void showError(String message) { AlertDialog.Builder builder = new AlertDialog.Builder(Fido2DemoActivity.this); builder.setTitle("错误"); builder.setMessage(message); builder.setPositiveButton("确认", null); builder.show(); } private void showError(String message, Fido2Response fido2Response) { StringBuilder errMsgBuilder = new StringBuilder(); errMsgBuilder.append(message) .append(fido2Response.getFido2Status()) .append("=") .append(fido2Response.getFido2StatusMessage()) .append(String.format(Locale.getDefault(), "(Ctap错误:0x%x=%s)", fido2Response.getCtapStatus(), fido2Response.getCtapStatusMessage())); showError(errMsgBuilder.toString()); } public void showMsg(String message) { AlertDialog.Builder builder = new AlertDialog.Builder(Fido2DemoActivity.this); builder.setTitle("提示"); builder.setMessage(message); builder.setPositiveButton("确认", null); builder.show(); } public void btnClearLogClicked(View view) { reusltView.setText(""); regCredentialId = null; } private byte[] getChallege() { return SecureRandom.getSeed(16); } }

安装测试apk,点击注册或认证按钮。

干得好,您已经成功完成了Codelab并学到了:

源码下载源码下载

1894E85F57B06676DE6DF2353533C2B0D97FD742AC2FE403A5FC7BC.zip?needInitFileName=true)

已复制代码