1. 導入

1.1. Web Authenticationとは

Web Authenticationは、W3Cで2019年3月に勧告になった、Webアプリケーションの新しいセキュアな認証方式仕様です。 WebAuthnという略称で表記されることもあります。ローカル認証や公開鍵認証、Origin別の鍵管理を組み合わせることで、フィッシングなど認証プロセスに対する攻撃に対して堅固で便利な認証を実現しています。 主要ブラウザで実装されており、セキュリティ、利便性を重視するユーザーに対して優れた選択肢を提供します。

1.2. Passkeysとは

Passkeysは、WebAuthn仕様の使いやすさを向上させたWebAuthn Level3仕様に対するわかりやすいブランディング名です。

1.3. WebAuthn4Jとは

WebAuthn4Jは、Web Authentication仕様に基づくサーバーサイド検証を行うためのJavaライブラリです。WebAuthn/Passkeysだけでなく、Apple App Attestや、FIDO CTAP2セキュリティキーを用いた独自アプリケーションのサーバーサイド検証に用いることも可能です。 Web Authentication仕様で定められた全ての構成証明ステートメント(Attestation)をサポートしながら、外部ライブラリへの依存関係は最小限に抑えたポータブルなライブラリです。

1.4. 特徴

1.4.1. サポートする構成証明ステートメントフォーマット

WebAuthn4Jでは、以下の通り全ての構成証明ステートメントフォーマットをサポートしています。

  • Packed attestation

  • FIDO U2F attestation

  • Android Key attestation

  • Android SafetyNet attestation

  • TPM attestation

  • Apple Anonymous attestation

  • None attestation

  • Apple App Attest attestation

1.4.2. 準拠状況

FIDO Allianceが提供するFIDO2 Test Tools で必須のテストケースに加え、オプションのAndroid Key attestationのテストケースを全て合格しています。

FIDO2 Test ToolsはFIDO2 Transport Binding ProfileのREST API経由でテストを実行する為、WebAuthn4J Spring Securityが 提供するFIDO2 Transport Binding ProfileのREST API実装に対して実行して検証を行っています。

1.4.3. ポータビリティ

WebAuthn4JはSLF4JとJacksonにのみ依存しており、非常に高いポータビリティを持っています。 あなたのJavaアプリケーションにほぼ制約なく組み込めるでしょう。

1.5. 要件

1.5.1. 言語

  • Java11以降 (EdDSAを利用したい場合、Java17以降)

1.6. 動作環境

  • SecureContext(サイトがHTTPS接続か、localhost。WebAuthn仕様上の要件)

1.7. Maven Centralからの取得

Mavenを使用している場合、webauthn4jを依存関係として追加して使用してください:

<properties>
  ...
  <!-- Use the latest version whenever possible. -->
  <webauthn4j.version>0.28.5.RELEASE</webauthn4j.version>
  ...
</properties>

<dependencies>
  ...
  <dependency>
    <groupId>com.webauthn4j</groupId>
    <artifactId>webauthn4j-core</artifactId>
    <version>${webauthn4j.version}</version>
  </dependency>
  ...
</dependencies>

1.8. ソースコード

ソースコードは Github で管理されています。

git clone git@github.com:webauthn4j/webauthn4j.git

1.9. ライセンス

WebAuthn4jは Apache 2.0 License の オープンソースソフトウェアです。

1.10. 開発への参加

WebAuthn4Jに興味を持って頂きありがとうございます。WebAuthn4JはGitHub上で開発を進めています。 お気軽にIssueやPull Requestをお寄せ下さい。

2. クイックスタート

このクイックスタートでは、まずWebAuthn認証の処理の概要を紹介し、その後、WebAuthn4Jライブラリがカバーする範囲や制限、およびWebAuthn4Jを利用したWebAuthn認証の実装方法を説明します。

2.1. WebAuthn認証の処理概要

2.1.1. 認証のフロー

WebAuthn認証は、端的に言えば、ウェブでの利用にあわせて設計された公開鍵認証です。 予め鍵ペアを作成し、サーバー側に公開鍵、認証デバイス側に秘密鍵を保存しておき、 認証時は認証デバイスで秘密鍵で署名を行い、署名をサーバー側に送信して公開鍵で署名を検証することで認証を行う方式です。

署名対象のデータは、クライアントのデータや、認証デバイスのデータです。クライアントのデータには、予めサーバー側で生成したチャレンジや、表示しているサイトのドメイン(origin)等、サーバー側に関連するデータも含まれます。 署名の検証だけでなく、チャレンジの一致もサーバー側で検証しているため、リプレイ攻撃を防止できます。 他にも様々なクライアントのデータや、認証デバイスのデータが署名対象データに含まれており、サーバー側での検証の対象となっています。 認証時のデータの流れを図にすると以下の通りです。

authentication sequence

2.1.2. 登録のフロー

WebAuthnの新しいクレデンシャル登録処理とは、クライアントが認証デバイスに対して新しい鍵ペアの生成を要求し、 認証デバイスからクライアントに返却された公開鍵等をクレデンシャルとしてサーバーに登録する処理です。 実は、新しいクレデンシャル登録時も認証時と類似したフローであり、 まずサーバーから送信されたチャレンジを含むクライアントデータと、認証デバイスのデータに対して 認証デバイスが署名して返却し、これをクライアント経由で受け取ったサーバーは署名検証を実施、 成功した場合、クレデンシャルレコードとして登録する、という流れになっています。 但し、登録時の場合、署名対象の認証デバイスのデータに、生成された新しい鍵ペアの公開鍵が含まれます。 サーバーにはこの公開鍵が登録され、認証時の署名検証で使用されることになります。

図にしますと以下の通りです。

registration sequence

前のセクションで説明した通り、認証時はクレデンシャルの秘密鍵で署名されているので、署名をクレデンシャルの公開鍵で検証する仕組となっていますが、 それでは公開鍵登録時に認証デバイスのデータとクライアントのデータに署名する際に使用される秘密鍵は何なのでしょうか。また、この署名を検証する公開鍵をサーバーはどうやって入手するのでしょうか。 通常、実はこの秘密鍵は認証デバイスのモデル毎に固有の鍵で、認証デバイスにあらかじめ焼きこまれています。 検証に使用する公開鍵に関しては、予め信頼する認証デバイスの公開鍵をサーバーに構成しておくか、 FIDO Metadata Serviceのように、認証デバイスのモデル毎の公開鍵を公開しているレジストリから取得することが可能です。

