3단계: 신뢰 당사자로 통합

이 가이드에서는 개별 판매자, 비즈니스, 앱 개발자와 같은 직접 신뢰 당사자 (RP)가 Digital Credentials API를 통합하여 Android 앱과 웹에서 디지털 ID 사용자 인증 정보를 요청하고 검증하는 방법을 설명합니다.

신뢰 당사자 통합 작동 방식

모든 신뢰 당사자 통합은 4단계를 따릅니다. A단계와 C단계는 서버에서 실행되고 B단계는 Android 앱 또는 브라우저에서 실행됩니다. 단계를 선택하여 구현 세부정보로 이동합니다.

프로덕션 사용자 인증 정보를 요청하기 전에 샌드박스에서 빌드 및 테스트

A~D단계를 구현하는 동안 Google의 사전 신뢰 샌드박스 애셋을 사용하여 즉시 빌드하고 테스트할 수 있습니다.

요청 서명

Google의 샌드박스 테스트 키 및 메타데이터를 사용하여 OpenID4VP 요청에 서명합니다.

테스트 사용자 인증 정보

샌드박스 모드의 Android 기기에서 Utopia 테스트 신분증 패스를 사용하여 테스트합니다.

응답 확인

Utopia Sandbox 인증서를 기준으로 issuerAuth을 검증합니다.

온라인 인증을 위한 백엔드 서버, 클라이언트 앱 또는 브라우저, Google 월렛 간의 엔드 투 엔드 시퀀스

지원되는 형식, 사용자 인증 정보, 기능

Google 월렛은 Android 9 (API 수준 28) 이상 및 지원되는 브라우저에서 OpenID4VP (버전 1.0)를 통해 ISO mdoc 기반 디지털 ID를 지원합니다.

지원되는 사용자 인증 정보 및 속성

Google 월렛에서 요청할 수 있는 doctype_value 식별자 (mDL, 신분증 패스, 사진이 있는 신분증) 및 속성 클레임 경로를 조회합니다.

지원되는 발급기관 및 IACA 인증서

미국 주, 국제 발급기관, Sandbox Utopia 발급기관의 공식 IACA 루트 인증서를 다운로드하여 issuerAuth의 유효성을 검사합니다.


A단계: 요청 형식 지정 및 서명 (서버 측)

지갑에서 사용자 인증 정보를 요청하려면 서버에서 OpenID4VP를 사용하여 요청 형식을 지정해야 합니다. 단일 dcql_query 객체에서 특정 사용자 인증 정보 또는 여러 사용자 인증 정보를 요청할 수 있습니다.

JSON 요청 예시

다음은 Android 기기 또는 웹의 지갑에서 신원 사용자 인증 정보를 가져오는 mdoc requestJson 요청의 샘플입니다.

{
      "requests" : [
        {
          "protocol": "openid4vp-v1-signed",
          "data": {<signed_credential_request>} // This is an object, shouldn't be a string.
        }
      ]
}

요청 암호화

client_metadata에는 각 요청의 암호화 공개 키가 포함됩니다. 각 요청의 비공개 키를 저장하고 이를 사용하여 월렛 앱에서 수신하는 토큰을 인증하고 승인해야 합니다.

통합 OpenID4VP 메타데이터

사용자 인증 정보 요청의 형식을 지정할 때는 아래의 샘플 요청 코드와 같이 client_metadata 객체 내에 gw_rp_metadata_bytes 필드를 포함해야 합니다. 이 필드에는 Google 월렛에서 신원을 확인하고 사용자에게 브랜딩을 표시하는 데 필요한 Base64URL로 인코딩된 신뢰 당사자 메타데이터가 포함됩니다.

사용자 인증 정보 요청 페이로드

requestJson의 credential_request 매개변수에는 다음 필드가 포함됩니다.

특정 사용자 인증 정보

