FIDO2提供基于WebAuthn标准的FIDO2客户端实现,这里仅演示对FIDO2客户端相关API的使用,涉及应用服务器及FIDO服务器的相关处理,这里不做介绍,请参考相关标准。
FIDO2分成两个操作,注册和认证,处理流程类似。过程如下:
创建一个Activity。
从FIDO服务器获取挑战值及相关策略,并组装请求消息。(这里仅提供FIDO客户端的API,涉及与FIDO服务器的交互,请参考相关规范,并联系FIDO服务器供应商获取相关接口说明。)
调用Fido2Client.getRegistrationIntent()发起注册。或调用Fido2Client.getAuthenticationIntent()发起认证。
上一步调用成功后,在回调中调用Fido2Intent.launchFido2Activity(),启动注册(requestCode为Fido2Client.REGISTRATION_REQUEST)或认证(requestCode为Fido2Client.AUTHENTICATION_REQUEST)。该回调在主线程中执行。
在回调Activity.onActivityResult()中,调用Fido2Client.getFido2RegistrationResponse()或Fido2Client.getFido2AuthenticationResponse()获取注册或认证的结果。
把注册或认证的结果发送给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);
- }
- }