このように、クレデンシャルの公開鍵登録のメッセージを認証デバイスのモデル固有の秘密鍵で署名することで、 登録しようとしている認証デバイスが特定のモデルであることを証明する"Attestation"(構成証明)という仕組がWebAuthnには備わっており、 このデバイスの構成証明情報を含んだデータ構造は、構成証明ステートメント(Attestation Statement)と呼ばれます。 ただし、ユーザーがどのモデルの認証デバイスを使っているかという構成証明ステートメントは、ユーザートラッキングにも使われうる情報です。 そのため、デフォルトの構成では、認証デバイスが構成証明ステートメントを返却してもクライアントによって破棄され、サーバー側には送信されません。 構成証明ステートメントを送信するようオプションで明示的に指定した場合にのみ、エンドユーザーの同意を得たうえで構成証明ステートメントは開示されます。

2.2. WebAuthn4Jのスコープ

WebAuthn4Jは特定のWebアプリケーションフレームワークに依存しないポータビリティを実現する為、WebAuthnの登録時、認証時のサーバー側の検証に意図的に機能を絞り込んでいます。

webauthn4j scope

そのため、HTTPリクエストからのパラメータの取出、チャレンジのセッションへの保存、 フロントエンド側への返却、 生成された公開鍵などをクレデンシャルレコードとして保存する処理、認証時にクレデンシャルレコードをロードする処理、といった機能は具備していません。 それらの処理は、利用しているフレームワークにあわせて実装する必要があるためです。 ご利用のフレームワークで、それらをケアしてくれるWebAuthn4Jのラッパーライブラリが存在する場合は、そちらを利用すると良いでしょう。 例えば、

  • Quarkus Security WebAuthn

  • Spring Security Passkeys

  • Vert.x Auth WebAuthn4J

といったラッパーライブラリが提供されています。 ラッパーライブラリが存在しない場合は、自前でそれらの処理を実装頂く必要があります。次のセクションで説明していきます。

2.3. WebAuthn4Jを利用した登録処理の実装

2.3.1. WebAuthnの鍵ペアの生成

WebAuthnの鍵ペアの生成において中心となるAPIは、ブラウザの navigator.credentials.create メソッドです。 このAPIを呼び出すことで、WebAuthnの鍵ペアが認証デバイスによって生成され、公開鍵を含むWebAuthnのクレデンシャルが戻り値として返却されます。

navigator.credentials.create メソッドの呼出時には、様々なオプションが指定出来ます。 そのオプションの一つに、 challenge が存在します。前述の通り、チャレンジはリプレイ攻撃を防止するためのパラメータであり、サーバー側で生成した値をパラメータとして指定し、また、同じ値をセッション等に保存しておく必要があります。 登録のフローの図の通り、まずバックエンドサーバーでチャレンジを生成してセッションに保存し、それをクライアントに渡す必要があります。 バックエンドサーバーからフロントエンドへのチャレンジの受け渡し方法はWebAuthn仕様では特に定められていません。 HTMLページに埋め込んでも良いですし、チャレンジを返却するRESTエンドポイントを用意することも可能です。 navigator.credentials.create メソッドのパラメータである、 PublicKeyCredentialCreationOptions 全体を返却するエンドポイントを用意するのも良いアイデアでしょう。 WebAuthnのJava Script APIには、 PublicKeyCredential.parseCreationOptionsFromJSON というメソッドが用意されており、JSONとしてシリアライズされた PublicKeyCredentialCreationOptions をパースすることが可能です。 但し、2024/12現在、Safariでは PublicKeyCredential.parseCreationOptionsFromJSON が利用できません。代替策については、Safariで未サポートなJSON serialization APIsの代替 を参照してください。

WebAuthn4Jは PublicKeyCredentialCreationOptions を表現するJavaのクラスを提供しており、バックエンドサーバー側でJSONを組み立てる際にご活用頂けます。

例 1: PublicKeyCredentialCreationOptions 全体をREST Endpointから取得して navigator.credentials.create を呼出
const response = await fetch("/passkeys/attestationOptions") //fetch PublicKeyCredentialCreationOptions as JSON string
const publicKeyCredentialCreationOptionsJSON = await response.json() // convert to JSONObject
const credentialCreationOptions = PublicKeyCredential.parseCreationOptionsFromJSON(publicKeyCredentialCreationOptionsJSON); // convert to PublicKeyCredentialCreationOptions
const publicKeyCredential = await navigator.credentials.create({ publicKey: credentialCreationOptions}); // create PublicKeyCredential

いずれにせよ、バックエンドサーバー側でチャレンジを生成し、セッションに保存した上で、何らかの方法でフロントエンド側に引き渡した上で、 フロントエンド側のJava Scriptで navigator.credentials.create メソッドを呼び出してWebAuthnクレデンシャルを生成して下さい。 navigator.credentials.create メソッドに指定できるその他のオプションに関しては、 MDN: CredentialsContainer: create() メソッドを参照下さい。

2.3.2. WebAuthnの公開鍵のサーバーへの登録

生成されたWebAuthnクレデンシャルは、何らかの方法でバックエンドサーバー側に送信する必要があります。 バックエンドサーバー側にどのようなフォーマットで送信するかについてもWebAuthn仕様では定義されていません。 但し、WebAuthnクレデンシャルである PublicKeyCredential というJavaScriptの型には、 toJSON というメソッドが用意されており、 こちらと JSON.stringify を利用してシリアライズしたデータを送信するのが一つのベストプラクティスです。 但し、この toJSON メソッドもSafariでは利用できませんが、代替策については、Safariで未サポートなJSON serialization APIsの代替 を参照してください。

例 2: PublicKeyCredential の送信
const registrationResponseJSON = publicKeyCredential.toJSON(); // convert to JSONObject
await fetch("/register", {
    method : 'POST',
    headers: {
        'Content-Type': 'application/x-www-form-urlencoded'
    },
    body: new URLSearchParams({
        'username': document.getElementById('username').value,
        'registrationResponseJSON': JSON.stringify(registrationResponseJSON) //convert to string
    })
});

バックエンドサーバー側は受け取ったWebAuthnクレデンシャルを検証した上で、公開鍵を含むWebAuthnクレデンシャルレコードを永続化する必要があります。 WebAuthn4Jでは、 PublicKeyCredential のJSON表現を WebAuthnManager#verifyRegistrationResponseJSON というメソッドで直接検証することが可能です。 WebAuthnManager#parseRegistrationResponseJSON は、検証を行わず、PublicKeyCredential のデシリアライズ処理のみを行います。 検証でエラーが発生した場合に、元のパースされたデータにアクセスしたい場合は、 WebAuthnManager#parseRegistrationResponseJSON メソッドを用いてパースしたうえで、 得られた RegistrationData のインスタンスを WebAuthnManager#verify メソッドに渡して検証を実行してください。

