Skip to main content

はじめに

本記事では OpenID4VC High Assurance Interoperability Profile 1.0 (以降 HAIP(ハイプ)) に準拠する Verifiable Credential (以降 VC) 発行手順について解説します。

仕様の要点

VC 発行手順に関しては、HAIP は概ね OpenID for Verifiable Credential Issuance 1.0 (以降 OID4VCI) と FAPI 2.0 Security Profile (以降 FAPI2SP) を組み合わせたものだと言えます。 ただし、留意すべき差分があるので、主要なものについて紹介します。

送信者限定アクセストークン

FAPI2SP では、送信者限定アクセストークンを実現する方法として次のものが利用可能です。 一方、HAIP で許可されるのは DPoP のみです。

クライアント認証

FAPI2SP では、クライアント認証方式として次のものが利用可能です。 HAIP では、これらに加えて OAuth 2.0 Attestation-Based Client Authentication (以降 ABCA) も利用可能です。
  • attest_jwt_client_auth (ABCA)
なお、HAIP の文脈で ABCA を利用する場合、クライアント・アテステーションx5c ヘッダパラメーターを含まなければなりません。 また、署名検証用公開鍵を含む X.509 証明書 (証明書チェーンの先頭の証明書) は、自己署名証明書であってはなりません。
HAIP 仕様を字句通りに解釈すると、ABCA 以外のクライアント認証方式も利用可能です。 しかし、HAIP 仕様の議論は ABCA の利用を前提として進められているように見受けられので、HAIP 仕様がその考えを反映するように変更される (「ABCA のみを許可する」と仕様変更される) 可能性があるため、本記事ではクライアント認証として ABCA を使います。

キー・アテステーション

OID4VCI は、クレデンシャルリクエストに含める鍵証明のフォーマットを幾つか定義しています。 これらのうち、jwt Proof Type (OID4VCI Appendix F.1) では、key_attestation ヘッダパラメーターにキー・アテステーション (OID4VCI Appendix D) を指定することができます。 また、attestation Proof Type (OID4VCI Appendix F.3) では、キー・アテステーションそのものを鍵証明として指定します。 HAIP の文脈でキー・アテステーションを用いる場合、それらは x5c ヘッダパラメーターを含まなければなりません。 また、署名検証用公開鍵を含む X.509 証明書 (証明書チェーンの先頭の証明書) は、自己署名証明書であってはなりません。

スコープ

一般的に、アクセストークンは一つ以上のスコープと紐付いています。 HAIP の文脈では、それらのスコープの中に、特定のクレデンシャル設定を指すものが含まれていなければなりません。 具体的には、クレデンシャル・イシュア・メタデータの credential_configurations_supported に列挙されているクレデンシャル設定群の、どれか一つ以上のクレデンシャル設定の scope プロパティーの値が、アクセストークンに紐付いている必要があります。

手順の概要

認可コードフローによる VC 発行手順の概要は下記の通りです。
  1. PAR リクエスト
  2. 認可リクエスト
  3. トークンリクエスト
  4. クレデンシャルリクエスト
しかし、それぞれのリクエストが様々なトークン群を要求するため、実際の手順はより複雑になります。 下記は、トークン群の生成も含めた手順の概要を示しています。
  1. PAR リクエスト
    • PKCE トークン (コード・ベリファイアとコード・チャレンジ) 生成 (PKCE)
    • クライアント・アテステーション生成 (ABCA)
    • アテステーション・チャレンジ取得 (ABCA)
    • クライアント・アテステーション・PoP 生成 (ABCA)
    • DPoP Proof JWT 生成 (DPoP)
    • PAR リクエスト送信 (PAR)
  2. 認可リクエスト
    • 認可リクエスト送信
  3. トークンリクエスト
    • クライアント・アテステーション生成 (ABCA) (再利用可)
    • アテステーション・チャレンジ取得 (ABCA) (再利用可)
    • クライアント・アテステーション・PoP 生成 (ABCA) (再利用可)
    • DPoP Proof JWT 生成 (DPoP)
    • トークンリクエスト送信
  4. クレデンシャルリクエスト
    • ノンス取得 (OID4VCI)
    • キー・アテステーション生成 (OID4VCI)
    • 鍵証明生成 (OID4VCI)
    • DPoP Proof JWT 生成 (DPoP)
    • クレデンシャルリクエスト送信 (OID4VCI)

実際の手順

このセクションでは、実際の手順をみていきます。 なお、トークン群の生成に用いるスクリプト群や秘密鍵・公開鍵・証明書については、authlete/oid4vci-demo で公開しているものを利用します。

PAR リクエスト

