本文へスキップ
ecdsa.com
Java · Kotlin

WebAuthn4J

WebAuthn4J は、Spring Security のパスキー対応と Keycloak の基盤となっている WebAuthn 検証ライブラリです。ユーザー検証に隠れたデフォルト値はなく、署名は DER のみを受け付け、カウンターが巻き戻ると必ずエラーになります。
ライセンス
Apache-2.0
確認したバージョン
0.31.11.RELEASE ·
ソースコード
github.com/webauthn4j/webauthn4j
ドキュメント
webauthn4j.github.io/webauthn4j/en/

互換性の概要

WebAuthn

WebAuthn4J は、登録と認証のレスポンス(あらゆる形式のアテステーションステートメント、COSE 鍵、フラグ、カウンター、署名)を検証し、オプションの構築はリライングパーティに任せます。Spring Security と Keycloak は内部でこのライブラリを使っています。0.31.11 は Jackson 3(tools.jackson.*)に依存しているため、アプリケーション自身の JSON 処理にまだ Jackson 2 の com.fasterxml.jackson を使っている場合は注意が必要です。また、API には JetBrains の @NotNull アノテーションが付いており、単一ファイルの java ランチャーでコンパイルすると、org.jetbrains:annotations をクラスパスに追加するまで失敗しました。

設定し忘れるようなユーザー検証のデフォルトポリシーはありません。RegistrationParameters と AuthenticationParameters は、userVerificationRequired と userPresenceRequired を必須の引数として受け取ります。チェックでは、UV フラグが立っていない登録とアサーションは、false を渡すと検証を通り、true を渡すと UserNotVerifiedException で失敗しました。サンプルのように、この値はオプションで送る userVerification と一致させてください。

署名は ASN.1 DER でなければなりません。64 バイトの生の r‖s 形式のアサーション署名は BadSignatureException: Assertion signature is not valid. で失敗し、high-S の DER 署名は検証を通りました。保存済みの値より小さいカウンターは MaliciousCounterValueException: Malicious counter value is detected. Cloned authenticators exist in parallel. でログインを失敗させます。一方、同期パスキーが報告するように常に 0 のままのカウンターは通過します。Ed25519 の認証情報は、登録から認証まで一通り検証できました。

createNonStrictWebAuthnManager() は、どのアテステーションステートメントもトラストアンカーと照合せずに受け入れます。アテステーション none を要求する場合はこれで正しいのですが、アテステーションをポリシーで判断する場合は誤りです。その場合はトラストアンカーを設定し、厳格なマネージャーを使ってください。

WebAuthn インスペクターはブラウザーが送信する内容をデコードし、AAGUID 検索はアテステーションポリシーのために認証器の機種を特定します。

事実とその根拠となるチェック

以下の内容はドキュメントからの転記ではありません。2026年10月20日に、チェック content/libraries/verify/webauthn4j/run.sh がコンテナイメージ maven:3.9.16-eclipse-temurin-25 内で WebAuthn4J 0.31.11.RELEASE を実行しました。このページの内容はその出力と一致していなければならず、各事実には該当する出力行を示しています。

  • ユーザー検証の引数

    mandatory

    userVerificationRequired と userPresenceRequired はコンストラクターの必須引数です。false では UV フラグのないレスポンスが検証を通り、true では UserNotVerifiedException で失敗しました。

    チェックによる記録
    fact uv-requirement-argument = mandatory
    evidence uv-requirement-argument: registration with UV clear, userVerificationRequired false: verified; true: UserNotVerifiedException: Verifier is configured to check user verified, but UV flag in authenticatorData is not set.
    evidence uv-requirement-argument: RegistrationParameters and AuthenticationParameters take userVerificationRequired and userPresenceRequired as required booleans
  • アサーション署名のエンコーディング

    der

    DER 署名を含む正しい ES256 アサーションは検証を通りました。

    チェックによる記録
    fact webauthn-assertion-encoding = der
  • 生の r‖s 形式のアサーション署名

    rejected

    64 バイトの生の r‖s 署名は BadSignatureException: Assertion signature is not valid. で失敗しました。

    チェックによる記録
    fact webauthn-raw-signature = rejected
  • high-S のアサーション署名

    accepted

    s を n − s に置き換えた DER 署名も検証を通りました。

    チェックによる記録
    fact webauthn-high-s = accepted
  • 署名カウンターの巻き戻り

    rejected

    保存済みの値が 5 のときにカウンター 3 を送ると MaliciousCounterValueException で失敗し、保存済みの値が 0 のときのカウンター 0 は検証を通りました。

    チェックによる記録
    fact webauthn-counter-regression = rejected
  • Ed25519 の認証情報

    supported

    Ed25519(COSE -8)の認証情報で、登録と認証に成功しました。

    チェックによる記録
    fact webauthn-ed25519 = supported
    evidence webauthn-ed25519: verified

使い方

検証マネージャーと、ユーザー検証とユーザー存在確認の要件を明示した登録パラメーターを作成します。
import com.webauthn4j.WebAuthnManager;
import com.webauthn4j.data.RegistrationParameters;
import com.webauthn4j.data.PublicKeyCredentialParameters;
import com.webauthn4j.data.PublicKeyCredentialType;
import com.webauthn4j.data.attestation.statement.COSEAlgorithmIdentifier;
import com.webauthn4j.data.client.Origin;
import com.webauthn4j.data.client.challenge.DefaultChallenge;
import com.webauthn4j.server.ServerProperty;
import java.util.List;

public class Usage {
    public static void main(String[] args) {
        // "Non-strict" accepts every attestation format without trust anchors; pair it
        // with attestation "none" in the options, or configure trust anchors instead.
        WebAuthnManager manager = WebAuthnManager.createNonStrictWebAuthnManager();
        ServerProperty server = new ServerProperty(new Origin("https://example.com"), "example.com", new DefaultChallenge());

        // There is no default: every verification states UV and UP requirements explicitly.
        RegistrationParameters registration = new RegistrationParameters(server,
            List.of(new PublicKeyCredentialParameters(PublicKeyCredentialType.PUBLIC_KEY, COSEAlgorithmIdentifier.ES256)),
            true, true);
        // manager.verify(manager.parseRegistrationResponseJSON(json), registration) does the rest.
        System.out.println("uv required: " + registration.isUserVerificationRequired()
            + ", up required: " + registration.isUserPresenceRequired() + ", manager: " + manager.getClass().getSimpleName());
    }
}

スニペットのテストハーネスは、すべてのサンプルをコンテナイメージ maven:3.9.16-eclipse-temurin-25 内でそのまま実行し、出力が一致しなければ実行は失敗します。

セットアップ
mvn -q -B dependency:copy-dependencies -DoutputDirectory=lib >/dev/null
コマンド
java -cp 'lib/*' Usage.java
一致すべき出力(正規表現)
uv required: true, up required: true, manager: WebAuthnManager

ライブラリ