例 3: PublicKeyCredential のサーバーサイド検証
String registrationResponseJSON = "<registrationResponseJSON>"; /* set registrationResponseJSON received from frontend */
RegistrationData registrationData;
try {
    registrationData = webAuthnManager.parseRegistrationResponseJSON(registrationResponseJSON);
}
catch (DataConversionException e) {
    // If you would like to handle WebAuthn data structure parse error, please catch DataConversionException
    throw e;
}

// Server properties
Origin origin = null /* set origin */;
String rpId = null /* set rpId */;
Challenge challenge = null /* set challenge */;
ServerProperty serverProperty = new ServerProperty(origin, rpId, challenge);

// expectations
List<PublicKeyCredentialParameters> pubKeyCredParams = null;
boolean userVerificationRequired = false;
boolean userPresenceRequired = true;

RegistrationParameters registrationParameters = new RegistrationParameters(serverProperty, pubKeyCredParams, userVerificationRequired, userPresenceRequired);

try {
    webAuthnManager.verify(registrationData, registrationParameters);
} catch (VerificationException e) {
    // If you would like to handle WebAuthn data verification error, please catch VerificationException
    throw e;
}

// please persist CredentialRecord object, which will be used in the authentication process.
CredentialRecord credentialRecord =
        new CredentialRecordImpl( // You may create your own CredentialRecord implementation to save friendly authenticator name
                registrationData.getAttestationObject(),
                registrationData.getCollectedClientData(),
                registrationData.getClientExtensions(),
                registrationData.getTransports()
        );
save(credentialRecord); // please persist credentialRecord in your manner

RegistrationParameters は、WebAuthnManager#verifyRegistrationResponseJSON メソッドのもう一つの引数であり、 サーバーの状態や検証条件をまとめたパラメータです。

  • serverProperty は、サーバーの状態を渡すパラメータです。次の[ServerProperty]を参照して下さい。

  • pubKeyCredParams には PublicKeyCredentialCreationOptions で指定した pubKeyCredParams と同じ値を指定して下さい。

  • userVerificationRequired は、認証デバイスでのユーザーの生体認証やPIN確認などでの当人認証を必須とするかのパラメータです。

  • userPresenceRequired は、認証デバイス側でのユーザーの介在確認を必須とするかのパラメータです。ユーザーによって何らかのジェスチャー入力が行われたことを示すUPフラグを確認します。 このジェスチャーには、生体認証に限らず、静電容量ボタンのタッチ等、当人認証が行われない操作も含まれます。 WebAuthnにおいては、UPフラグは基本的に必須ですので true を指定すべきですが、パスワードからパスキーへの自動アップグレード時のクレデンシャル自動生成時のシナリオに限っては false となります。

サーバーの状態については、 serverProperty としてまとめています。 ServerProperty のコンストラクタを呼び出す際のパラメータには以下の値を指定して下さい。

  • origin にはWebAuthnによる認証を提供するサイトのOriginを指定して下さい。WebAuthnでは、ブラウザが認識しているOriginをClientDataに書き込んで署名を行います。WebAuthn4Jは書き込まれたOriginが指定されたOriginと合致するかを検証することで、 フィッシング攻撃を防ぎます。

  • rpId にはWebAuthnによる認証を提供するサイトのrpIdを指定して下さい。rpIdは資格情報のスコープを指定するパラメータです。 詳しくは WebAuthnの仕様書のrpIdの項 を参照して下さい。

  • challenge には発行したChallengeを指定して下さい。challenge はリプレイ攻撃を防ぐ為のパラメータです。 サーバー側で challenge としてランダムなバイト列を生成し、フロントエンド側でWebAuthn JS APIを実行する際に パラメータとして指定して署名対象に含め、サーバー側で値の一致を検証することで、リプレイ攻撃からユーザーを防御することが出来ます。 発行したChallengeを検証時まで永続化しておくのはWebAuthn4Jライブラリ呼出側の責務です。セッションなどに格納しておくと良いでしょう。

検証に成功した場合は、返却された値から CredentialRecord インスタンスを作成し、データベース等へアプリケーション側で永続化して下さい。 認証時に使用します。 永続化方法について詳しくは、 CredentialRecordのシリアライズ、デシリアライズ を参照して下さい。 検証に失敗した場合は、 VerificationException のサブクラスの例外が発生します。

2.4. WebAuthn4Jを利用した認証処理の実装

2.4.1. WebAuthnのアサーションの生成

WebAuthnでの認証時において中心となるAPIは、ブラウザの navigator.credentials.get メソッドです。 認証のフローの図の通り、認証処理においても、まずバックエンドサーバー側でチャレンジを生成し、セッションに保存する一方、クライアントにチャレンジを引き渡す必要があります。 navigator.credentials.get メソッドのパラメータにも challenge が存在するためです。 バックエンドサーバーからフロントエンド(クライアント)への認証処理のチャレンジの受け渡し方法もWebAuthn仕様では定められていません。登録処理同様、お好みの方法でチャレンジをフロントエンド側に引き渡して下さい。 navigator.credentials.get メソッドのパラメータである、 PublicKeyCredentialGetOptions をパースするJava Script APIは、 PublicKeyCredential.parseCreationGetOptionsFromJSON です。 PublicKeyCredential.parseCreationGetOptionsFromJSON がSafariで利用できない問題の代替案はSafariで未サポートなJSON serialization APIsの代替 を参照してください。 navigator.credentials.get メソッドに指定できるその他のオプションに関しては、 MDN: CredentialsContainer: get() メソッドを参照下さい。

例 4: PublicKeyCredentialGetOptions 全体をREST Endpointから取得して navigator.credentials.get を呼出
const response = await fetch("/passkeys/assertionOptions");
const publicKeyCredentialRequestOptionsJSON = await response.json();
const credentialGetOptions = PublicKeyCredential.parseRequestOptionsFromJSON(publicKeyCredentialRequestOptionsJSON);
const publicKeyCredential = await navigator.credentials.get({ publicKey: credentialGetOptions});

2.4.2. WebAuthnのアサーションの検証処理、および後処理