HAIP は FAPI2SP をベースとしており、FAPI2SP が PAR を必須としているため、HAIP 準拠の認可リクエストでも PAR の利用が必須となります。 ここでは、PAR エンドポイントで認可リクエストを登録し、リクエスト URI を取得します。

PKCE トークン生成

HAIP は FAPI2SP をベースとしており、FAPI2SP が PKCE を必須としているため、HAIP 準拠の認可リクエストにはコード・チャレンジ、トークンリクエストにコード・ベリファイアを含めなければなりません。 そのため、pkce スクリプトを用いてそれらを用意します。
実行結果:
シェル組込コマンド eval を次のように用いると、pkce スクリプトの出力結果をそのままシェル変数に代入することができます。

クライアント・アテステーション生成

クライアント・アテステーションは generate-client-attestation スクリプトを用いて生成することができます。 なお、HAIP では x5c ヘッダパラメーターが必須なので、--x5c オプションを用いて x5c ヘッダパラメーターに列挙する X.509 証明書を指定する必要があるので注意してください。 --x5c オプションは複数回指定可能で、指定された順番で X.509 証明書を x5c ヘッダパラメーターに追加していきます。
下記は、生成されるクライアント・アテステーションのヘッダとペイロードの例です。
次の点に注目してください。
  • typ ヘッダパラメーターの値が oauth-client-attestation+jwt である。
  • x5c ヘッダパラメーターに署名検証用公開鍵の X.509 証明書 (--x5c オプションで指定したもの) が含まれている。
  • sub クレームにクライアント識別子 (--client-id オプションで指定したもの) が設定されている。
  • cnf.jwk クレームにクライアントの鍵 (--client-key オプションで指定したもの) が設定されている。

アテステーション・チャレンジ取得

ABCA 仕様によれば、認可サーバーがチャレンジエンドポイントを提供している場合、そのエンドポイントから発行されるアテステーション・チャレンジをクライアント・アテステーション・PoP に埋め込む必要があります。 認可サーバーがチャレンジエンドポイントを提供しているかどうかは、サーバーメタデータに challenge_endpoint パラメーターが含まれているかどうかで判定することができます。 チャレンジエンドポイントは、HTTP POST リクエストを受け、attestation_challenge プロパティを含む JSON を返します。 下記は ABCA 仕様から抜粋したリクエストとレスポンスの例です。 アテステーション・チャレンジ・リクエストの例
アテステーション・チャレンジ・レスポンスの例
CHALLENGE_ENDPOINT というシェル変数にチャレンジエンドポイントの URL が入っている場合、次のコマンドを実行することでアテステーション・チャレンジの値を CHALLENGE というシェル変数に代入することができます。

クライアント・アテステーション・PoP 生成

クライアント・アテステーション・PoP は generate-client-attestation-pop スクリプトを用いて生成することができます。 challenge クレームを含める場合は --challenge オプションを指定してください。
下記は、生成されるクライアント・アテステーション・PoP のヘッダとペイロードの例です。
次の点に注目してください。
  • typ ヘッダパラメーターの値が oauth-client-attestation-pop+jwt である。
  • aud クレームに認可サーバー識別子 (--as-id オプションで指定したもの) が設定されている。
  • challenge クレームにアテステーション・チャレンジ (--challenge オプションで指定したもの) が設定されている。

DPoP Proof JWT 生成

FAPI2SP に次のように書かれているため、認可コードも DPoP-bound にする必要があります。
if using DPoP, shall support “Authorization Code Binding to DPoP Key” (as required by Section 10.1 of RFC9449);
これを実現する方法は、PAR エンドポイントで登録するリクエストに dpop_jkt リクエストパラメーターを加えるか、もしくは DPoP Proof JWT を加えるか、のどちらかになります。 仕様書にも書かれているように DPoP Proof JWT のほうが実装が単純になるので (リクエストの種類に関わらずクライアントは認可サーバーに送るリクエストに常に DPoP Proof JWT を含めるという実装にすればよいので)、ここでは DPoP Proof JWT を生成することにします。 generate-dpop-proof スクリプトに、PAR リクエストの HTTP メソッド (POST 固定) を指定する -m POST オプションと、PAR エンドポイントの URL を表す -u $PAR_ENDPOINT オプション、クライアントの鍵を表す -k client.jwk オプションを渡して DPoP Proof JWT を生成します。
下記は、生成される DPoP Proof JWT のヘッダとペイロードの例です。
次の点に注目してください。
  • typ ヘッダパラメーターの値が dpop+jwt である。
  • jwk ヘッダパラメーターにクライアントの鍵 (-k オプションで指定したもの) が設定されている。
  • htm クレームに PAR リクエストの HTTP メソッド (-m オプションで指定したもの) が設定されている。
  • htu クレームに PAR エンドポイントの URL (-u オプションで指定したもの) が設定されている。
