はじめに
本記事では 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)
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 発行手順の概要は下記の通りです。- PAR リクエスト
- 認可リクエスト
- トークンリクエスト
- クレデンシャルリクエスト
- PAR リクエスト
- 認可リクエスト
- 認可リクエスト送信
- トークンリクエスト
- クレデンシャルリクエスト
実際の手順
このセクションでは、実際の手順をみていきます。 なお、トークン群の生成に用いるスクリプト群や秘密鍵・公開鍵・証明書については、authlete/oid4vci-demo で公開しているものを利用します。PAR リクエスト

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 オプションを指定してください。
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 を生成します。
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 ヘッダに設定します。
クレデンシャルリクエスト

ノンス取得
クレデンシャル・イシュアがノンスエンドポイントを提供している場合、鍵証明やキー・アテステーションにはそのノンスエンドポイントが発行するノンスを含めなければなりません。 クレデンシャル・イシュアがノンスエンドポイントを提供しているかどうかは、クレデンシャル・イシュアのメタデータに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 (文字列) のみです。