navigator.credentials.get メソッドによって生成されたアサーションは、バックエンドサーバー側に送信し、検証する必要があります。登録時同様、toJSON メソッドでシリアライズが可能です。

例 5: PublicKeyCredential をサーバーに送信
const authenticationResponseJSON = publicKeyCredential.toJSON();
console.debug("authenticationResponseJSON: %s", authenticationResponseJSON);
await fetch("/passkeys/authenticate", {
    method : 'POST',
    headers: {
        'Content-Type': 'application/json'
    },
    body: JSON.stringify(authenticationResponseJSON)
});

WebAuthn4Jでは、 PublicKeyCredential のJSON表現を WebAuthnManager#verifyAuthenticationResponseJSON というメソッドで検証することが可能です。 パースし、検証する2段階を踏む場合は、WebAuthnManager#parseAuthenticationResponseJSON メソッドと WebAuthnManager#verify メソッドをご利用下さい。

例 6: PublicKeyCredential のサーバーサイド検証
String authenticationResponseJSON = "<authenticationResponseJSON>"; /* set authenticationResponseJSON received from frontend */

AuthenticationData authenticationData;
try {
    authenticationData = webAuthnManager.parseAuthenticationResponseJSON(authenticationResponseJSON);
} catch (DataConversionException e) {
    // If you would like to handle WebAuthn data structure parse error, please catch DataConversionException
    throw e;
}

// Server properties
Origin origin = null /* set origin */;
String rpId = null /* set rpId */;
Challenge challenge = null /* set challenge */;
ServerProperty serverProperty = new ServerProperty(origin, rpId, challenge);

// expectations
List<byte[]> allowCredentials = null;
boolean userVerificationRequired = true;
boolean userPresenceRequired = true;

CredentialRecord credentialRecord = load(authenticationData.getCredentialId()); // please load authenticator object persisted in the registration process in your manner
AuthenticationParameters authenticationParameters =
        new AuthenticationParameters(
                serverProperty,
                credentialRecord,
                allowCredentials,
                userVerificationRequired,
                userPresenceRequired
        );

try {
    webAuthnManager.verify(authenticationData, authenticationParameters);
} catch (VerificationException e) {
    // If you would like to handle WebAuthn data validation error, please catch ValidationException
    throw e;
}
// please update the counter of the authenticator record
updateCounter(
        authenticationData.getCredentialId(),
        authenticationData.getAuthenticatorData().getSignCount()
);

WebAuthnManager#verifyAuthenticationResponseJSON メソッドのもう一つの引数である AuthenticationParameters は、サーバーの状態や検証条件をまとめたパラメータです。

  • serverProperty は、サーバーの状態を渡すパラメータです。詳しくは[ServerProperty] を参照して下さい。

  • userVerificationRequired は認証デバイスでのユーザーの生体認証やPIN確認などでの当人認証を必須とするかのパラメータです。パスワード+認証デバイスの「所持」による多要素認証を行う場合は、パスワードで本人性の確認が出来ている為 false で良いでしょう。 パスワードレス認証として、認証デバイスによる本人性確認+「所持」による多要素認証を行う場合は true を指定する必要があります。

  • authenticator には、登録時に永続化した CredentialRecord を指定してください。

検証に成功した場合は、認証に成功したものと見做すことが出来ますので、永続化された CredentialRecord に紐づけたcounterおよび、uvInitialized、backedUpの値を更新してください。 カウンタは万が一認証デバイスのクローンが 作成されたことを検知するために用意されています。 カウンタについて詳しくは WebAuthnの仕様書のカウンタの項 を参照して下さい。

その後、認証済セッションを作成するなど、ユーザー認証成功時の処理を実施下さい。 検証に失敗した場合は、 VerificationException のサブクラスの例外が発生します。

2.5. Apple App Attestの検証

続いて、Apple App Attestの検証方法について解説します。 Apple App Attestは、WebAuthnに類似したデータ構造を持つため、Verifierの設計も、WebAuthn用のVerifierを踏襲しています。 なお、リスクメトリックの評価には対応していません。

2.5.1. Maven Centralからの取得

Apple App Attestの検証用クラスは、WebAuthn4J本体(webauthn4j-core)とは別の、webauthn4j-appattestというモジュールとして配布されています。 Mavenを使用している場合、以下のようにwebauthn4j-appattestを依存関係として追加してください。

<properties>
  ...
  <!-- Use the latest version whenever possible. -->
  <webauthn4j.version>0.28.5.RELEASE</webauthn4j.version>
  ...
</properties>

<dependencies>
  ...
  <dependency>
    <groupId>com.webauthn4j</groupId>
    <artifactId>webauthn4j-appattest</artifactId>
    <version>${webauthn4j.version}</version>
  </dependency>
  ...
</dependencies>

2.5.2. Apple App Attest構成証明の検証

認証デバイスの登録時に構成証明を検証する際は、DCAttestationRequest を引数に DeviceCheckManager#verify メソッドを用いて登録リクエストのパース、検証を行ってください。 登録リクエストの検証でエラーが発生した場合に、元のパースされたデータにアクセスしたい場合は、 DeviceCheckManager#parse メソッドを用いて登録リクエストをパースしたうえで、 得られた DCAttestationData のインスタンスを DeviceCheckManager#verify メソッドに渡して実行してください。

DCAttestationRequest のメンバー はiOS上でDevice Check App Attest APIを実行して取得した値となります。 何らかの方法でiOSデバイス側からサーバー側に伝送し、指定してください。

DCAttestationParameters は、DeviceCheckManager#verify メソッドのもう一つの引数であり、 サーバーの状態や検証条件をまとめたパラメータです。 サーバーの状態については、 DCServerProperty としてまとめています。 DCServerProperty のコンストラクタを呼び出す際のパラメータには以下の値を指定して下さい。

  • teamIdentifier にはiOSアプリ開発時のteam identifierを指定してください。 詳しくは Apple Apple Attestのサーバサイド検証手順 を参照して下さい。

  • cfBundleIdentifier にはiOSアプリ開発時のbundle identifierを指定してください。 詳しくは Apple Apple Attestのサーバサイド検証手順 を参照して下さい。

  • challenge には発行したChallengeを指定して下さい。challenge はリプレイ攻撃を防ぐ為のパラメータです。 サーバー側で challenge としてランダムなバイト列を生成し、iOS側でApp Attest APIを実行する際に パラメータとして指定して署名対象に含め、サーバー側で値の一致を検証することで、リプレイ攻撃からユーザーを防御することが出来ます。 発行したChallengeを検証時まで永続化しておくのはアプリケーション側の責務です。セッションなどに格納しておくと良いでしょう。

