In diesem Leitfaden wird erläutert, wie sich vertrauende Parteien (Relying Parties, RPs) technisch in die Digital Credentials API einbinden können, um mobile Führerscheine (mobile Driver's Licenses, mDL) und digitale Identitätsnachweise von Google Wallet in Android-Apps und im Web anzufordern und zu validieren.
Registrierungsprozess und Voraussetzungen
Bevor Sie in der Produktion live gehen, müssen Sie Ihre Anwendung der vertrauenden Partei offiziell bei Google registrieren.
- In der Sandbox testen: Sie können sofort mit der Entwicklung beginnen, indem Sie unsere Sandbox-Umgebung und eine Test-ID erstellen. Das Akzeptieren der Nutzungsbedingungen ist für Tests nicht erforderlich.
- Aufnahmeformular einreichen: Füllen Sie das Aufnahmeformular für vertrauende Parteien aus. Die Aufnahme dauert in der Regel 3 bis 5 Arbeitstage. Ihr Produktname und Ihr Logo werden auf dem Zustimmungsbildschirm für Nutzer angezeigt, damit diese erkennen können, wer ihre Daten anfordert.
- Nutzungsbedingungen akzeptieren:Sie müssen die Nutzungsbedingungen unterzeichnen, bevor Sie live gehen.
Antworten auf Fragen zum Verfolgen Ihrer Bewerbung oder zum Testen aus nicht unterstützten Regionen finden Sie in den FAQs.
Unterstützte Formate und Funktionen
Google Wallet unterstützt digitale Ausweise auf Basis von ISO mdoc.
- Unterstützte Anmeldedaten: Sie können sich die unterstützten Anmeldedaten und Attribute ansehen.
- Unterstützte Protokolle: OpenID4VP (Version 1.0).
- Mindest-Android-SDK:Android 9 (API-Level 28) und höher.
- Unterstützte Browser:Eine umfassende Liste der Browser, die die Digital Credentials API unterstützen, finden Sie auf der Seite zur Unterstützung des Ökosystems.
- FAQs:Antworten auf Fragen zur Länderunterstützung und zum Zeitplan für neue Regionen finden Sie in den FAQs zu Anmeldedaten und Daten.
Anfrage formatieren
Wenn Sie Anmeldedaten aus einem beliebigen Wallet anfordern möchten, müssen Sie Ihre Anfrage mit OpenID4VP formatieren. Sie können bestimmte Anmeldedaten oder mehrere Anmeldedaten in einem einzigen dcql_query-Objekt anfordern.
JSON-Beispielanfrage
Hier sehen Sie ein Beispiel für eine requestJson-Anfrage für mdoc, um Anmeldedaten aus einem beliebigen Wallet auf einem Android-Gerät oder im Web abzurufen.
{
"requests" : [
{
"protocol": "openid4vp-v1-signed",
"data": {<signed_credential_request>} // This is an object, shouldn't be a string.
}
]
}
Anfrageverschlüsselung
Die client_metadata enthält den öffentlichen Verschlüsselungsschlüssel für jede Anfrage.
Sie müssen private Schlüssel für jede Anfrage speichern und damit das Token authentifizieren und autorisieren, das Sie von der Wallet-App erhalten.
Der Parameter credential_request in requestJson enthält die folgenden Felder.
Bestimmte Anmeldedaten
{
"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": {
"doctype_value": "org.iso.18013.5.1.mDL" // this is for mDL. Use com.google.wallet.idcard.1 for ID pass
},
"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
],
"isserauth_alg_values": [
-7
]
}
}
}
}
Beliebige infrage kommende Anmeldedaten
Hier sehen Sie die Beispielanfrage für mDL und digitalen Identitätsnachweis. Der Nutzer kann mit einer der beiden Optionen fortfahren.
{
"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": "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_sets : [
{
"options": [
[ "mdl-request" ],
[ "id_pass-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
],
"isserauth_alg_values": [
-7
]
}
}
}
}
Sie können eine beliebige Anzahl unterstützter Attribute aus beliebigen Anmeldedaten anfordern, die in Google Wallet gespeichert sind.
Signierte Anfragen
Signierte Anfragen (JWT-gesicherte Autorisierungsanfragen) kapseln Ihre überprüfbare Präsentationsanfrage in einem kryptografisch signierten JSON Web Token (JWT) mithilfe Ihrer PKI-Infrastruktur. So wird die Integrität der Anfrage sichergestellt und Ihre Identität gegenüber Google Wallet nachgewiesen.
Vorbereitung
Bevor Sie die Codeänderungen für signierte Anfragen implementieren, müssen Sie Folgendes haben:
- Privater Schlüssel:Sie benötigen einen privaten Schlüssel (z. B. Elliptic Curve
ES256), um die Anfrage zu signieren. Dieser wird auf Ihrem Server verwaltet. - Zertifikat:Sie benötigen ein Standard-X.509-Zertifikat, das von Ihrem Schlüsselpaar abgeleitet wurde.
- Registrierung:Ihr öffentliches Zertifikat muss bei Google Wallet registriert sein. Wenden Sie sich an unser Supportteam unter
wallet-identity-rp-support@google.com.
Logik zur Erstellung von Anfragen
Um eine Anfrage zu erstellen, müssen Sie Ihren privaten Schlüssel verwenden und die Nutzlast in ein JWS einfügen.
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()
# Create a URL-safe Base64 hash (removing padding '=')
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
# Format for android origin: origin = android:apk-key-hash:<base64SHA256_ofAppSigningCert>
# Format for web origin: origin = <origin_url>
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.
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
API auslösen
Die gesamte API-Anfrage sollte serverseitig generiert werden. Je nach Plattform übergeben Sie das generierte JSON an die nativen APIs.
In-App (Android)
So fordern Sie Anmeldedaten aus Ihren Android-Apps an:
Abhängigkeiten aktualisieren
Aktualisieren Sie in der Datei „build.gradle“ Ihres Projekts die Abhängigkeiten, um den Credential Manager (Beta) zu verwenden:
dependencies {
implementation("androidx.credentials:credentials:1.5.0-beta01")
implementation("androidx.credentials:credentials-play-services-auth:1.5.0-beta01")
}
Credential Manager konfigurieren
Um ein CredentialManager-Objekt zu konfigurieren und zu initialisieren, fügen Sie eine ähnliche Logik hinzu:
// Use your app or activity context to instantiate a client instance of CredentialManager.
val credentialManager = CredentialManager.create(context)
Attribute zur Identität anfordern
Anstatt einzelne Parameter für Identitätsanfragen anzugeben, stellt die App sie alle zusammen als JSON-String in der CredentialOption bereit.
Der Credential Manager übergibt diesen JSON-String an die verfügbaren digitalen Wallets, ohne den Inhalt zu prüfen. Jedes Wallet ist dann für Folgendes verantwortlich:
- JSON-String parsen, um die Identitätsanfrage zu verstehen.
- Ermitteln, welche der gespeicherten Anmeldedaten die Anfrage erfüllen.
Wir empfehlen Partnern, ihre Anfragen auch für Android-App-Integrationen auf dem Server zu erstellen.
Sie verwenden requestJson aus Anfrageformat
als request im Funktionsaufruf GetDigitalCredentialOption().
// 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)
}
}
Anmeldedatenantwort verarbeiten
Sobald Sie eine Antwort vom Wallet erhalten, prüfen Sie, ob sie erfolgreich war und die credentialJson-Antwort enthält.
// 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}")
}
}
Die credentialJson-Antwort enthält ein verschlüsseltes identityToken (JWT), das vom W3C definiert wurde. Die Wallet-App ist für die Erstellung dieser Antwort verantwortlich.
Beispiel:
{
"protocol" : "openid4vp-v1-signed",
"data" : {
<encrpted_response>
}
}
Sie senden diese Antwort zurück an den Server, um ihre Authentizität zu bestätigen. Hier finden Sie die Schritte zum Bestätigen der Anmeldedatenantwort.
Web
Wenn Sie Anmeldedaten mit der Digital Credentials API in Chrome oder anderen unterstützten Browsern anfordern möchten, stellen Sie die folgende Anfrage.
const credentialResponse = await navigator.credentials.get({
digital : {
requests : [
{
protocol: "openid4vp-v1-signed",
data: {<credential_request>} // This is an object, shouldn't be a string.
}
]
}
})
Senden Sie die Antwort von dieser API zurück an Ihren Server, um die Anmeldedatenantwort zu bestätigen.
Antwort bestätigen
Sobald das Wallet das verschlüsselte identityToken (JWT) zurückgibt, müssen Sie eine strenge serverseitige Validierung durchführen, bevor Sie den Daten vertrauen.
Antwort entschlüsseln
Verwenden Sie den privaten Schlüssel, der dem öffentlichen Schlüssel entspricht, der in den client_metadata der Anfrage gesendet wurde, um das JWE zu entschlüsseln. Dadurch erhalten Sie ein vp_token.
Beispiel für 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 führt zu einem vp_token-JSON mit den
Anmeldedaten.
{
"vp_token":
{
"cred1": ["<base64UrlNoPadding_encoded_credential>"] // This applies to OpenID4VP 1.0 spec.
}
}
Sitzungsprotokoll erstellen
Als Nächstes erstellen Sie das SessionTranscript aus ISO/IEC 18013-5:2021 mit einer Android- oder webspezifischen Übergabestruktur:
SessionTranscript = [ null, // DeviceEngagementBytes not available null, // EReaderKeyBytes not available [ "OpenID4VPDCAPIHandover", AndroidHandoverDataBytes // BrowserHandoverDataBytes for Web ] ]Für Android- und Web-Übergaben müssen Sie dieselbe Nonce verwenden, die Sie zum Generieren von
credential_requestverwendet haben.Android-Übergabe
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()
Browserübergabe
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()
Mit dem
SessionTranscriptmuss die Geräteantwort gemäß ISO/IEC 18013-5:2021, Abschnitt 9, validiert werden. Dazu sind mehrere Schritte erforderlich, z. B.:Zertifikat des Ausstellers des Ausweises prüfen. Informationen zu den unterstützten IACA-Zertifikaten des Ausstellers finden Sie hier.
MSO-Signatur bestätigen (18013-5, Abschnitt 9.1.2)
ValueDigestsfür Datenelemente berechnen und prüfen (18013-5, Abschnitt 9.1.2)deviceSignature-Signatur bestätigen (18013-5, Abschnitt 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
}
Datenschutzfreundliche Altersüberprüfung (Zero-Knowledge Proof, ZKP)
Wenn Sie Zero-Knowledge Proofs unterstützen möchten (z.B. um zu bestätigen, dass ein Nutzer über 18 Jahre alt ist, ohne sein genaues Geburtsdatum zu sehen), ändern Sie das Anfrageformat in mso_mdoc_zk und geben Sie die erforderliche zk_system_type-Konfiguration an.
Eine allgemeine Übersicht über ZKP und seine Funktionen finden Sie in den FAQs.
...
"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
{
...
Sie erhalten einen verschlüsselten Zero-Knowledge Proof vom Wallet zurück. Sie können diesen Proof mit den IACA-Zertifikaten der Aussteller mithilfe der Longfellow-zk-Bibliothek von Google validieren.
Der Verifier-Dienst enthält einen bereitstellungsfertigen, Docker-basierten Server, mit dem Sie die Antwort mit bestimmten IACA-Zertifikaten des Ausstellers validieren können.
Sie können die Datei „certs.pem“ ändern, um IACA-Ausstellerzertifikate zu verwalten, denen Sie vertrauen möchten.
Ressourcen und Support
- FAQs:Antworten auf häufig gestellte Fragen zur technischen Integration finden Sie in den FAQs zu digitaler Identität und Anmeldedaten.
- Referenzimplementierung: Sehen Sie sich unsere Referenzimplementierung für Identitätsprüfer auf GitHub an.
- Testwebsite:Testen Sie den End-to-End-Ablauf unter verifier.multipaz.org.
- OpenID4VP-Spezifikation:Sehen Sie sich die technische Spezifikation für OpenID4VP an.
- Support:Wenn Sie Hilfe bei der Fehlerbehebung benötigen oder Fragen zur Integration haben, wenden Sie sich an
wallet-identity-rp-support@google.com.