認可サーバーによっては、DPoP Proof JWT が nonce クレームを含むことを要求します。 その場合は -n オプションをつけて DPoP Proof JWT を生成してください。 DPoP Nonce の詳細については『DPoP Nonce』を参照してください。

PAR リクエスト送信

トークン群の用意ができたので、PAR リクエストを送信します。
このリクエストの要点は次の通りです。 PAR リクエストが成功すると、PAR エンドポイントからは request_uri プロパティーを含む JSON が返されます。 下記は PAR 仕様から抜粋した PAR レスポンスの例です。
request_uri プロパティーの値は、発行されたリクエスト URI です。 このリクエスト URI は、後ほど認可リクエストの request_uri リクエストパラメーターの値として用います。
FAPI2SP の要請により、リクエスト URI の有効時間は 600 秒未満となります。

認可リクエスト

ブラウザ経由で、認可サーバーの認可エンドポイントに認可リクエストを投げます。 その際、PAR エンドポイントから発行されたリクエスト URI を request_uri リクエストパラメーターの値として用います。

認可エンドポイントから返される認可ページでユーザー認証と権限付与同意をおこなうと、認可コードが発行されます。 この認可コードは、後ほどトークンリクエストの code リクエストパラメーターの値として用います。
FAPI2SP の要請により、認可コードの有効時間は最長で 60 秒となります。 すぐに有効期限が切れるので、手作業でトークンリクエストを投げる場合は注意してください。

トークンリクエスト

トークン群生成

トークンリクエストにおいても、クライアント・アテステーションとクライアント・アテステーション・PoP が必要となりますが、PAR リクエスト用に作成したものがまだ有効期限切れしていなければ、再利用可能です。 一方、DPoP Proof JWT は、htu クレームにエンドポイントの URL を設定する必要があるため、PAR リクエスト用に作成したものを再利用することはできません。 -u オプションにトークンエンドポイントの URL を指定して generate-dpop-proof スクリプトを再実行し、DPoP Proof JWT を再生成してください。

トークンリクエスト送信

トークン群を用意後、トークンリクエストを送信します。
このリクエストの要点は次の通りです。 トークンリクエストが成功すると、トークンエンドポイントからは access_token プロパティーを含む JSON が返されます。
access_token プロパティーの値は、発行されたアクセストークンです。 このアクセストークンは、後ほどクレデンシャルリクエストの Authorization HTTP ヘッダに設定します。

クレデンシャルリクエスト

HAIP 仕様を字句通りに解釈すると、必ずしもクレデンシャルリクエストに鍵証明を含める必要はありません。 しかし、現実のユースケースにおいてキー・バインディングを行わないとは考えにくいので、ここで示すクレデンシャルリクエストの例には鍵証明を含めます。

ノンス取得

クレデンシャル・イシュアがノンスエンドポイントを提供している場合、鍵証明やキー・アテステーションにはそのノンスエンドポイントが発行するノンスを含めなければなりません。 クレデンシャル・イシュアがノンスエンドポイントを提供しているかどうかは、クレデンシャル・イシュアのメタデータに nonce_endpoint パラメーターが含まれているかどうかで判定することができます。 ノンスエンドポイントは、HTTP POST リクエストを受け、c_nonce プロパティを含む JSON を返します。下記は OID4VCI 仕様から抜粋したリクエストとレスポンスの例です。 ノンスリクエストの例
ノンスレスポンスの例
NONCE_ENDPOINT というシェル変数にノンスエンドポイントの URL が入っている場合、次のコマンドを実行することでノンスの値を NONCE というシェル変数に代入することができます。

キー・アテステーション生成

キー・アテステーションは generate-key-attestation スクリプトで生成することができます。 なお、HAIP では x5c ヘッダパラメーターが必須なので、--x5c オプションを用いて x5c ヘッダパラメーターに列挙する X.509 証明書を指定する必要があるので注意してください。 --x5c オプションは複数回指定可能で、指定された順番で X.509 証明書を x5c ヘッダパラメーターに追加していきます。
下記は、生成されるキー・アテステーションのヘッダとペイロードの例です。
次の点に注目してください。
  • typ ヘッダパラメーターの値が key-attestation+jwt である。
  • x5c ヘッダパラメーターに署名検証用公開鍵の X.509 証明書 (--x5c オプションで指定したもの) が含まれている。
  • attested_keys クレームにアテストされる鍵 (--attested-key オプションで指定したもの) が含まれている。
  • nonce クレームにノンス (--nonce オプションで指定したもの) が設定されている。