検証に失敗した場合は、 VerificationException のサブクラスの例外が発生します。 検証に成功した場合は、返却された値から DCAppleDevice インスタンスを作成し、データベース等へアプリケーション側で永続化して下さい。 認証時に必要となります。

商用環境か?開発環境か?

Apple App Attestは、開発中用に開発用の構成証明を返却することが可能です。 WebAuthn4Jはデフォルトでは商用の構成証明を受け入れる設定となっており、 開発用の構成証明を利用する場合は、 DCAttestationDataVerifier#setProductionfalse を設定する必要があります。

// Client properties
byte[] keyId = null; /* set keyId */
byte[] attestationObject = null; /* set attestationObject */
byte[] challenge = null; /* set challenge */
byte[] clientDataHash = MessageDigestUtil.createSHA256().digest(challenge);

// Server properties
String teamIdentifier = null /* set teamIdentifier */;
String cfBundleIdentifier = null /* set cfBundleIdentifier */;
DCServerProperty dcServerProperty = new DCServerProperty(teamIdentifier, cfBundleIdentifier, new DefaultChallenge(challenge));

DCAttestationRequest dcAttestationRequest = new DCAttestationRequest(keyId, attestationObject, clientDataHash);
DCAttestationParameters dcAttestationParameters = new DCAttestationParameters(dcServerProperty);
DCAttestationData dcAttestationData;
try {
    dcAttestationData = deviceCheckManager.parse(dcAttestationRequest);
} catch (DataConversionException e) {
    // If you would like to handle Apple App Attest data structure parse error, please catch DataConversionException
    throw e;
}
try {
    deviceCheckManager.verify(dcAttestationData, dcAttestationParameters);
} catch (ValidationException e) {
    // If you would like to handle Apple App Attest data validation error, please catch ValidationException
    throw e;
}

// please persist Authenticator object, which will be used in the authentication process.
DCAppleDevice dcAppleDevice =
        new DCAppleDeviceImpl( // You may create your own Authenticator implementation to save friendly authenticator name
                dcAttestationData.getAttestationObject().getAuthenticatorData().getAttestedCredentialData(),
                dcAttestationData.getAttestationObject().getAttestationStatement(),
                dcAttestationData.getAttestationObject().getAuthenticatorData().getSignCount(),
                dcAttestationData.getAttestationObject().getAuthenticatorData().getExtensions()
        );
save(dcAppleDevice); // please persist authenticator in your manner

2.5.3. Apple App Attestアサーションの検証

認証時にアサーションを検証する際は、DCAssertionRequest を引数に DeviceCheckManager#verify メソッドを 実行してください。DCAssertionRequest の コンストラクタの引数に指定する、 keyIdassertionclientDataHash は iOS側でApple App Attest APIを実行して取得した値となります。 何らかの方法でフロントエンド側からサーバー側に伝送し、指定してください。 DeviceCheckManager#verify メソッドのもう一つの引数である DCAssertionParameters の コンストラクタの引数に指定する、 serverProperty はサーバー側から取得する値をまとめたパラメータです。

DCAppleDevice には、登録時に永続化した DCAppleDevice を指定してください。

検証に失敗した場合は、 VerificationException のサブクラスの例外が発生します。 検証後は、 DCAppleDevice に紐づけたカウンタの値を更新してください。カウンタは万が一認証デバイスのクローンが 作成された場合を検知するために用意されています。カウンタについて詳しくは WebAuthnの仕様書のカウンタの項 を参照して下さい。

// Client properties
byte[] keyId = null /* set keyId */;
byte[] assertion = null /* set assertion */;
byte[] clientDataHash = null /* set clientDataHash */;

// Server properties
String teamIdentifier = null /* set teamIdentifier */;
String cfBundleIdentifier = null /* set cfBundleIdentifier */;
byte[] challenge = null;
DCServerProperty dcServerProperty = new DCServerProperty(teamIdentifier, cfBundleIdentifier, new DefaultChallenge(challenge));

DCAppleDevice dcAppleDevice = load(keyId); // please load authenticator object persisted in the attestation process in your manner

DCAssertionRequest dcAssertionRequest =
        new DCAssertionRequest(
                keyId,
                assertion,
                clientDataHash
        );
DCAssertionParameters dcAssertionParameters =
        new DCAssertionParameters(
                dcServerProperty,
                dcAppleDevice
        );

DCAssertionData dcAssertionData;
try {
    dcAssertionData = deviceCheckManager.parse(dcAssertionRequest);
} catch (DataConversionException e) {
    // If you would like to handle Apple App Attest data structure parse error, please catch DataConversionException
    throw e;
}
try {
    deviceCheckManager.verify(dcAssertionData, dcAssertionParameters);
} catch (ValidationException e) {
    // If you would like to handle Apple App Attest data validation error, please catch ValidationException
    throw e;
}
// please update the counter of the authenticator record
updateCounter(
        dcAssertionData.getCredentialId(),
        dcAssertionData.getAuthenticatorData().getSignCount()
);

3. 設定

WebAuthn4Jを利用する上で中心となるクラスは WebAuthnManager クラスです。 WebAuthnManager は登録リクエスト検証時の構成証明ステートメントの署名と信頼性の検証を、 それぞれ AttestationStatementVerifierCertPathTrustworthinessVerifier インタフェースの実装に委譲します。

大多数のサイトは厳密な構成証明ステートメントの検証を必要とせず、エンタープライズ用途以外では厳密な構成証明ステートメントの検証は非推奨とされていることから( WebAuthn仕様書関連個所参照 )、 WebAuthn4Jでは構成証明ステートメントの検証をしないように AttestationStatementVerifierCertPathTrustworthinessVerifier を構成した WebAuthnManager のインスタンスを返却する WebAuthnManager.createNonStrictWebAuthnManager ファクトリメソッドを用意しています。

もし、エンタープライズなユースケースで、認証デバイスの厳密な検証が要件である場合は、 WebAuthnManager クラスのコンストラクタを用いて AttestationStatementVerifierCertPathTrustworthinessVerifier の実装をコンストラクタインジェクションして構成して下さい。

3.1. 構成証明ステートメントの検証