{
  "response_type": "vp_token",
  "response_mode": "dc_api.jwt", // change this to dc_api if you want to demo with a non encrypted response.
  "nonce": "1234",
  "dcql_query": {
    "credentials": [
      {
        "id": "cred1",
        "format": "mso_mdoc",
        "meta": {
          // Use org.iso.18013.5.1.mDL for mDL,
          // com.google.wallet.idcard.1 for ID pass, or
          // org.iso.23220.photoid.1 for ID pass (using photo ID document type)
          "doctype_value": "org.iso.18013.5.1.mDL"
        },
        "claims": [
          {
            "path": [
              "org.iso.18013.5.1",
              "family_name"
            ],
            "intent_to_retain": false // set this to true if you are saving the value of the field
          },
          {
            "path": [
              "org.iso.18013.5.1",
              "given_name"
            ],
            "intent_to_retain": false
          },
          {
            "path": [
              "org.iso.18013.5.1",
              "age_over_18"
            ],
            "intent_to_retain": false
          }
        ]
      }
    ]
  },
  "client_metadata": {
    "jwks": {
      "keys": [ // sample request encryption key
        {
          "kty": "EC",
          "crv": "P-256",
          "x": "pDe667JupOe9pXc8xQyf_H03jsQu24r5qXI25x_n1Zs",
          "y": "w-g0OrRBN7WFLX3zsngfCWD3zfor5-NLHxJPmzsSvqQ",
          "use": "enc",
          "kid" : "1",  // This is required
          "alg" : "ECDH-ES",  // This is required
        }
      ]
    },
    "vp_formats_supported": {
      "mso_mdoc": {
        "deviceauth_alg_values": [
          -7
        ],
        "issuerauth_alg_values": [
          -7
        ]
      }
    },
    "gw_rp_metadata_bytes": "<base64url encoded metadata string>"
  }
}

모든 적격 사용자 인증 정보

다음은 mDL, 신분증 패스, 신분증 패스 (사진이 있는 신분증 문서 유형 사용)의 요청 예시입니다. 사용자는 둘 중 하나를 선택하여 진행할 수 있습니다.

{
  "response_type": "vp_token",
  "response_mode": "dc_api.jwt", // change this to dc_api if you want to demo with a non encrypted response.
  "nonce": "1234",
  "dcql_query": {
    "credentials": [
      {
        "id": "mdl-request",
        "format": "mso_mdoc",
        "meta": {
          "doctype_value": "org.iso.18013.5.1.mDL"
        },
        "claims": [
          {
            "path": [
              "org.iso.18013.5.1",
              "family_name"
            ],
            "intent_to_retain": false // set this to true if you are saving the value of the field
          },
          {
            "path": [
              "org.iso.18013.5.1",
              "given_name"
            ],
            "intent_to_retain": false
          },
          {
            "path": [
              "org.iso.18013.5.1",
              "age_over_18"
            ],
            "intent_to_retain": false
          }
        ]
      },
      {  // Credential type 2: ID pass
        "id": "id_pass-request",
        "format": "mso_mdoc",
        "meta": {
          "doctype_value": "com.google.wallet.idcard.1"
        },
        "claims": [
          {
            "path": [
              "org.iso.18013.5.1",
              "family_name"
            ],
            "intent_to_retain": false // set this to true if you are saving the value of the field
          },
          {
            "path": [
              "org.iso.18013.5.1",
              "given_name"
            ],
            "intent_to_retain": false
          },
          {
            "path": [
              "org.iso.18013.5.1",
              "age_over_18"
            ],
            "intent_to_retain": false
          }
        ]
      },
      {  // Credential type 3: ID pass (using photo ID document type)
        "id": "photo_id-request",
        "format": "mso_mdoc",
        "meta": {
          "doctype_value": "org.iso.23220.photoid.1"
        },
        "claims": [
          {
            "path": [
              "org.iso.23220.1",
              "family_name"
            ],
            "intent_to_retain": false // set this to true if you are saving the value of the field
          },
          {
            "path": [
              "org.iso.23220.1",
              "given_name"
            ],
            "intent_to_retain": false
          },
          {
            "path": [
              "org.iso.23220.1",
              "age_over_18"
            ],
            "intent_to_retain": false
          }
        ]
      }
    ]
    credential_sets : [
      {
        "options": [
          [ "mdl-request" ],
          [ "id_pass-request" ],
          [ "photo_id-request" ]
        ]
      }
    ]
  },
  "client_metadata": {
    "jwks": {
      "keys": [ // sample request encryption key
        {
          "kty": "EC",
          "crv": "P-256",
          "x": "pDe667JupOe9pXc8xQyf_H03jsQu24r5qXI25x_n1Zs",
          "y": "w-g0OrRBN7WFLX3zsngfCWD3zfor5-NLHxJPmzsSvqQ",
          "use": "enc",
          "kid" : "1",  // This is required
          "alg" : "ECDH-ES",  // This is required
        }
      ]
    },
    "vp_formats_supported": {
      "mso_mdoc": {
        "deviceauth_alg_values": [
          -7
        ],
        "issuerauth_alg_values": [
          -7
        ]
      }
    },
    "gw_rp_metadata_bytes": "<base64url encoded metadata string>"
  }
}

