1. 詳細
1.1. クレデンシャル情報の表現
クレデンシャル情報を表現するインタフェースとして、 CredentialRecord
インタフェースが存在します。 登録時に、 RegistrationData
クラスが含む値を用いて CredentialRecord
インタフェースの インスタンスを作成し、アプリケーションの作法に則り永続化してください。認証時の検証に必要となります。 なお、永続化する際は、検索する際の利便性を考え、credentialIdをキーに永続化すると良いでしょう。
CredentialRecord
インタフェースの実装クラスを設計する際は、アプリケーションの要件に合わせて拡張すると良いでしょう。 典型的には、 CredentialRecord
をユーザーが識別するための名前フィールドの追加などが考えられるでしょう。
1.2. CredentialRecordのシリアライズ、デシリアライズ
認証デバイスの登録時、CredentialRecord
のインスタンスをデータベース等に永続化し、認証時に利用できるようにするのはアプリケーションの責務ですが、
CredentialRecord
を構成する各メンバをシリアライズ、デシリアライズする際に使用できるクラスをWebAuthn4Jでは用意しています。 アプリケーションで永続化を実装する際の補助としてご利用ください。
1.2.1. attestedCredentialData
AttestedCredentialDataConverter
で AttestedCredentialData
を byte[]
に変換したり、その逆の変換を行うことが出来ます。 なお、String
として保存したい場合、さらに Base64UrlUtil
を用いることで byte[]
から Base64Url String
に変換することが出来ます。
AttestedCredentialDataConverter attestedCredentialDataConverter = new AttestedCredentialDataConverter(objectConverter);
// serialize
byte[] serialized = attestedCredentialDataConverter.convert(attestedCredentialData);
// deserialize
AttestedCredentialData deserialized = attestedCredentialDataConverter.convert(serialized);
1.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;
}
}
1.2.3. transports
JSON文字列として保存したい場合は ObjectConverter
が使用できます。
String serializedTransports = objectConverter.getJsonConverter().writeValueAsString(transports);
1.3. DCAppleDeviceのシリアライズ、デシリアライズ
webauthn4j-appattestでは、CredentialRecord
インタフェースの代わりに、DCAppleDevice
インタフェースを実装したクラスを、構成証明の検証時とアサーションの検証時の間で永続化する必要があります。 概ねCredentialRecordのシリアライズ、デシリアライズで解説した方法でシリアライズ、デシリアライズが可能ですが、一点気を付ける必要がある点として、 webauthn4j-appattest独自のクラス(例えば AppleAppAttestAttestationStatement
)のシリアライズ、デシリアライズを行う為に、
ObjectConverter
は DeviceCheckCBORModule
が登録されたものを使用する必要があります。
DeviceCheckCBORModule
が登録された ObjectConverter
は DeviceCheckManager#createObjectConverter
で得ることが出来ます。
1.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
が提供されています。
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
が提供されています。
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)
1.5. モジュール構成
WebAuthn4Jは、以下のModuleから構成されます。
1.5.4. Metadata-Async: webauthn4j-metadata-async.jar
FIDO Metadata Serviceを用いたTrustAnchorの解決など、追加的な機能の非同期バージョンを提供します。
1.6. カスタムな検証ロジックの実装
WebAuthn4Jでは、カスタムな検証ロジックを実装し、追加することが可能です。 登録時の検証にカスタムロジックを追加する場合は、 CustomRegistrationVerifier
を実装してください。 認証時の検証にカスタムロジックを追加する場合は、 CustomAuthenticationVerifier
を実装してください。
1.7. カスタムなデータ変換ロジックの実装
WebAuthn4Jでは、JSONやCBORのシリアライズ、デシリアライズ処理にJacksonライブラリを使用しています。 Client ExtensionやAuthenticator Extensionのデータ変換でカスタムな変換を行いたい場合、WebAuthn4Jが内部で使用している Jacksonの ObjectMapper
にカスタムなシリアライザ、デシリアライザを登録することで実現できます。
1.8. クラス
1.8.2. Converter, WebAuthnModule
データパッケージ配下のクラスはJacksonによってシリアライズ、デシリアライズ可能なように設計されています。 一部のクラスはカスタムなシリアライザ、デシリアライザが必要であり、 converter
パッケージ配下に集約されています。 カスタムシリアライザ、デシリアライザは WebAuthnJSONModule
と WebAuthnCBORModule
というJacksonのModuleにまとめられています。 WebAuthn4Jは内部で使用するJacksonの ObjectMapper
に自動で WebAuthnModule
を適用しますが、WebAuthnManager
の外部で WebAuthn4Jのシリアライザ、デシリアライザを使用したい場合は、Jacksonの ObjectMapper
に WebAuthnJSONModule
と WebAuthnCBORModule
を登録すると 良いでしょう。
1.8.3. TrustAnchorsResolver
TrustAnchorsResolver
インタフェースは TrustAnchorCertPathTrustworthinessVerifier
で構成証明ステートメントの信頼性の 検証を行う際に信頼するルート証明書のセットを探索するために使用されます。
1.8.4. TrustAnchorsProvider
TrustAnchorsProvider
インタフェースは前述の TrustAnchorsResolver
インタフェースの実装である TrustAnchorsResolverImpl
がTrustAnchorの読込処理を委譲する先のインタフェースです。実装としてJava Key StoreファイルからTrustAnchorを読み込む
KeyStoreFileTrustAnchorsProvider
クラスが提供されている他、WebAuthn4J Spring Securityでは、SpringのResourceから TrustAnchorを読み込む CertFileResourcesTrustAnchorProvider
が提供されています。
1.9. WebAuthn以外のFIDO CTAP2セキュリティキーを用いた独自アプリケーションでの利用
FIDO CTAP2セキュリティキーにとって、WebAuthnは一つの応用例でしかなく、セキュアな認証を必要とする独自アプリケーションで セキュリティキーを利用することも可能です。本節では、FIDO CTAP2セキュリティキーを用いた独自アプリケーションにおけるAttestation、Assertion検証でWebAuthn4Jを利用する方法を説明します。
1.9.1. FIDO CTAP2セキュリティキーを用いた独自アプリケーションでの登録、認証のフロー
FIDO CTAP2セキュリティキーを独自アプリケーションで認証に使用する場合、セキュリティキーを登録するために、 アプリからFIDO CTAP2セキュリティキーの authenticatorMakeCredential メソッドを呼び出し、公開鍵やデバイスの構成情報を 含むデータ(構成証明、Attestation)を取得し保存します。 取得されたAttestationは、セキュリティキーがアプリとして受け入れ可能なキーか判定するために検証が必要です。 WebAuthn4Jでは、 CoreRegistrationVerifier
クラスを用いることで、取得されたAttestationを検証可能です。
認証時には、同様にアプリからFIDO CTAP2セキュリティキーの authenticatorGetAssertion メソッドを呼び出し、認証時にサーバーに送信される署名を含んだデータ(アサーション、Assertion)を取得します。 取得されたAssertionを検証することで、アプリは認証に用いられたセキュリティキーが、登録時に用いられたセキュリティキーと同一であることを確認し、正当なアクセスか判定することが可能となります。WebAuthn4Jでは、 CoreAuthenticationVerifier
クラスを用いることで、取得されたAssertionを検証可能です。
1.9.2. アプリケーション固有のクライアントデータの真正性の担保、検証
上記のフローに従って実装することで、FIDO CTAP2セキュリティキーを用いた安全な認証が実現可能ですが、 FIDO CTAP2セキュリティキーを呼び出す主体(クライアント)と、Attestation、Assertionを検証する主体(サーバー)が分離している場合、クライアントが登録、認証時にアプリケーション固有のクライアントデータを生成し、クライアントデータを追加でサーバーで検証したい場合もあります。クライアントデータ自体はAttestation、Assertionと一緒に送信すれば良いですが、 クライアントデータを中間者攻撃から防御するために、クライアントデータに対して署名を行い、保護する必要があります。
さて、FIDO CTAP2では、登録時に利用する authenticatorMakeCredential メソッドと認証時に利用する authenticatorGetAssertion メソッド 、どちらにも共通するパラメータとして、clientDataHash
というパラメータが存在します。セキュリティキーは、受け取った clientDataHash
パラメータを署名対象のデータの一部として署名を生成するため、アプリケーションとして署名で保護したいクライアントデータのハッシュを取得し、
clientDataHash
にセットすることで、アプリケーション固有のクライアントデータが改竄されていない真正なデータか、サーバー側で検証することが出来ます。