構成証明ステートメントの検証は、 AttestationStatementVerifier インタフェースの実装クラスが提供します。 構成証明ステートメント毎に、対応する実装クラスが提供されていますので、必要なVerifierからなるListを WebAuthnManager クラスのコンストラクタに指定して下さい。 例えば、 packed のみをサポートする場合は、 PackedAttestationStatementVerifier を唯一の要素とするListとし、 例えば、 packed, tpm をサポートする場合は、 PackedAttestationStatementVerifierTPMAttestationStatementVerifier からなるListを指定して下さい。 Attestation検証を行わない NoneAttestationStatementVerifierNullPackedAttestationStatementVerifier と、 他の AttestationStatementVerifier を混在させるのはやめましょう。 検証を行わない NoneAttestationStatementVerifier などを、他の検証を行う AttestationStatementVerifier と混ぜてしまうと、Attestation検証迂回に使用される抜け穴となります。

3.1.1. 構成証明ステートメントの信頼性の検証

構成証明ステートメント自体の信頼性の検証は、証明書パスの検証、自己署名のパターンがありますが、 証明書パスの検証は CertPathTrustworthinessVerifier インタフェースの実装に検証が委譲されます。 WebAuthn4Jは CertPathTrustworthinessVerifier インタフェースの実装として DefaultCertPathTrustworthinessVerifier を提供しています。 DefaultCertPathTrustworthinessVerifierTrustAnchorRepository インタフェースを通じて取得した TrustAnchor をトラストアンカーとして証明書パスの検証を行うことで構成証明ステートメントの信頼性の検証を行います。

3.1.2. トラストアンカーの解決

前節で TrustAnchorRepository インタフェースがトラストアンカーの取得に使用されると述べましたが、 TrustAnchorRepository インタフェースは AAGUIDattestationCertificateKeyIdentifier に基づいて TrustAnchor を返却するインタフェースです。 webauthn4j-core モジュールでは、 TrustAnchorRepository の実装として、KeyStoreTrustAnchorRepository を提供しています。 KeyStoreTrustAnchorRepository は、Java Key Storeファイルからトラストアンカーを取得します。なお、 KeyStoreTrustAnchorRepositoryAAGUIDattestationCertificateKeyIdentifier に応じて異なる TrustAnchorを返却することはせず、Java Key Storeファイルに登録された証明書を全てトラストアンカーとして扱います。

FIDO Metadata Serviceを用いたトラストアンカーの取得
FIDO Metadata Statement関連の機能を提供する webauthn4j-metadata モジュールは実験的な提供段階です。

FIDO Allianceでは、FIDO Metadata Serviceという、認証デバイスのメタデータを配信するサービスを提供しています。 webauthn4j-metadata モジュールでは、TrustAnchorRepository の実装として MetadataBLOBBasedTrustAnchorRepository を提供しています。 MetadataBLOBBasedTrustAnchorRepositoryFidoMDS3MetadataBLOBAsyncProvider と組み合わせることで FIDO Metadata Serviceの公開する情報に基づいてトラストアンカーを構築することが可能です。

3.2. ログ

WebAuthn4JはSLF4Jをログインタフェースライブラリとして使用します。 Logbackなどログ実装ライブラリを構成し、ログをお好みのスタイルで出力してください。

4. 詳細

4.1. クレデンシャル情報の表現

クレデンシャル情報を表現するインタフェースとして、 CredentialRecord インタフェースが存在します。 登録時に、 RegistrationData クラスが含む値を用いて CredentialRecord インタフェースの インスタンスを作成し、アプリケーションの作法に則り永続化してください。認証時の検証に必要となります。 なお、永続化する際は、検索する際の利便性を考え、credentialIdをキーに永続化すると良いでしょう。 CredentialRecord インタフェースの実装クラスを設計する際は、アプリケーションの要件に合わせて拡張すると良いでしょう。 典型的には、 CredentialRecord をユーザーが識別するための名前フィールドの追加などが考えられるでしょう。

4.2. CredentialRecordのシリアライズ、デシリアライズ

認証デバイスの登録時、CredentialRecord のインスタンスをデータベース等に永続化し、認証時に利用できるようにするのはアプリケーションの責務ですが、 CredentialRecord を構成する各メンバをシリアライズ、デシリアライズする際に使用できるクラスをWebAuthn4Jでは用意しています。 アプリケーションで永続化を実装する際の補助としてご利用ください。

4.2.1. attestedCredentialData

AttestedCredentialDataConverterAttestedCredentialDatabyte[] に変換したり、その逆の変換を行うことが出来ます。 なお、String として保存したい場合、さらに Base64UrlUtil を用いることで byte[] から Base64Url String に変換することが出来ます。

AttestedCredentialDataConverter attestedCredentialDataConverter = new AttestedCredentialDataConverter(objectConverter);

// serialize
byte[] serialized = attestedCredentialDataConverter.convert(attestedCredentialData);
// deserialize
AttestedCredentialData deserialized = attestedCredentialDataConverter.convert(serialized);

4.2.2. attestationStatement

AttestationStatement はインタフェースであり、PackedAttestationStatement や、AndroidKeyAttestationStatement など、フォーマットにあわせて複数の実装があります。 AttestationStatement のCBOR表現は具象型が何であるかについて自己記述性を持たない為、フォーマットは別途、別のフィールドとして永続化する必要があります。 そのため、CBORとしてシリアライズする際は、実際のattestationStatementのフィールドと、フォーマットのフィールドを持つエンベロープクラスを用意し、エンベロープクラスをシリアライズしなければなりません。 エンベロープクラス自体は、WebAuthn4Jライブラリでは提供していないため、以下の例を参考にアプリケーションコード側で実装下さい。

//serialize
AttestationStatementEnvelope envelope = new AttestationStatementEnvelope(attestationStatement);
byte[] serializedEnvelope = objectConverter.getCborConverter().writeValueAsBytes(envelope);

//deserialize
AttestationStatementEnvelope deserializedEnvelope = objectConverter.getCborConverter().readValue(serializedEnvelope, AttestationStatementEnvelope.class);
AttestationStatement deserializedAttestationStatement = deserializedEnvelope.getAttestationStatement();
class AttestationStatementEnvelope{

    @JsonProperty("attStmt")
    @JsonTypeInfo(
            use = JsonTypeInfo.Id.NAME,
            include = JsonTypeInfo.As.EXTERNAL_PROPERTY,
            property = "fmt"
    )
    private AttestationStatement attestationStatement;

    @JsonCreator
    public AttestationStatementEnvelope(@JsonProperty("attStmt") AttestationStatement attestationStatement) {
        this.attestationStatement = attestationStatement;
    }