Google 월렛에 저장된 모든 ID 사용자 인증 정보에서 원하는 수의 지원되는 속성을 요청할 수 있습니다.

서명된 요청

서명된 요청(JWT 보안 승인 요청)은 PKI 인프라를 사용하여 암호화 서명된 JSON 웹 토큰(JWT) 내에 검증 가능한 프레젠테이션 요청을 캡슐화하여 요청 무결성을 보장하고 Google 월렛에 ID를 증명합니다.

기본 요건

서명된 요청에 대한 코드 변경사항을 구현하기 전에 다음 사항을 확인하세요.

  • 비공개 키: 요청에 서명하려면 서버에서 관리되는 비공개 키 (예: 타원 곡선 ES256)가 필요합니다. 신뢰 당사자로서 샌드박스 테스트를 하려면 샌드박스 테스트 비공개 키를 사용하세요.
  • 인증서: 키 쌍에서 파생된 표준 X.509 인증서가 필요합니다.
  • 등록: 공개 인증서 (또는 확인자 등록 기관의 루트 CA)가 Google 월렛에서 신뢰할 수 있는지 확인합니다.

요청 구성 로직

OpenID4VP 요청을 구성하고 서명할 때는 각 필드에 필요한 정확한 인코딩 형식에 주의하세요. base64와 base64url를 혼합하거나 패딩 (=)을 사용하면 서명 또는 출처 검증이 실패합니다.