鍵証明生成

jwt Proof Type の鍵証明は generate-key-proof スクリプトで生成することができます。
下記は、生成される鍵証明のヘッダとペイロードの例です。
次の点に注目してください。
  • typ ヘッダパラメーターの値が openid4vci-proof+jwt である。
  • jwk ヘッダパラメーターにクライアントの鍵 (--key オプションで指定したもの) が設定されている。
  • key_attestation ヘッダパラメーターにキー・アテステーション (--key-attestation オプションで指定したもの) が設定されている。
  • iss クレームにクライアント識別子 (--client-id オプションで指定したもの) が設定されている。
  • aud クレームにクレデンシャル・イシュア識別子 (--issuer オプションで指定したもの) が設定されている。
  • nonce クレームにノンス (--nonce オプションで指定したもの) が設定されている。

DPoP Proof JWT 生成

-u オプションにクレデンシャルエンドポイントの URL を指定して generate-dpop-proof スクリプトを再実行し、DPoP Proof JWT を再生成します。 なお、クレデンシャルリクエストでは DPoP Proof JWT とアクセストークンを一緒に送信することになるので、DPoP Proof JWT には ath クレームを含めなければなりません。 このため、generate-dpop-proof スクリプトを実行する際、-a オプションを追加してください。

クレデンシャルリクエスト送信

鍵証明が用意できたので、クレデンシャルリクエストを送信します。 ここでは、digital_credential スコープが指すクレデンシャル設定の識別子が DigitalCredential であると想定しています。
このリクエストの要点は次の通りです。 上記のリクエストでは jwt Proof Type の鍵証明を用いたため、キー・アテステーションを別の JWT に埋め込みました。 一方で、attestation Proof Type の鍵証明であれば、次のようにキー・アテステーションを直接鍵証明として用いることができます。
下記はクレデンシャルレスポンスの例です。
credentials 配列の各要素は JSON オブジェクトであり、それらの credential プロパティの値が発行された VC を表しています。 この例では発行された VC は一つのみで、そのフォーマットは SD-JWT VC となっています。

SD-JWT VC

発行された SD-JWT VC の Issuer-signed JWT 部のヘッダとペイロードをデコードすると次のようになります。
次の点に注目してください。
  • typ ヘッダパラメーターの値が dc+sd-jwt である。
  • _sd クレームにディスクロージャ群の SHA-256 ダイジェスト値が列挙されている (ただし偽のダイジェスト値も含む)。
  • _sd_alg クレームがディスクロージャ群のダイジェスト値の計算に用いたハッシュアルゴリズムを示している。
  • SD-JWT VC では必須の vct クレームが含まれている。
  • cnf.jwk クレームに、鍵証明で指定されたクライアント鍵が設定されている。
下記はディスクロージャ群の情報です。

証明書チェーン検証

HAIP では、クライアント・アテステーションとキー・アテステーションは x5c ヘッダパラメーターを含んでいなければなりません。 そのようなアテステーションを受け取った認可サーバーやクレデンシャル・イシュアは、x5c ヘッダパラメーターで指定される証明書チェーンが信頼するルート証明書群のいずれかに繋がることを確認します。 ただし、この確認作業の際に用いるルート証明書群については、HAIP には指定がありません。 とはいえ、HAIP の策定作業が EUDI Wallet を想定して進められていることを鑑みると、HAIP 運用では EU の規制当局が指定するルート証明書を用いることを要求されるだろうことは容易に想像がつきます。 このような状況下では、汎用的な HAIP 実装は、検証に用いるルート証明書群を設定する機能を用意しておくのがよいでしょう。 そういうわけで、Authlete も次のようなプロパティ群をサービスに新設しました。 下記は、クライアント・アテステーションとキー・アテステーションの証明書チェーンの検証に用いるルート証明書群の設定例です。

HAIP 有効化

認可サーバーやクレデンシャル・イシュアへのリクエストに対して HAIP バリデーションを実行するかどうかを決める方法は、実装依存です。 Authlete は次の三つの方法を提供しています。 haipVersion プロパティーや haip 属性に設定することが可能な値は、現時点 (2026 年 4 月) の Authlete の実装では 1.0 (文字列) のみです。

おわりに

HAIP は VC 関連仕様の相互運用性とセキュリティを高めるためのプロファイルです。 この文書で紹介した HAIP 関連の機能は、Authlete バージョン 3.0.31 以降で利用可能です。 詳細についてはコンタクトフォームからお問い合わせください。