    @JsonProperty("fmt")
    public String getFormat() {
        return attestationStatement.getFormat();
    }

    public AttestationStatement getAttestationStatement() {
        return attestationStatement;
    }
}

4.2.3. transports

JSON文字列として保存したい場合は ObjectConverter が使用できます。

String serializedTransports = objectConverter.getJsonConverter().writeValueAsString(transports);

4.2.4. counter

このメンバは元々long型の為、特に変換は不要です。

4.2.5. authenticatorExtensions

authenticatorExtensionsは元来CBORデータの為、CBOR byte配列に変換できます。 なお、String として保存したい場合、さらに Base64UrlUtil を用いることで byte[] から Base64Url String に変換することが出来ます。

byte[] serializedAuthenticatorExtensions = objectConverter.getCborConverter().writeValueAsBytes(authenticatorExtensions);

4.2.6. clientExtensions

clientExtensionsは元来JSONデータの為、JSON文字列に変換できます。

String serializedClientExtensions = objectConverter.getJsonConverter().writeValueAsString(clientExtensions);

4.3. DCAppleDeviceのシリアライズ、デシリアライズ

webauthn4j-appattestでは、CredentialRecord インタフェースの代わりに、DCAppleDevice インタフェースを実装したクラスを、構成証明の検証時とアサーションの検証時の間で永続化する必要があります。 概ねCredentialRecordのシリアライズ、デシリアライズで解説した方法でシリアライズ、デシリアライズが可能ですが、一点気を付ける必要がある点として、 webauthn4j-appattest独自のクラス(例えば AppleAppAttestAttestationStatement )のシリアライズ、デシリアライズを行う為に、 ObjectConverterDeviceCheckCBORModule が登録されたものを使用する必要があります。 DeviceCheckCBORModule が登録された ObjectConverterDeviceCheckManager#createObjectConverter で得ることが出来ます。

4.4. Safariで未サポートなJSON serialization APIsの代替

クイックスタートでは、 PublicKeyCredentialCreationOptions をパースするAPIとして PublicKeyCredential.parseCreationOptionsFromJSON が、 PublicKeyCredential をシリアライズするAPIとして PublicKeyCredential#toJSON が存在すると紹介しましたが、2024/12現在、Safariでは利用できません。 代わりとして、GitHubが提供するnpmライブラリ、 github/@webauthn-json が提供する、 pony-fillを利用するのがお勧めです。 PublicKeyCredential.parseCreationOptionsFromJSON の代わりに、 parseCreationOptionsFromJSON が、 naviagator.credentials.create の代わりに create が提供されています。

例 7: github/@webauthn-jsonを利用したクレデンシャルの作成
import {
  create,
  parseCreationOptionsFromJSON,
} from "@github/webauthn-json/browser-ponyfill";

const response = await fetch("<endpoint path that returns PublicKeyCredentialCreationOptions as JSON>") //fetch PublicKeyCredentialCreationOptions as JSON string
const publicKeyCredentialCreationOptionsJSON = await response.json() // convert to JSONObject
const credentialCreationOptions = parseCreationOptionsFromJSON(publicKeyCredentialCreationOptionsJSON); // convert to PublicKeyCredentialCreationOptions
const publicKeyCredential = await create({ publicKey: credentialCreationOptions}); // create PublicKeyCredential
const registrationResponseJSON = publicKeyCredential.toJSON() // JSON object of publicKeyCredential
const registrationResponseJSONStr = JSON.stringify(registrationResponseJSON) // JSON string representation of publicKeyCredential

このpony-fillの create メソッドを利用して得られた publicKeyCredentialでは、 toJSON メソッドが利用可能です。

PublicKeyCredential.parseRequestOptionsFromJSON の代わりとしては、 parseRequestOptionsFromJSON が、 naviagator.credentials.get の代わりに get が提供されています。

例 8: github/@webauthn-jsonを利用したクレデンシャルの取得
import {
  get,
  parseRequestOptionsFromJSON,
} from "@github/webauthn-json/browser-ponyfill";

const response = await fetch("<endpoint path that returns PublicKeyCredentialRequestOptions as JSON>");
const publicKeyCredentialRequestOptionsJSON = await response.json();
const credentialGetOptions = parseRequestOptionsFromJSON(publicKeyCredentialRequestOptionsJSON);
const publicKeyCredential = await get({ publicKey: credentialGetOptions});
const authenticationResponseJSON = publicKeyCredential.toJSON()
const authenticationResponseJSONStr = JSON.stringify(authenticationResponseJSON)

4.5. モジュール構成

WebAuthn4Jは、以下のModuleから構成されます。

4.5.1. Core: webauthn4j-core.jar

WebAuthn Attestation/Assertionの検証機能およびコア機能を提供します。

4.5.2. Metadata: webauthn4j-metadata.jar

FIDO Metadata Serviceを用いたTrustAnchorの解決など、追加的な機能を提供します。

4.5.3. Core-Async: webauthn4j-core-async.jar

WebAuthn Attestation/Assertionの検証機能およびコア機能の非同期版を提供します。

4.5.4. Metadata-Async: webauthn4j-metadata-async.jar

FIDO Metadata Serviceを用いたTrustAnchorの解決など、追加的な機能の非同期バージョンを提供します。

4.5.5. App Attest: webauthn4j-appattest.jar

Apple App Attest Attestation/Assertionの検証機能を提供します。

4.5.6. Test: webauthn4j-test.jar

WebAuthn4Jのテストを行うための内部ライブラリです。含まれているクラスは、Publicであっても、セマンティックバージョニングに従わずに 破壊的変更が入る場合があります。

4.5.7. Util: webauthn4j-util.jar

WebAuthn4Jライブラリで使用されるユーティリティクラスをまとめたライブラリです。

4.6. カスタムな検証ロジックの実装

WebAuthn4Jでは、カスタムな検証ロジックを実装し、追加することが可能です。 登録時の検証にカスタムロジックを追加する場合は、 CustomRegistrationVerifier を実装してください。 認証時の検証にカスタムロジックを追加する場合は、 CustomAuthenticationVerifier を実装してください。

4.6.1. カスタム検証ロジックの登録

CustomRegistrationVerifierCustomAuthenticationVerifier の実装は WebAuthnManager のコンストラクタの customRegistrationVerifiers パラメータおよび customAuthenticationVerifiers パラメータを通じて登録することが出来ます。

4.7. カスタムなデータ変換ロジックの実装

