Wallet機能のセットアップと使用方法
このチュートリアルは、VCKnots の wallet ライブラリ(Go ライブラリ)のセットアップ、Credential の受領と提示のサンプル実装、本番環境での利用に向けた考慮事項を説明します。
wallet は OpenID for Verifiable Credentials の各仕様を実装しています。
- Credential の受領(OID4VCI): Credential Offer と pre-authorized code フローを使って、Issuer から Credential を受け取ります。
- Credential の提示(OID4VP):
openid4vp:形式の Authorization Request に応答し、Verifiable Presentation を Verifier に送信します。
受領と提示のどちらも JWT-VC(application/vc+jwt)と SD-JWT VC(application/dc+sd-jwt)に対応しています。
SD-JWT VC では選択的開示と Key Binding JWT も利用できます。
1. 前提条件
- 対応している仕様:
- 受領: OpenID for Verifiable Credential Issuance 1.0(Pre-Authorized Code フロー)
- 提示: OpenID for Verifiable Presentations - draft 24(クロスデバイスフロー、
response_mode=direct_post) - 各機能の実装範囲の詳細は VC Knots Coverage を参照してください。
1-1. Go環境の要件
- Goのバージョン: vcknots/wallet ライブラリは、
wallet/mise.tomlに固定されたバージョンの Go(現在は Go 1.26.5)を要求します。 - 開発環境管理 (mise):
- 開発環境の管理には mise の使用を推奨します。
walletディレクトリでmise installを実行すると、必要な Go バージョンがインストールされ、環境変数も設定されます。
# macOS
brew install mise
# curl経由でのインストール
curl https://mise.jdx.dev/install.sh | sh
# (vcknotsリポジトリのルートから)
cd wallet
mise install
- GOPRIVATE 環境変数:
- mise を使用しない場合は、以下の環境変数を手動で設定してください。設定しないと
go mod downloadが失敗します。
- mise を使用しない場合は、以下の環境変数を手動で設定してください。設定しないと
export GOPRIVATE="github.com/trustknots/vcknots/wallet"
1-2. サンプル実行環境の要件 (Issuer/Verifierサーバー)
このチュートリアルのサンプルコード(Credential の受領と提示)は、対話する相手(Issuer と Verifier)が存在することを前提としています。
このリポジトリの Node.js ベースのサンプルサーバー(server/)が両方の役割を提供します。
wallet のサンプルコードを実行する前に、サーバーを起動してください。
# vcknotsリポジトリのルートから
pnpm install
# issuer+verifierモジュール、server-coreモジュール、serverモジュールをbuild
pnpm -F @trustknots/vcknots build
pnpm -F @trustknots/server-core build
pnpm -F @trustknots/server build
# サーバーを起動(http://localhost:8080 で待ち受け)
pnpm -F @trustknots/server start
サーバーは、このチュートリアルで使用する以下のエンドポイントを提供します。
-
POST /configurations/:configurationId/offer:Credential Offer の作成 -
POST /token,POST /nonce,POST /credentials:OID4VCI の token エンドポイント、nonce エンドポイント、credential エンドポイント -
POST /request,POST /request-object:OID4VP の Authorization Request の作成 -
POST /callback:Verifier の応答受け取りエンドポイント -
GET /.well-known/openid-credential-issuer,GET /.well-known/oauth-authorization-server:メタデータエンドポイント -
ローカルテストでの HTTP 許可: wallet はデフォルトで、Issuer と Verifier のエンドポイントに HTTPS を要求します。 ローカルのサンプルサーバーは HTTP で動作するため、ローカルテスト時は明示的に HTTP を許可してください。
export VCKNOTS_WALLET_HTTP_ALLOWED=true
テストコードから env.SetHTTPAllowed(true)(パッケージ github.com/trustknots/vcknots/wallet/env)を呼び出す方法もあります。
⚠️ セキュリティ警告: 本番環境では
VCKNOTS_WALLET_HTTP_ALLOWEDを有効化しないでください。HTTPS 必須の検証を維持してください。
2. 初期設定
このセクションでは、ライブラリの依存関係をインストールし、wallet のコア機能を集約する Wallet インスタンスを初期化する手順を説明します。
2-1. 依存関係のインストール
GOPRIVATE を設定した後、wallet ディレクトリで以下のコマンドを実行し、go.mod にリストされている依存ライブラリ(github.com/go-jose/go-jose/v4, go.etcd.io/bbolt, golang.org/x/crypto など)をダウンロードします。
go mod download
2-2. Walletの初期化
ライブラリのトップレベル API は github.com/trustknots/vcknots/wallet パッケージにあります。
最も簡単な初期化は wallet.NewWallet() で、すべてのディスパッチャコンポーネントをデフォルトのプラグイン実装で初期化します。
import (
"log"
"github.com/trustknots/vcknots/wallet"
)
w, err := wallet.NewWallet()
if err != nil {
log.Fatal(err)
}
Wallet は内部で 6 つのディスパッチャコンポーネントを協調させます。
それぞれが wallet 機能の一つの側面を担当します。
credstore.CredStoreDispatcher:Credential の永続化(デフォルトは bbolt を使うローカルストレージ)receiver.ReceivingDispatcher:Credential 発行プロトコル(OID4VCI)presenter.PresentationDispatcher:Credential 提示プロトコル(OID4VP)serializer.SerializationDispatcher:Credential のシリアライズ(JWT-VC, SD-JWT VC)verifier.VerificationDispatcher:署名の暗号学的検証idprof.IdentityProfileDispatcher:DID とアイデンティティプロファイル(did:key)
カスタム設定が必要な場合(たとえば OID4VP の Request Object 検証に使うトラストルートを設定する場合)は、ディスパッチャを自分で構築して wallet.NewWalletWithConfig に渡します。
各ディスパッチャのコンストラクタはエラーを返します。
wallet.Config で nil のままにしたフィールドには、デフォルト実装が自動的に補われます。
以下のコードは、wallet/examples/ 配下のサンプルで使われている初期化です。
package main
import (
"crypto/x509"
"fmt"
"os"
"github.com/trustknots/vcknots/wallet"
"github.com/trustknots/vcknots/wallet/credstore"
"github.com/trustknots/vcknots/wallet/idprof"
"github.com/trustknots/vcknots/wallet/presenter"
"github.com/trustknots/vcknots/wallet/presenter/plugins/oid4vp"
"github.com/trustknots/vcknots/wallet/receiver"
"github.com/trustknots/vcknots/wallet/serializer"
"github.com/trustknots/vcknots/wallet/verifier"
)
func newWallet(certPath string) (*wallet.Wallet, error) {
credStore, err := credstore.NewCredStoreDispatcher(credstore.WithDefaultConfig())
if err != nil {
return nil, err
}
receiverDisp, err := receiver.NewReceivingDispatcher(receiver.WithDefaultConfig())
if err != nil {
return nil, err
}
serializerDisp, err := serializer.NewSerializationDispatcher(serializer.WithDefaultConfig())
if err != nil {
return nil, err
}
verifierDisp, err := verifier.NewVerificationDispatcher(verifier.WithDefaultConfig())
if err != nil {
return nil, err
}
idProf, err := idprof.NewIdentityProfileDispatcher(idprof.WithDefaultConfig())
if err != nil {
return nil, err
}
// OID4VP Request Objectのx5c証明書チェーン検証に使うトラストルート
certFile, err := os.ReadFile(certPath)
if err != nil {
return nil, err
}
certPool := x509.NewCertPool()
if !certPool.AppendCertsFromPEM(certFile) {
return nil, fmt.Errorf("failed to parse certificate: %s", certPath)
}
oid4vpPresenter := &oid4vp.Oid4vpPresenter{
X509TrustChainRoots: certPool,
}
presenterDisp, err := presenter.NewPresentationDispatcher(
presenter.WithPlugin(presenter.Oid4vp, oid4vpPresenter),
)
if err != nil {
return nil, err
}
return wallet.NewWalletWithConfig(wallet.Config{
CredStore: credStore,
IDProfiler: idProf,
Receiver: receiverDisp,
Serializer: serializerDisp,
Verifier: verifierDisp,
Presenter: presenterDisp,
})
}
- 保存先: デフォルトの Credential ストアは、
go.etcd.io/bboltを使って<ユーザー設定ディレクトリ>/vcknots/wallet/.local_credstore.dbに永続化します(Linux では~/.config/vcknots/wallet/.local_credstore.db、macOS では~/Library/Application Support/vcknots/wallet/.local_credstore.dbなど)。
3. Wallet機能のサンプル実装
Wallet インスタンスを使用して、wallet の主要な機能(鍵の準備、Credential の受領、Credential の提示)を実行する具体的な Go コードサンプルを示します。
これらのサンプルは wallet/examples/server_integration_sdjwt/server_integration_sdjwt.go と wallet/examples/common/common.go に基づいています。
3-1. テスト用の鍵の準備 (IKeyEntryインターフェース)
主要なワークフローメソッド(ReceiveCredential, PresentCredential)は、署名操作のために IKeyEntry インターフェースを要求します。
これにより、ライブラリ利用者は鍵管理の実装(例: メモリ、HSM、セキュアエンクレーブ)を差し替えることができます。
IKeyEntry インターフェースは以下のように定義されています。
// IKeyEntry は、署名操作のための鍵エントリを表すインターフェースです。
type IKeyEntry interface {
ID() string
PublicKey() jose.JSONWebKey
Sign(data []byte) ([]byte, error)
}
- 署名フォーマット: ECDSA 実装の
Signは、DER エンコードされた ASN.1 署名と、生の IEEE P1363(R || S)署名のどちらを返しても構いません。 ライブラリが内部で(JWKSignerにより)DER 署名を IEEE P1363 に正規化するため、両方の形式が動作します。
チュートリアル用に、wallet/examples/common/common.go の MockKeyEntry と同等のインメモリ実装を使用します。
// MockKeyEntry は IKeyEntry のテスト用実装です。
type MockKeyEntry struct {
id string
privateKey *ecdsa.PrivateKey
}
func NewMockKeyEntry() (*MockKeyEntry, error) {
privKey, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
if err != nil {
return nil, err
}
return &MockKeyEntry{
id: "test-key-id-" + uuid.NewString(),
privateKey: privKey,
}, nil
}
func (m *MockKeyEntry) ID() string { return m.id }
func (m *MockKeyEntry) PublicKey() jose.JSONWebKey {
return jose.JSONWebKey{
Key: &m.privateKey.PublicKey,
Algorithm: "ES256", // P-256曲線
Use: "sig",
}
}
// Sign は SHA-256 ハッシュ -> ECDSA署名 -> IEEE P1363 形式へのシリアライズ を行います。
func (m *MockKeyEntry) Sign(payload []byte) ([]byte, error) {
hash := sha256.Sum256(payload)
r, s, err := ecdsa.Sign(rand.Reader, m.privateKey, hash[:])
if err != nil {
return nil, err
}
const keySize = 32 // P-256: 256 bits / 8
signature := make([]byte, 2*keySize)
r.FillBytes(signature[:keySize])
s.FillBytes(signature[keySize:])
return signature, nil
}
3-2. Credentialの受領 (OID4VCI)
wallet は、Issuer から取得した CredentialOffer を ReceiveCredential に渡して Credential を受領します。
実際の運用では offer URI は QR コードやディープリンクから取得します。
ローカルのサンプルサーバーでは POST /configurations/:configurationId/offer で作成できます。
offer URI は openid-credential-offer://?credential_offer=... という形式です。
これを wallet.CredentialOffer にパースして ReceiveCredential に渡します。
import (
"encoding/json"
"net/url"
"github.com/trustknots/vcknots/wallet"
"github.com/trustknots/vcknots/wallet/credential"
"github.com/trustknots/vcknots/wallet/receiver"
)
func receiveSDJwtCredential(w *wallet.Wallet, key wallet.IKeyEntry, offerURI string) (*wallet.SavedCredential, error) {
// 1. openid-credential-offer:// URIをパース
parsed, err := url.Parse(offerURI)
if err != nil {
return nil, err
}
var offerJSON struct {
CredentialIssuer string `json:"credential_issuer"`
CredentialConfigurationIDs []string `json:"credential_configuration_ids"`
Grants map[string]*wallet.CredentialOfferGrant `json:"grants"`
}
if err := json.Unmarshal([]byte(parsed.Query().Get("credential_offer")), &offerJSON); err != nil {
return nil, err
}
issuerURL, err := url.Parse(offerJSON.CredentialIssuer)
if err != nil {
return nil, err
}
offer := &wallet.CredentialOffer{
CredentialIssuer: issuerURL,
CredentialConfigurationIDs: offerJSON.CredentialConfigurationIDs,
Grants: offerJSON.Grants, // キー: "urn:ietf:params:oauth:grant-type:pre-authorized_code"
}
// 2. OID4VCI (pre-authorized codeフロー) でCredentialを受領
return w.ReceiveCredential(wallet.ReceiveCredentialRequest{
CredentialOffer: offer,
Type: receiver.Oid4vci,
Key: key, // JWT proof (key binding) の署名に使用
RequestedFormat: credential.SDJwtVC, // "application/dc+sd-jwt"
})
}
ReceiveCredentialRequest の補足:
- RequestedFormat: SD-JWT VC を受領する場合は
credential.SDJwtVC、JWT-VC を受領する場合はcredential.JwtVcを指定します。 省略した場合は、先頭の credential configuration ID について Issuer メタデータからフォーマットを解決します(解決できない場合は JWT-VC にフォールバックします)。 - TxCode: offer が transaction code を要求する場合は
TxCodeを設定します。token エンドポイントにtx_codeとして送信されます。 - CachedIssuerMetadata: 設定すると、
ReceiveCredentialは Issuer メタデータの取得をスキップします(セクション 4 を参照)。
ReceiveCredential は、Issuer と Authorization Server のメタデータを取得し、pre-authorized code でアクセストークンを取得し、Key で署名した JWT proof を生成して Credential を要求し、結果を Credential ストアに保存します。
戻り値の *wallet.SavedCredential には、パース済みの Credential とストレージエントリの両方が含まれます。
3-3. Credentialの提示 (OpenID4VP)
Verifier から openid4vp:?... 形式のリクエスト URI を受け取ったら(通常は QR コードのスキャンで取得します。ローカルのサンプルサーバーでは POST /request または POST /request-object で作成できます)、PresentCredential を呼び出します。
import (
"log"
sdjwtvc "github.com/trustknots/vcknots/wallet/serializer/plugins/sdjwtvc"
)
func presentCredential(w *wallet.Wallet, key wallet.IKeyEntry, oid4vpURI string) error {
// SD-JWT VC提示のオプション: Key Binding JWT を付与する。
// 開示するクレームはリクエストの DCQL クエリが指定したものになる。
options := &sdjwtvc.SdJwtVcPresentationOptions{
RequireKeyBinding: true,
}
redirectURI, err := w.PresentCredential(oid4vpURI, key, options)
if err != nil {
return err
}
if redirectURI != "" {
log.Printf("Verifier requested redirect: %s\n", redirectURI)
}
return nil
}
PresentCredential は、OID4VP リクエストをパースし(request_uri で参照される JAR Request Object も含み、その署名は X509TrustChainRoots に対して検証されます)、保存済みの Credential のうち最も新しく受領した 1 件を選択し(DCQL クエリとの照合は現時点では行いません)、key で Verifiable Presentation をシリアライズして署名し、Verifier の response_uri(response_mode=direct_post)に POST します。
Wallet が redirect_uri に送信することはありません。
Verifier の応答に redirect_uri が含まれる場合、その値が戻り値として呼び出し側に返されます(含まれない場合は空文字列です)。
- 提示オプション: 第 3 引数にはフォーマット固有のオプションを渡します。
SD-JWT VC では
sdjwtvc.SdJwtVcPresentationOptionsにより、Key Binding JWT の付与(RequireKeyBinding)を制御します。 開示するクレームは DCQL クエリのclaimsが指定したもの(claim_setsがある場合は満たせる最初の選択肢)になり、claimsが無い場合は 1 件も開示しません(OID4VP 1.0 Section 6.4.1)。Credential が要求されたクレームを持っていない場合、またはSelectedClaimsを指定していてその範囲外のクレームが要求された場合、PresentCredentialはserializerTypes.ErrClaimsNotSatisfiableで失敗し、Verifier には何も送りません。 KB-JWT の audience と nonce は OID4VP リクエスト(client_idとnonce)から自動的に設定されます。リクエストに transaction data が含まれる場合のtransaction_dataハッシュも同様です。nilを渡すと、その Credential のフォーマットに応じたデフォルトのオプションが使われます(JWT-VC の提示ではnilが典型的です)。 - リダイレクト処理: Verifier のリダイレクト URI をコールバックで受け取りたい場合は、
PresentCredentialWithOptionsに&wallet.PresentCredentialOptions{OnRedirect: func(uri string) error {...}}を渡します。
3-4. 保存されたCredentialの参照
ReceiveCredential で保存された Credential は、GetCredentialEntries で一覧取得できます。
ページネーション(Offset, Limit)と Go 関数によるフィルタリング(Filter)をサポートします。
ID を指定して 1 件だけ取得する場合は GetCredentialEntry を使います。
func listSavedCredentials(w *wallet.Wallet) ([]*wallet.SavedCredential, error) {
limit := 10
entries, total, err := w.GetCredentialEntries(wallet.GetCredentialEntriesRequest{
Offset: 0,
Limit: &limit,
Filter: func(sc *wallet.SavedCredential) bool {
return true // 例: return sc.Entry.MimeType == string(credential.SDJwtVC)
},
})
if err != nil {
return nil, err
}
log.Printf("Found %d matching entries (Total: %d)\n", len(entries), total)
for _, entry := range entries {
log.Printf(" - Entry ID: %s, MimeType: %s\n", entry.Entry.Id, entry.Entry.MimeType)
}
return entries, nil
}
4. Issuerメタデータの取得
Credential を受領する際、wallet は Issuer の .well-known/openid-credential-issuer エンドポイントにアクセスし、Issuer の設定(credential エンドポイント、サポートする credential configuration など)を取得する必要があります。
ReceiveCredential はこのメタデータを内部で取得しますが、FetchCredentialIssuerMetadata で明示的に取得し、ReceiveCredentialRequest の CachedIssuerMetadata フィールドに渡すこともできます。
これにより、ReceiveCredential を呼び出すたびにメタデータを再取得するオーバーヘッドを回避できます。
import (
"log"
"net/url"
receiverTypes "github.com/trustknots/vcknots/wallet/receiver/types"
)
func fetchIssuerMetadata(w *wallet.Wallet) (*receiverTypes.CredentialIssuerMetadata, error) {
// 注意: IssuerのベースURLを渡します。/.well-known/... パスは内部で解決されます
issuerURL, _ := url.Parse("http://localhost:8080")
metadata, err := w.FetchCredentialIssuerMetadata(issuerURL, receiverTypes.Oid4vci)
if err != nil {
return nil, err
}
log.Printf("Fetched metadata for issuer: %s\n", metadata.CredentialIssuer)
// metadata.CredentialEndpoint, metadata.CredentialConfigurationSupported, ...
return metadata, nil
}
5. 型定義の説明
vcknots/wallet ライブラリの Wallet とのインタラクションに使用される主要な Go の型定義について説明します。
IKeyEntry
鍵管理のコアインターフェース。ID(), PublicKey(), Sign() の 3 つのメソッドを定義します。ライブラリ利用者は、HSM やセキュアエンクレーブと連携するためにこれを実装します。
定義は wallet/wallet.go を参照してください。
Config
NewWalletWithConfig の入力。6 つのディスパッチャコンポーネントと、オプションの DPoP 設定(DPoPConfig)を保持します。nil のフィールドにはデフォルト実装が補われます。
定義は wallet/wallet.go を参照してください。
ReceiveCredentialRequest
ReceiveCredential の主要な入力。CredentialOffer、受領プロトコル(Type)、proof の署名に使用する鍵(IKeyEntry)、要求する Credential フォーマット(RequestedFormat)、オプションの CachedIssuerMetadata と TxCode をカプセル化します。
定義は wallet/wallet.go を参照してください。
CredentialOffer
Issuer から受け取るオファーの詳細。Issuer の URL(CredentialIssuer)、credential configuration ID(CredentialConfigurationIDs)、認可グラント(Grants)を含みます。
定義は wallet/wallet.go を参照してください。
SavedCredential
Credential ストアに保存された Credential。*credential.Credential(パース済みの Credential)と *types.CredentialEntry(ストレージメタデータ: ID、生データ、MIME タイプ、受領時刻)をラップします。ReceiveCredential, GetCredentialEntries, GetCredentialEntry の戻り値です。
定義は wallet/wallet.go を参照してください。
GetCredentialEntriesRequest
GetCredentialEntries の検索条件。ページネーション(Offset, Limit)と、Go 関数による動的なフィルタリング(Filter)をサポートします。
定義は wallet/wallet.go を参照してください。
PresentCredentialOptions
PresentCredentialWithOptions の入力。フォーマット固有の SerializeOptions と、オプションの OnRedirect コールバックを保持します。
定義は wallet/wallet.go を参照してください。
SdJwtVcPresentationOptions
SD-JWT VC 提示のオプション: ClaimsQuery, SelectedClaims, RequireKeyBinding, Audience, Nonce, TransactionData。audience、nonce、ClaimsQuery は OID4VP リクエストから自動的に設定され、SelectedClaims は ClaimsQuery が開示できる範囲の上限になります。
定義は wallet/serializer/plugins/sdjwtvc/sdjwtvc.go を参照してください。
DIDCreateOptions
GenerateDID のオプション。DID のタイプ(TypeID、例: "did:key")と、関連付ける公開鍵(PublicKey)を指定します。
定義は wallet/wallet.go を参照してください。
DPoPConfig
token エンドポイントと credential エンドポイントへの DPoP proof 付与を有効化します(Enabled)。専用の鍵(Key)も指定できます。有効化時に鍵を指定しない場合は、インメモリの P-256 鍵が生成されます。
定義は wallet/wallet.go を参照してください。
6. Walletのメソッド
ReceiveCredential
OID4VCI(Pre-Authorized Code フロー)で Issuer から Credential を受領し、Credential ストアに保存します。
func (w *Wallet) ReceiveCredential(req ReceiveCredentialRequest) (*SavedCredential, error)
パラメータ:
req: 受領リクエスト(ReceiveCredentialRequest)
戻り値:
- 受領し保存された Credential(SavedCredential)
PresentCredential
OID4VP の Authorization Request に応答し、Verifiable Presentation を Verifier に送信します。
func (w *Wallet) PresentCredential(uriString string, key IKeyEntry, options serializerTypes.SerializePresentationOptions) (string, error)
パラメータ:
uriString: OID4VP リクエスト URI(openid4vp:?...)key: Presentation の署名に使用する鍵(IKeyEntry)options: フォーマット固有の提示オプション(SD-JWT VC では SdJwtVcPresentationOptions)。nilを渡すと、その Credential のフォーマットのデフォルトが使われます
戻り値:
- Verifier が提示したリダイレクト URI(リダイレクトがない場合は空文字列)
PresentCredentialWithOptions
PresentCredential と同じ動作に加えて、Verifier がリダイレクト URI を返した場合にコールバックを呼び出します。
func (w *Wallet) PresentCredentialWithOptions(uriString string, key IKeyEntry, options *PresentCredentialOptions) (string, error)
パラメータ:
uriString: OID4VP リクエスト URI(openid4vp:?...)key: Presentation の署名に使用する鍵(IKeyEntry)options: シリアライズオプションとリダイレクトコールバック(PresentCredentialOptions)
戻り値:
- Verifier が提示したリダイレクト URI(リダイレクトがない場合は空文字列)
GetCredentialEntries
保存された Credential を、ページネーションとフィルタリング付きで取得します。
func (w *Wallet) GetCredentialEntries(req GetCredentialEntriesRequest) ([]*SavedCredential, int, error)
パラメータ:
req: 検索条件(GetCredentialEntriesRequest)
戻り値:
- 条件に一致した Credential(SavedCredential)と、一致件数の合計
GetCredentialEntry
ID を指定して、保存された Credential を 1 件取得します。
func (w *Wallet) GetCredentialEntry(id string) (*SavedCredential, error)
パラメータ:
id: Credential エントリの ID
戻り値:
- 保存された Credential(SavedCredential)。デフォルトのローカルストアでは、ID が存在しない場合はエラーを返します
FetchCredentialIssuerMetadata
Issuer の .well-known/openid-credential-issuer エンドポイントから Issuer メタデータを取得します。
func (w *Wallet) FetchCredentialIssuerMetadata(endpoint *url.URL, receivingType receiverTypes.SupportedReceivingTypes) (*receiverTypes.CredentialIssuerMetadata, error)
パラメータ:
endpoint: Issuer のベース URL(/.well-known/...パスは内部で解決されます)receivingType: 受領プロトコル(receiver.Oid4vci)
戻り値:
- Issuer メタデータ(
receiverTypes.CredentialIssuerMetadata)
GenerateDID
公開鍵から DID を生成します。
func (w *Wallet) GenerateDID(options DIDCreateOptions) (*idprofTypes.IdentityProfile, error)
パラメータ:
options: DID のタイプと公開鍵(DIDCreateOptions)
戻り値:
- 生成されたアイデンティティプロファイル(
idprofTypes.IdentityProfile)
7. 注意事項
-
モック鍵は本番環境で使用禁止 (CRITICAL):
- このチュートリアルで示したインメモリの鍵実装(および
wallet/examples/common/のMockKeyEntry)は、秘密鍵を Go のヒープメモリ上に平文で保持するため、テストとデモンストレーションのみを目的としています。 - 本番環境では、
Signオペレーションを OS のキーストア(iOS Secure Enclave, Android Keystore)や HSM に委譲し、秘密鍵自体がアプリケーションのメモリ空間にロードされない(non-exportable な)形でIKeyEntryを実装してください。
- このチュートリアルで示したインメモリの鍵実装(および
-
GOPRIVATE の設定:
go mod downloadまたはgo buildが失敗する場合、GOPRIVATE 環境変数の設定が欠落している可能性が最も高いです。
-
署名フォーマットの互換性:
Signは ES256 について、DER エンコードされた ASN.1 署名と生の IEEE P1363 署名のどちらを返しても構いません。ライブラリが JWS 構造に埋め込む前に DER を P1363 に正規化します。
-
永続化ストレージ (bbolt):
credstore.WithDefaultConfig()は、go.etcd.io/bboltを使って<ユーザー設定ディレクトリ>/vcknots/wallet/.local_credstore.dbに Credential を永続化します。プロセスがこのディレクトリを作成し書き込めることを確認してください。
-
HTTPS の強制と実行時の環境変数(
wallet/env/env.go):- wallet はデフォルトで、Issuer と Verifier のエンドポイントに HTTPS を要求します。
VCKNOTS_WALLET_HTTP_ALLOWED=trueを設定すると、HTTP エンドポイントを許可します(ローカル開発とテスト用途のみ)。VCKNOTS_WALLET_DEBUG=trueはデバッグモードを有効化し、同時に HTTP 許可動作も有効化します。- 本番運用の指針: 本番環境では両方とも未設定(または
false)のままにし、HTTPS 必須の検証を維持してください。
-
OpenID4VP
client_idの厳格な検証:- この wallet は、OpenID4VP コンフォーマンステストに準拠するため、
client_idを厳格に検証します。重複プレフィックス(例:x509_san_dns:x509_san_dns:...)や不正な形式は拒否されます。 x509_san_dns:スキームの場合、リクエスト JWT のx5cヘッダーから証明書を抽出し、証明書の Subject Alternative Name (SAN) DNS フィールドとclient_idの値を照合します。- 検証ロジックは
wallet/presenter/plugins/oid4vp/を参照してください。
- この wallet は、OpenID4VP コンフォーマンステストに準拠するため、
-
証明書検証のテスト設定(
InsecureSkipX509Verify):Oid4vpPresenter構造体は、テスト環境用にInsecureSkipX509Verifyオプションを提供しています。- デフォルト動作(本番環境):
X509TrustChainRootsに対する完全な証明書チェーン検証を実行します。 - テスト設定(
InsecureSkipX509Verify: true): 証明書チェーンの検証をスキップし、x5cヘッダーから証明書を直接抽出します。SAN とclient_idの照合のみを実行します。 - ⚠️ 重大な警告:
InsecureSkipX509Verify: trueは、コンフォーマンステストやローカル開発環境でのみ使用してください。本番環境では絶対に使用しないでください。
-
DPoP サポート(オプション):
wallet.Config{DPoP: wallet.DPoPConfig{Enabled: true}}を設定すると、wallet は token リクエストと credential リクエストに DPoP proof を付与し、サーバーからの DPoP nonce チャレンジも処理します。
8. トラブルシューティング
-
Q:
go mod downloadがpackage ... is privateまたは404 Not Foundで失敗する。- A: GOPRIVATE 環境変数が設定されていません。「1. 前提条件」に戻り、
export GOPRIVATE="github.com/trustknots/vcknots/wallet"が実行されていること(または mise を使用していること)を確認してください。
- A: GOPRIVATE 環境変数が設定されていません。「1. 前提条件」に戻り、
-
Q:
ReceiveCredentialまたはPresentCredentialがconnection refusedまたはtimeoutで失敗する。- A: Issuer/Verifier サーバーが起動していません。「1. 前提条件」に従い、
pnpm -F @trustknots/server startでサーバーを起動し、http://localhost:8080 が応答することを確認してください。
- A: Issuer/Verifier サーバーが起動していません。「1. 前提条件」に従い、
-
Q:
ReceiveCredentialがcredential issuer must use https schemeで失敗する。- A: wallet はデフォルトで HTTPS を強制します。HTTP のサンプルサーバーに対してローカルテストする場合は、
VCKNOTS_WALLET_HTTP_ALLOWED=trueを設定してください(またはenv.SetHTTPAllowed(true)を呼び出してください)。
- A: wallet はデフォルトで HTTPS を強制します。HTTP のサンプルサーバーに対してローカルテストする場合は、
-
Q:
ReceiveCredentialがfailed to fetch issuer metadataで失敗する。- A: サーバーは起動していても、
/.well-known/openid-credential-issuerエンドポイントが正しく機能していない可能性があります。curl http://localhost:8080/.well-known/openid-credential-issuerを実行して、JSON メタデータが返されることを確認してください。
- A: サーバーは起動していても、
-
Q: OpenID4VP コンフォーマンステストで
client_id検証エラーが発生する。- A: コンフォーマンステストは、意図的に不正な
client_id(重複プレフィックスなど)を送信して wallet の検証ロジックをテストします。invalid client_id: duplicate prefix detectedやSAN of the certificate and client_id did not matchのようなエラーは期待される動作であり、wallet が正しくセキュリティチェックを実施していることを示します。
- A: コンフォーマンステストは、意図的に不正な
-
Q: OpenID4VP コンフォーマンステストで
x509: certificate is not standards compliantエラーが発生する。- A: コンフォーマンステストサーバーは、自己署名証明書や非標準的な証明書を使用することがあります。テスト環境でのみ
InsecureSkipX509Verify: trueを設定してください。p := &oid4vp.Oid4vpPresenter{X509TrustChainRoots: systemRoots,InsecureSkipX509Verify: true, // テスト環境のみ} - ⚠️ 警告: 本番環境では必ず
false(または未設定)にしてください。
- A: コンフォーマンステストサーバーは、自己署名証明書や非標準的な証明書を使用することがあります。テスト環境でのみ
実行可能なエンドツーエンドのサンプル(JWT-VC、SD-JWT VC、KB-JWT 付き SD-JWT VC)は wallet/examples/README.ja.md を参照してください。