필드 필수 인코딩 / 형식 문자 및 패딩
client_id(x509_hash:<hash>) 패딩이 없는 base64url URL 보안 알파벳 (-, _)을 사용하고 후행 =를 제거합니다.
expected_origins(android:apk-key-hash:<hash>) 패딩이 없는 표준 base64 표준 알파벳 (+, /)을 사용하고 후행 =를 제거합니다.
expected_origins(https://...) 후행 슬래시가 없는 소문자 URL 예: https://example.com (뒤에 / 없음)
x5c JWS 헤더 패딩이 있는 표준 base64 표준 알파벳 (+, /)을 사용하고 후행 = 패딩을 유지합니다.

요청을 구성하려면 비공개 키를 사용하고 페이로드를 JWS로 래핑합니다.

def construct_openid4vp_request(
    doctypes: list[str],
    requested_fields: list[dict],
    nonce_base64: str,
    jwe_encryption_public_jwk: jwk.JWK,
    is_zkp_request: bool,
    is_signed_request: bool,
    state: dict,
    origin: str
) -> dict:

    # ... [Existing logic to build 'presentation_definition' and basic 'request_payload'] ...

    # ------------------------------------------------------------------
    # SIGNED REQUEST IMPLEMENTATION (JAR)
    # ------------------------------------------------------------------
    if is_signed_request:
        try:
            # 1. Load the Verifier's Certificate
            # We must load the PEM string into a cryptography x509 object
            verifier_cert_obj = x509.load_pem_x509_certificate(
                CERTIFICATE.encode('utf-8'),
                backend=default_backend()
            )

            # 2. Calculate Client ID (x509_hash)
            # We calculate the SHA-256 hash of the DER-encoded certificate.
            cert_der = verifier_cert_obj.public_bytes(serialization.Encoding.DER)
            verifier_fingerprint_bytes = hashlib.sha256(cert_der).digest()

            # Important: Use URL-safe Base64 (base64url: '-', '_') without padding ('=') for client_id
            verifier_fingerprint_b64 = base64.urlsafe_b64encode(verifier_fingerprint_bytes).decode('utf-8').rstrip("=")

            # Format the client_id as required by the spec
            client_id = f'x509_hash:{verifier_fingerprint_b64}'

            # 3. Update Request Payload with JAR specific fields
            request_payload["client_id"] = client_id

            # Explicitly set expected origins to prevent relay attacks
            # Important: Use standard Base64 (not URL-safe) without padding for android:apk-key-hash:
            # origin = f"android:apk-key-hash:{base64.b64encode(apk_cert_hash).decode('utf-8').rstrip('=')}"
            # Format for web origin (lowercase URL without trailing slash): origin = "https://example.com"
            if origin:
                request_payload["expected_origins"] = [origin]

            # 4. Create Signed JWT (JWS)
            # Load the signing private key
            signing_key = jwk.JWK.from_pem(PRIVATE_KEY.encode('utf-8'))

            # Initialize JWS with the JSON payload
            jws_token = jws.JWS(json.dumps(request_payload).encode('utf-8'))

            # Construct the JOSE Header
            # 'x5c' (X.509 Certificate Chain) is critical: it allows the wallet
            # to validate your key against the one registered in the console.
            # Important: Use standard Base64 ('+', '/') WITH padding ('=') for x5c
            x5c_value = base64.b64encode(cert_der).decode('utf-8')

            protected_header = {
                "alg": "ES256",                 # Algorithm (e.g., ES256 or RS256)
                "typ": "oauth-authz-req+jwt",   # Standard type for JAR
                "kid": "1",                     # Key ID
                "x5c": [x5c_value]              # Embed the certificate
            }

            # Sign the token
            jws_token.add_signature(
                key=signing_key,
                alg=None,
                protected=json_encode(protected_header)
            )

            # 5. Return the Request Object
            # Instead of returning the raw JSON, we return the signed JWT string
            # under the 'request' key.
            return {"request": jws_token.serialize(compact=True)}

        except Exception as e:
            print(f"Error signing OpenID4VP request: {e}")
            return None

    # ... [Fallback for unsigned requests] ...
    return request_payload

B단계: API 트리거 (클라이언트 측)

전체 API 요청은 서버 측에서 생성되어야 합니다. 플랫폼에 따라 생성된 JSON을 플랫폼 API에 전달합니다.

인앱 (Android)

Android 앱에서 신원 사용자 인증 정보를 요청하려면 다음 단계를 따르세요.

종속 항목 업데이트

프로젝트의 build.gradle에서 인증 관리자 (베타)를 사용하도록 종속 항목을 업데이트합니다.

dependencies {
    implementation("androidx.credentials:credentials:1.5.0-beta01")
    implementation("androidx.credentials:credentials-play-services-auth:1.5.0-beta01")
}

인증 관리자 구성

CredentialManager 객체를 구성하고 초기화하려면 다음과 유사한 로직을 추가하세요.

// Use your app or activity context to instantiate a client instance of CredentialManager.
val credentialManager = CredentialManager.create(context)

ID 속성 요청

앱은 ID 요청에 개별 매개변수를 지정하는 대신 CredentialOption 내에 JSON 문자열로 모두 함께 제공합니다. 인증 관리자는 이 JSON 문자열의 콘텐츠를 검사하지 않고 사용 가능한 디지털 지갑에 전달합니다. 각 지갑은 다음을 담당합니다.

  • JSON 문자열을 파싱하여 ID 요청을 이해합니다.
  • 저장된 사용자 인증 정보 중 요청을 충족하는 사용자 인증 정보가 있는지 확인합니다.

파트너는 Android 앱 통합의 경우에도 서버에서 요청을 만드는 것이 좋습니다.

요청 형식 지정 및 서명의 requestJson를 GetDigitalCredentialOption() 함수 호출의 request로 사용합니다.

// The request in the JSON format to conform with
// the JSON-ified Digital Credentials API request definition.
val requestJson = generateRequestFromServer()
val digitalCredentialOption =
    GetDigitalCredentialOption(requestJson = requestJson)

// Use the option from the previous step to build the `GetCredentialRequest`.
val getCredRequest = GetCredentialRequest(
    listOf(digitalCredentialOption)
)

coroutineScope.launch {
    try {
        val result = credentialManager.getCredential(
            context = activityContext,
            request = getCredRequest
        )
        verifyResult(result)
    } catch (e : GetCredentialException) {
        handleFailure(e)
    }
}

사용자 인증 정보 응답 처리

월렛에서 응답을 받으면 응답이 성공적이고 credentialJson 응답을 포함하는지 확인합니다.

// Handle the successfully returned credential.
fun verifyResult(result: GetCredentialResponse) {
    val credential = result.credential
    when (credential) {
        is DigitalCredential -> {
            val responseJson = credential.credentialJson
            validateResponseOnServer(responseJson) // make a server call to validate the response
        }
        else -> {
            // Catch any unrecognized credential type here.
            Log.e(TAG, "Unexpected type of credential ${credential.type}")
        }
    }
}

// Handle failure.
fun handleFailure(e: GetCredentialException) {
  when (e) {
        is GetCredentialCancellationException -> {
            // The user intentionally canceled the operation and chose not
            // to share the credential.
        }
        is GetCredentialInterruptedException -> {
            // Retry-able error. Consider retrying the call.
        }
        is NoCredentialException -> {
            // No credential was available.
        }
        else -> Log.w(TAG, "Unexpected exception type ${e::class.java}")
    }
}

credentialJson 응답에는 W3C에서 정의한 암호화된 identityToken (JWT)이 포함됩니다. 월렛 앱은 이 응답을 작성합니다.

예:

{
  "protocol" : "openid4vp-v1-signed",
  "data" : {
    <encrypted_response>
  }
}

이 응답을 서버로 다시 전달하여 진위 여부를 확인합니다. 응답 복호화 및 검증을 참조하세요.

웹

Chrome 또는 기타 지원되는 브라우저에서 Digital Credentials API를 사용하여 신원 사용자 인증 정보를 요청하려면 다음 요청을 실행합니다.

const credentialResponse = await navigator.credentials.get({
          digital : {
          requests : [
            {
              protocol: "openid4vp-v1-signed",
              data: {<credential_request>} // This is an object, shouldn't be a string.
            }
          ]
        }
      })

이 API의 응답을 서버로 다시 전송하여 사용자 인증 정보 응답을 검증합니다.


C단계: 응답 복호화 및 검증 (서버 측)

월렛에서 암호화된 identityToken (JWT)를 반환하면 데이터를 신뢰하기 전에 엄격한 서버 측 검증을 수행해야 합니다.

응답 복호화

요청의 client_metadata에서 전송된 공개 키에 해당하는 비공개 키를 사용하여 JWE를 복호화합니다. 그러면 vp_token가 생성됩니다.

Python 예시:

from jwcrypto import jwe, jwk

# Retrieve the Private Key from Datastore
reader_private_jwk = jwk.JWK.from_json(jwe_private_key_json_str)
# Save public key thumbprint for session transcript
encryption_public_jwk_thumbprint = reader_private_jwk.thumbprint()


# Decrypt the JWE encrypted response from Google Wallet
jwe_object = jwe.JWE()
jwe_object.deserialize(encrypted_jwe_response_from_wallet)
jwe_object.decrypt(reader_private_jwk)
decrypted_payload_bytes = jwe_object.payload
decrypted_data = json.loads(decrypted_payload_bytes)

decrypted_data는 사용자 인증 정보가 포함된 vp_token JSON을 생성합니다.

{
  "vp_token":
  {
    "cred1": ["<base64UrlNoPadding_encoded_credential>"] // This applies to OpenID4VP 1.0 spec.
  }
}

SessionTranscript 재구성 및 DeviceResponse 검증

  1. Android 또는 웹별 핸드오버 구조를 사용하여 ISO/IEC 18013-5:2021에서 SessionTranscript를 만듭니다.
SessionTranscript = [
  null,                // DeviceEngagementBytes not available
  null,                // EReaderKeyBytes not available
  [
    "OpenID4VPDCAPIHandover",
    AndroidHandoverDataBytes   // BrowserHandoverDataBytes for Web
  ]
]

Android 및 웹 핸드오버 모두에서 credential_request를 생성하는 데 사용한 것과 동일한 nonce를 사용합니다.

Android 핸드오버

AndroidHandoverData = [
  origin,             // "android:apk-key-hash:<base64SHA256_ofAppSigningCert>",
  nonce,           // nonce that was used to generate credential request,
  encryption_public_jwk_thumbprint,  // Encryption public key (JWK) Thumbprint
]

AndroidHandoverDataBytes = hashlib.sha256(cbor2.dumps(AndroidHandoverData)).digest()

브라우저 핸드오버

BrowserHandoverData =[
  origin,               // Origin URL
  nonce,               //  nonce that was used to generate credential request
  encryption_public_jwk_thumbprint,  // Encryption public key (JWK) Thumbprint
]

BrowserHandoverDataBytes = hashlib.sha256(cbor2.dumps(BrowserHandoverData)).digest()

SessionTranscript를 사용하여 기기 응답은 ISO/IEC 18013-5:2021 9항에 따라 검증되어야 합니다.

이 검증에는 다음 네 가지 검사가 포함됩니다.

  1. 발급자 인증서 확인: issuerAuth에서 발급자의 서명 인증서 체인을 추출하고 신뢰할 수 있는 IACA 루트 인증서와 비교하여 검증합니다. 지원되는 발급기관 및 IACA 인증서를 참고하세요. 샌드박스 테스트 ID로 테스트하는 경우 Utopia 샌드박스 인증서를 추가하세요.
  2. MSO 서명 확인 (ISO/IEC 18013-5 섹션 9.1.2)
  3. 데이터 요소의 ValueDigests를 계산하고 확인합니다 (ISO/IEC 18013-5 섹션 9.1.2).
  4. deviceSignature 서명 확인 (ISO/IEC 18013-5 섹션 9.1.3)
{
  "version": "1.0",
  "documents": [
    {
      "docType": "org.iso.18013.5.1.mDL",
      "issuerSigned": {
        "nameSpaces": {...}, // contains data elements
        "issuerAuth": [...]  // COSE_Sign1 w/ issuer PK, mso + sig
      },
      "deviceSigned": {
        "nameSpaces": 24(<< {} >>), // empty
        "deviceAuth": {
          "deviceSignature": [...] // COSE_Sign1 w/ device signature
        }
      }
    }
  ],
  "status": 0
}

D단계 (선택사항): 개인 정보 보호 연령 인증 (ZKP)

영지식 증명을 지원하려면 (예: 정확한 생년월일을 보지 않고 사용자가 18세 이상인지 확인) 요청 형식을 mso_mdoc_zk로 변경하고 필요한 zk_system_type 구성을 제공합니다.

ZKP의 정의와 기능에 대한 대략적인 개요는 FAQ를 참고하세요.

  ...
  "dcql_query": {
    "credentials": [{
      "id": "cred1",
      "format": "mso_mdoc_zk",
      "meta": {
        "doctype_value": "org.iso.18013.5.1.mDL"
        "zk_system_type": [
        {
          "system": "longfellow-libzk-v1",
          "circuit_hash": "f88a39e561ec0be02bb3dfe38fb609ad154e98decbbe632887d850fc612fea6f", // This will differ if you need more than 1 attribute.
          "num_attributes": 1, // number of attributes (in claims) this has can support
          "version": 5,
          "block_enc_hash": 4096,
          "block_enc_sig": 2945,
        }
        {
          "system": "longfellow-libzk-v1",
          "circuit_hash": "137e5a75ce72735a37c8a72da1a8a0a5df8d13365c2ae3d2c2bd6a0e7197c7c6", // This will differ if you need more than 1 attribute.
          "num_attributes": 1, // number of attributes (in claims) this has can support
          "version": 6,
          "block_enc_hash": 4096,
          "block_enc_sig": 2945,
        }
       ],
       "verifier_message": "challenge"
      },
     "claims": [{
         ...
      "client_metadata": {
        "jwks": {
          "keys": [ // sample request encryption key
            {
              ...

월렛에서 암호화된 영지식 증명을 받게 됩니다. Google의 longfellow-zk 라이브러리를 사용하여 발급기관 IACA 인증서에 대해 이 증명을 검증할 수 있습니다.

verifier-service에는 발급기관 IACA 인증서에 대해 응답을 검증할 수 있는 배포 준비가 완료된 Docker 기반 서버가 포함되어 있습니다.

certs.pem을 수정하여 신뢰하려는 IACA 발급기관 인증서를 관리할 수 있습니다.


SDK, 참조 구현, 지원

오픈소스 라이브러리, 참조 서버, 웹 인증 도구를 사용하여 구현을 가속화하세요.

리소스 / 라이브러리 제공하는 내용 링크
신원 확인 도구 참조 구현 OpenID4VP 요청 생성, JWS 서명, JWE 복호화, mdoc 응답 검증을 보여주는 샘플 서버 및 클라이언트입니다. GitHub 저장소
Multipaz SDK CBOR 디코딩, SessionTranscript 재구성, ISO/IEC 18013-5/7 서명 검증을 위한 DeviceResponseParser를 포함하는 오픈소스 라이브러리 (OpenWallet Foundation) openwallet-foundation/multipaz
Longfellow ZKP 라이브러리 및 검증 도구 서비스 영지식 증명 (mso_mdoc_zk) 연령 및 속성 어설션을 확인하기 위한 오픈소스 라이브러리 및 Docker 기반 서버입니다. google/longfellow-zk
대화형 웹 인증 도구 Google 월렛을 대상으로 W3C Digital Credentials API / OpenID4VP 요청을 테스트하는 호스팅된 웹 인증 도구입니다. verifier.multipaz.org
OpenID4VP 1.0 사양 공식 OpenID for Verifiable Presentations 1.0 프로토콜 사양입니다. OpenID4VP 사양
FAQ 및 개발자 지원 일반적인 기술 통합 질문에 대한 답변을 확인하거나 wallet-identity-rp-support@google.com에 이메일을 보내 디버깅 지원을 요청하세요. 기술 통합 FAQ

다음 단계