WebAuthn4Jでは、JSONやCBORのシリアライズ、デシリアライズ処理にJacksonライブラリを使用しています。 Client ExtensionやAuthenticator Extensionのデータ変換でカスタムな変換を行いたい場合、WebAuthn4Jが内部で使用している Jacksonの ObjectMapper にカスタムなシリアライザ、デシリアライザを登録することで実現できます。

4.7.1. カスタムなデータ変換ロジックの登録

WebAuthn4Jは、Jacksonの ObjectMapperObjectConverter というクラスでラップして使用しており、 カスタムなシリアライザ、デシリアライザを登録した ObjectMapperObjectConverter インスタンス作成時にコンストラクタから インジェクトし、その ObjectConverterWebAuthnManager のインスタンス作成時にパラメータとして指定してください。

4.8. クラス

4.8.1. Data transfer Objects

com.webauthn4j.data パッケージ配下のクラスはイミュータブルなDTOとして設計されています。

4.8.2. Converter, WebAuthnModule

データパッケージ配下のクラスはJacksonによってシリアライズ、デシリアライズ可能なように設計されています。 一部のクラスはカスタムなシリアライザ、デシリアライザが必要であり、 converter パッケージ配下に集約されています。 カスタムシリアライザ、デシリアライザは WebAuthnJSONModuleWebAuthnCBORModule というJacksonのModuleにまとめられています。 WebAuthn4Jは内部で使用するJacksonの ObjectMapper に自動で WebAuthnModule を適用しますが、WebAuthnManager の外部で WebAuthn4Jのシリアライザ、デシリアライザを使用したい場合は、Jacksonの ObjectMapperWebAuthnJSONModuleWebAuthnCBORModule を登録すると 良いでしょう。

4.8.3. TrustAnchorsResolver

TrustAnchorsResolver インタフェースは TrustAnchorCertPathTrustworthinessVerifier で構成証明ステートメントの信頼性の 検証を行う際に信頼するルート証明書のセットを探索するために使用されます。

4.8.4. TrustAnchorsProvider

TrustAnchorsProvider インタフェースは前述の TrustAnchorsResolver インタフェースの実装である TrustAnchorsResolverImpl がTrustAnchorの読込処理を委譲する先のインタフェースです。実装としてJava Key StoreファイルからTrustAnchorを読み込む KeyStoreFileTrustAnchorsProvider クラスが提供されている他、WebAuthn4J Spring Securityでは、SpringのResourceから TrustAnchorを読み込む CertFileResourcesTrustAnchorProvider が提供されています。

4.8.5. 例外クラス

データの変換に失敗した場合、 DataConversionException のサブクラスがスローされます。 データの検証に失敗した場合、 VerificationException のサブクラスがスローされます。

4.9. WebAuthn以外のFIDO CTAP2セキュリティキーを用いた独自アプリケーションでの利用

FIDO CTAP2セキュリティキーにとって、WebAuthnは一つの応用例でしかなく、セキュアな認証を必要とする独自アプリケーションで セキュリティキーを利用することも可能です。本節では、FIDO CTAP2セキュリティキーを用いた独自アプリケーションにおけるAttestation、Assertion検証でWebAuthn4Jを利用する方法を説明します。

4.9.1. FIDO CTAP2セキュリティキーを用いた独自アプリケーションでの登録、認証のフロー

FIDO CTAP2セキュリティキーを独自アプリケーションで認証に使用する場合、セキュリティキーを登録するために、 アプリからFIDO CTAP2セキュリティキーの authenticatorMakeCredential メソッドを呼び出し、公開鍵やデバイスの構成情報を 含むデータ(構成証明、Attestation)を取得し保存します。 取得されたAttestationは、セキュリティキーがアプリとして受け入れ可能なキーか判定するために検証が必要です。 WebAuthn4Jでは、 CoreRegistrationVerifier クラスを用いることで、取得されたAttestationを検証可能です。

認証時には、同様にアプリからFIDO CTAP2セキュリティキーの authenticatorGetAssertion メソッドを呼び出し、認証時にサーバーに送信される署名を含んだデータ(アサーション、Assertion)を取得します。 取得されたAssertionを検証することで、アプリは認証に用いられたセキュリティキーが、登録時に用いられたセキュリティキーと同一であることを確認し、正当なアクセスか判定することが可能となります。WebAuthn4Jでは、 CoreAuthenticationVerifier クラスを用いることで、取得されたAssertionを検証可能です。

4.9.2. アプリケーション固有のクライアントデータの真正性の担保、検証

上記のフローに従って実装することで、FIDO CTAP2セキュリティキーを用いた安全な認証が実現可能ですが、 FIDO CTAP2セキュリティキーを呼び出す主体(クライアント)と、Attestation、Assertionを検証する主体(サーバー)が分離している場合、クライアントが登録、認証時にアプリケーション固有のクライアントデータを生成し、クライアントデータを追加でサーバーで検証したい場合もあります。クライアントデータ自体はAttestation、Assertionと一緒に送信すれば良いですが、 クライアントデータを中間者攻撃から防御するために、クライアントデータに対して署名を行い、保護する必要があります。

さて、FIDO CTAP2では、登録時に利用する authenticatorMakeCredential メソッドと認証時に利用する authenticatorGetAssertion メソッド 、どちらにも共通するパラメータとして、clientDataHash というパラメータが存在します。セキュリティキーは、受け取った clientDataHash パラメータを署名対象のデータの一部として署名を生成するため、アプリケーションとして署名で保護したいクライアントデータのハッシュを取得し、 clientDataHash にセットすることで、アプリケーション固有のクライアントデータが改竄されていない真正なデータか、サーバー側で検証することが出来ます。

5. FAQ

5.1. WebAuthnManagerクラス、責務が多すぎでは?

Q: WebAuthnManager は、登録処理と認証処理という異なる2つの機能を提供しており、 認証処理だけを必要とするクラスから呼び出す場合も、WebAuthnManager のインスタンス化時に登録処理だけで必要なAttestation関連の設定が必要であり、不便です。

A: WebAuthnManager を分割した WebAuthnRegistrationManagerWebAuthnAuthenticationManager を提供していますのでそちらをご利用下さい。

6. 既知の問題

6.1. PS256、PS384、PS512のサポート

WebAuthn4JはPS256、PS384、PS512アルゴリズムをサポートしていません。 PS256、PS384、PS512アルゴリズムはWebAuthn仕様でオプションとされており、当面サポートする予定はありません。