In diesem Leitfaden wird erläutert, wie vertrauende Seiten und Entwickler von Lesegeräten die persönliche (Offline-)Überprüfung von digitalen Ausweisen, die in Google Wallet vorgelegt werden, gemäß dem internationalen Standard ISO/IEC 18013-5 implementieren können.
Digitale Berechtigungsnachweise in Google Wallet können in physischen Umgebungen (z. B. an POS-Terminals, Veranstaltungsorten, Fahrkartenschranken, Lesegeräten von Strafverfolgungsbehörden und mobilen Lese-Apps) sicher überprüft werden, ohne dass zum Zeitpunkt der Vorlage eine aktive Internetverbindung erforderlich ist.
Übersicht über die Offline-Präsentation (ISO/IEC 18013-5)
Der Standard ISO/IEC 18013-5 definiert ein standardisiertes, interoperables Protokoll für die Offline-Präsentation zwischen einem Inhaber (dem Mobilgerät des Nutzers, auf dem Google Wallet ausgeführt wird) und einem Lesegerät / Prüfer (einem physischen Terminal oder einer Companion-App).
Der Präsentationsablauf erfolgt in verschiedenen Phasen:
- Geräteinteraktion: Das Lesegerät und Wallet stellen den ersten Kontakt über NFC-Tippen (statische oder ausgehandelte Übergabe) oder QR-Code-Scan her. In dieser Phase werden Metadaten zur Geräteinteraktion und der sitzungsspezifische öffentliche Schlüssel des Lesegeräts (
EReaderKey) ausgetauscht. - Datentransportverbindung:Es wird ein sicherer, verschlüsselter Bluetooth Low Energy (BLE)-Kanal ausgehandelt, wobei das Lesegerät im Central Client- oder Peripheral Server-Modus agiert.
- Geräteanfrage:Das Lesegerät überträgt eine CBOR-codierte
DeviceRequest, in der der angeforderte Dokumenttyp (z. B.org.iso.18013.5.1.mDL) sowie die angeforderten Namespaces und Datenelemente angegeben sind. - Nutzereinwilligung und Geräteauthentifizierung: Google Wallet fordert den Nutzer auf, die angeforderten Datenelemente zu prüfen und die Weitergabe per biometrischer Authentifizierung oder Displaysperre zu bestätigen.
- Geräteantwort und kryptografische Überprüfung:Das Wallet sendet eine CBOR-codierte
DeviceResponsezurück, die das signierte Mobile Security Object (MSO) und vom Gerät signierte Datenelemente enthält. Das Lesegerät verifiziert die kryptografischen Signaturen anhand vertrauenswürdiger Root-Zertifikate.
Das Multipaz Open-Source SDK
Für die Implementierung einer Lesegerät- oder Prüf-App empfiehlt Google die Verwendung von Multipaz, einem Open-Source-Kotlin-Multiplatform-SDK (KMP), das ursprünglich von Google entwickelt und der OpenWallet Foundation (OWF) zur Verfügung gestellt wurde.
Multipaz bietet eine produktionsreife Implementierung von ISO/IEC 18013-5-Lesegerät- und Wallet-Protokollen, kryptografischen Verifizierungspipelines, CBOR-Codierung/-Decodierung und erweiterbaren Dokumenttypschemas.
Multipaz in Ihre Lesegerät-App einbinden
In den folgenden Schritten wird gezeigt, wie Sie das Multipaz SDK in eine Android-Lesegerät-App einbinden.
Schritt 1: Abhängigkeiten hinzufügen
Multipaz-Bibliotheken werden auf Maven Central veröffentlicht. Fügen Sie der Datei build.gradle.kts Ihrer Anwendung die erforderlichen Module hinzu:
// build.gradle.kts
dependencies {
// Core Multipaz library (protocol engine, CBOR, crypto)
implementation("org.multipaz:multipaz:0.100.0")
// Android-specific platform bindings (NFC, BLE, Keystore)
implementation("org.multipaz:multipaz-android:0.100.0")
// Standardized document types (mDL, EU PID, etc.)
implementation("org.multipaz:multipaz-doctypes:0.100.0")
}
Schritt 2: Android-Berechtigungen konfigurieren
Für die persönliche Überprüfung sind Hardwareberechtigungen für die NFC-Nutzung, das Scannen mit der Kamera (für die QR-Nutzung) und die Datenübertragung über Bluetooth Low Energy erforderlich. Fügen Sie Ihrer AndroidManifest.xml die folgenden Berechtigungen hinzu:
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
<!-- NFC Engagement -->
<uses-permission android:name="android.permission.NFC" />
<uses-feature android:name="android.hardware.nfc" android:required="false" />
<!-- Camera for QR Code Engagement -->
<uses-permission android:name="android.permission.CAMERA" />
<uses-feature android:name="android.hardware.camera" android:required="false" />
<!-- Bluetooth Low Energy Transport (Android 12+) -->
<uses-permission android:name="android.permission.BLUETOOTH_SCAN"
android:usesPermissionFlags="neverForLocation" />
<uses-permission android:name="android.permission.BLUETOOTH_CONNECT" />
<uses-permission android:name="android.permission.BLUETOOTH_ADVERTISE" />
<!-- Legacy Bluetooth Permissions for Android 11 and lower -->
<uses-permission android:name="android.permission.BLUETOOTH"
android:maxSdkVersion="30" />
<uses-permission android:name="android.permission.BLUETOOTH_ADMIN"
android:maxSdkVersion="30" />
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"
android:maxSdkVersion="30" />
</manifest>
Schritt 3: Verification Engine initialisieren
Verwenden Sie die Multipaz-Abstraktionen VerificationHelper oder Reader-Verbindung, um Engagement-Ereignisse zu verarbeiten und den Lebenszyklus der BLE-Kommunikation zu verwalten:
import org.multipaz.verification.VerificationHelper
import org.multipaz.cbor.Cbor
import org.multipaz.crypto.Crypto
class ReaderManager(private val context: android.content.Context) {
private var verificationHelper: VerificationHelper? = null
fun startListeningForEngagement() {
verificationHelper = VerificationHelper.Builder(
context = context,
listener = object : VerificationHelper.Listener {
override fun onDeviceConnected() {
// BLE channel established, send request
sendDeviceRequest()
}
override fun onResponseReceived(deviceResponseBytes: ByteArray) {
// Process and verify the received credential payload
handleDeviceResponse(deviceResponseBytes)
}
override fun onError(error: Throwable) {
// Handle transport or protocol errors
}
override fun onDeviceDisconnected(transportTransportSpecificTermination: Boolean) {
// Connection closed
}
}
).build()
}
}
Schritt 4: DeviceRequest erstellen
Geben Sie den Dokumenttyp und die einzelnen Datenelemente an, die Ihre Anwendung überprüfen muss. Wenden Sie immer das Prinzip der minimalen Offenlegung an (z.B. nur age_over_21 statt des vollständigen birth_date bei der Altersbestätigung anfordern):
fun sendDeviceRequest() {
// Specify the DocType and requested elements
val docType = "org.iso.18013.5.1.mDL"
val namespace = "org.iso.18013.5.1"
// Map of requested elements: elementName -> intentToRetain
val requestedElements = mapOf(
"family_name" to false,
"given_name" to false,
"age_over_21" to false,
"portrait" to false,
"driving_privileges" to false
)
// Build ISO/IEC 18013-5 DeviceRequest structure
val deviceRequestBytes = verificationHelper?.buildDeviceRequest(
docType = docType,
itemsToRequest = mapOf(namespace to requestedElements)
)
if (deviceRequestBytes != null) {
verificationHelper?.sendDeviceRequest(deviceRequestBytes)
}
}
Unterstützung zusätzlicher Dokumenttypen (DocTypes)
Multipaz unterstützt standardisierte Ausweise und bietet eine erweiterbare Architektur, um benutzerdefinierte oder domainspezifische Dokumenttypen anzufordern.
1. Integrierte Standarddokumenttypen
Die multipaz-doctypes-Bibliothek bietet vordefinierte Schemamodelle für Standardanmeldedaten:
| Dokumenttyp-ID | Standard / Umfang | Typischer Namespace | Gemeinsame Elemente |
|---|---|---|---|
org.iso.18013.5.1.mDL |
ISO/IEC 18013-5 – Digitaler Führerschein | org.iso.18013.5.1 |
family_name, given_name, birth_date, issue_date, expiry_date, issuing_authority, document_number, portrait, driving_privileges, age_over_18, age_over_21 |
eu.europa.ec.eudi.pid.1 |
Personenidentifikationsdaten der EU-Brieftasche für die digitale Identität (EUDIW) | eu.europa.ec.eudi.pid.1 |
family_name, first_name, birth_date, nationality, issuing_country, issuing_authority, personal_administrative_number |
com.google.wallet.idcard.1 |
Google Wallet-ID-Karte / Test-IDs | com.google.wallet.idcard.1 |
given_name, family_name, birth_date, document_number, portrait |
2. Benutzerdefinierte Dokumenttypen anfordern
Wenn Sie benutzerdefinierte Dokumenttypen anfordern möchten, definieren Sie beim Erstellen der DeviceRequest den Zielstring docType und die entsprechenden Namespace-Zuordnungen:
// Example: Requesting a custom event ticket credential
val customDocType = "com.example.events.ticket"
val customNamespace = "com.example.events.ticket.1"
val customRequestedItems = mapOf(
"ticket_id" to false,
"event_name" to false,
"seat_section" to false,
"vip_access" to false
)
val multiDocRequestBytes = verificationHelper?.buildMultiDocDeviceRequest(
documents = listOf(
DocumentRequest(
docType = customDocType,
namespaces = mapOf(customNamespace to customRequestedItems)
)
)
)
Kryptografische Überprüfung und Vertrauensverwaltung
Der Empfang einer Antwortnutzlast ist nur der erste Schritt. Lesegeräte müssen eine vierstufige kryptografische Überprüfung durchführen, um die Echtheit und Integrität des vorgelegten Berechtigungsnachweises zu bestätigen.
| Bestätigungsschritt | Überprüfungsziel |
|---|---|
| 1. Ausstellerauthentifizierung | IssuerAuth (COSE_Sign1) mit vertrauenswürdigen IACA-Root-Zertifikaten vergleichen |
| 2. Gültigkeitszeitraum prüfen | Achten Sie darauf, dass validFrom ≤ aktuelle Zeit ≤ validUntil ist. |
| 3. Datenintegritätsprüfung | SHA-256-Digests der zurückgegebenen Elemente berechnen und mit MSO ValueDigests abgleichen |
| 4. Geräteauthentifizierung | DeviceSigned-Signatur oder MAC mit dem an die SessionTranscript gebundenen DeviceKey prüfen |
1. Die 4-stufige Verifizierungspipeline
- Ausstellerauthentifizierung (
IssuerAuth):- Das Mobile Security Object (MSO) wird von der ausstellenden Behörde signiert (
IssuerAuth-Nutzlast). - Das Lesegerät verifiziert die
COSE_Sign1-Signatur mithilfe des Document Signer-Zertifikats und stellt sicher, dass die Zertifikatskette bis zu einem vertrauenswürdigen Root-Zertifikat der Issuing Authority CA (IACA) reicht.
- Das Mobile Security Object (MSO) wird von der ausstellenden Behörde signiert (
- Überprüfung des Gültigkeitszeitraums:
- Das Lesegerät vergleicht die Zeitstempel
validityInfo.validFromundvalidityInfo.validUntilim MSO mit der aktuellen Uhrzeit des Lesegeräts, um sicherzustellen, dass der Berechtigungsnachweis nicht abgelaufen ist.
- Das Lesegerät vergleicht die Zeitstempel
- Überprüfung der Datenintegrität (
ValueDigests):- Für jedes empfangene
IssuerSignedItemberechnet das Lesegerät den Digest (z.B. SHA-256) und prüft, ob er mit dem entsprechenden Hash-Eintrag imValueDigests-Wörterbuch des MSO übereinstimmt.
- Für jedes empfangene
- Geräteauthentifizierung (
DeviceSigned):- Das Lesegerät prüft, ob das Gerät, das den Berechtigungsnachweis vorlegt, den privaten Schlüssel hat, der dem im signierten MSO veröffentlichten
DeviceKeyentspricht. - Dazu wird der
DeviceAuth(entwederDeviceSignatureoderDeviceMac) über denSessionTranscriptverifiziert, die Sitzung an den sitzungsspezifischen Schlüssel des Lesegeräts gebunden und Replay- und Man-in-the-Middle-Angriffe verhindert.
- Das Lesegerät prüft, ob das Gerät, das den Berechtigungsnachweis vorlegt, den privaten Schlüssel hat, der dem im signierten MSO veröffentlichten
2. Vertrauenswürdige IACA-Root-Zertifikate verwalten
Produktionslesegeräte müssen einen sicheren, lokalen Trust Store mit vertrauenswürdigen IACA-Root-Zertifikaten enthalten:
- Produktions-IACA-Zertifikate:Laden Sie Root-Zertifikate von offiziellen Ausstellungsstellen herunter und konfigurieren Sie sie. Weitere Informationen finden Sie in der Liste Unterstützte Aussteller und IACA-Zertifikate.
- AAMVA VICAL:In den USA können Lesesysteme in den VICAL-Dienst (Verified Issuer Certificate Authority List) der American Association of Motor Vehicle Administrators (AAMVA) eingebunden werden, um die Trust-Anchor der Bundesstaaten automatisch zu synchronisieren.
- Sandbox-Test-Roots: Wenn du mit Sandbox-Anmeldedaten testest, muss das Lesegerät der Google Sandbox IACA Root vertrauen.
import org.multipaz.crypto.X509Cert
// Configure trusted IACA certificates in the trust store
val trustedCertificates = mutableListOf<X509Cert>()
// Add official state IACA certificates
trustedCertificates.add(X509Cert.fromPem(sampleStateIacaPem))
// Add Google Sandbox IACA root certificate for testing
trustedCertificates.add(X509Cert.fromPem(googleSandboxIacaPem))
val verifier = MultipazVerifier(trustStore = trustedCertificates)
val verificationResult = verifier.verify(deviceResponseBytes, sessionTranscript)
if (verificationResult.isIssuerAuthorized && verificationResult.isDeviceAuthenticated) {
// Credential is valid and authentic
} else {
// Reject presentation: cryptographic validation failed
}
Leserauthentifizierung (empfohlen)
Mit der Leserauthentifizierung kann die Leser-App ihre Identität gegenüber Google Wallet kryptografisch nachweisen, indem sie die ReaderAuthentication-Struktur mit einem autorisierten X.509-Leserzertifikat signiert.
- Warum es empfohlen wird: Mit der Leserauthentifizierung kann Ihre Leser-App oder Ihr Terminal dem Nutzer eine vertrauenswürdige Identität präsentieren. Für grundlegende öffentliche Attribute (z. B. die Bestätigung von
age_over_21) ist dies optional, wird aber für Lesegerät-Integrationen dringend empfohlen, um das Vertrauen der Nutzer zu stärken. Bei der Abfrage sensibler Attribute (z. B. der vollständigen Sozialversicherungsnummer, der Wohnadresse oder bestimmter staatlicher Bestätigungen) kann dies gesetzlich oder durch Richtlinien vorgeschrieben sein. - So funktioniert es: Das Lesegerät fügt seine Zertifikatskette hinzu und signiert das Sitzungstranskript. Google Wallet zeigt dem Nutzer vor der Datenfreigabe auf dem Zustimmungsbildschirm die bestätigte Identität und den Namen der Organisation des Lesegeräts an.
Test- und Entwicklungstools
Verwenden Sie die folgenden Entwicklertools und Referenzimplementierungen, um die Integration zu beschleunigen:
- Multipaz-Referenz-Apps:
- Klonen Sie das Multipaz-Repository und führen Sie die Android-Beispiel-App
IdentityReaderaus, um die Abläufe für die physische Überprüfung zu testen.
- Klonen Sie das Multipaz-Repository und führen Sie die Android-Beispiel-App
- Test-ID in Google Wallet erstellen:
- Folgen Sie unserer Anleitung unter Digitalen Identitätsnachweis in Google Wallet erstellen, um mit dem Utopia ePassport Simulator einen simulierten digitalen Test-Identitätsnachweis in Google Wallet bereitzustellen.
- Webbasierte Verifizierungstests:
- Mit verifier.multipaz.org können Sie CBOR-Anfragen prüfen, Anspruchsabfragen untersuchen und webbasierte Präsentationen nach W3C / ISO 18013-7 testen.
Fehlerbehebung und Diagnose vor Ort
In der folgenden Tabelle sind häufige Probleme bei der Offlinebestätigung und empfohlene Lösungen aufgeführt:
| Problem / Symptom | Ursache | Empfohlene Lösung |
|---|---|---|
| Zeitüberschreitung der BLE-Verbindung / Verbindungsfehler |
|
|
| NFC-Funktion schlägt fehl oder wird abgebrochen | Der Nutzer entfernt das Mobilgerät von der Antenne des Lesegeräts, bevor der BLE-Übergabedatensatz vollständig übertragen wurde. |
|
UNTRUSTED_ISSUER / Fehler bei der Zertifikatskette |
Das Zertifikat des Dokumentenunterzeichners ist nicht mit einem vertrauenswürdigen IACA-Zertifikat im lokalen Trust Store des Lesegeräts verknüpft. |
|
INVALID_VALIDITY_INFO / Abgelaufenes MSO |
|
|
DEVICE_AUTHENTICATION_FAILED |
Das Sitzungstranskript zwischen Lesegerät und Wallet stimmt nicht überein oder die temporäre Gerätesignatur ist ungültig. |
|
| Absturz aufgrund von Berechtigungen unter Android 12 und höher | Die Anwendung hat versucht, ohne Laufzeitberechtigungen über BLE zu scannen oder zu werben. |
|
UX- und Datenschutzrichtlinien für Lesegeräte für den persönlichen Gebrauch
Beim Entwerfen von physischen Lesegeräten und Companion-Apps:
- Selektive Offenlegung in der Benutzeroberfläche:Zeigen Sie dem Betreiber nur die Entscheidung oder das mindestens erforderliche Attribut an (z.B. ein auffälliges grünes Häkchen und „Alter 21+ bestätigt“ anstelle des vollständigen Geburtsdatums, der Adresse und der Führerscheinnummer des Nutzers).
- Klare Indikatoren für die physische Interaktion:Kennzeichnen Sie den NFC-Zielbereich deutlich und zeigen Sie visuelle Hinweise (z. B. Animationen oder Fortschrittsanzeigen) für jede Phase an: Tippen / Scannen → Verbinden → Verifizieren → Abgeschlossen.
- Umgang mit flüchtigen Daten:Speichere oder protokolliere keine personenbezogenen Daten, die du von der Wallet erhältst, es sei denn, dies ist ausdrücklich durch geltendes Recht vorgeschrieben und wird über
intentToRetain = trueoffengelegt.