Z tego przewodnika dowiesz się, jak podmioty polegające na weryfikacji i programiści czytników mogą wdrażać weryfikację cyfrowych dokumentów tożsamości prezentowanych z Portfela Google w trybie stacjonarnym (offline) zgodnie z międzynarodowym standardem ISO/IEC 18013-5.
Cyfrowe dokumenty tożsamości w Portfelu Google można bezpiecznie weryfikować w środowiskach fizycznych (takich jak terminale płatnicze, miejsca wydarzeń, bramki transportu publicznego, czytniki organów ścigania i aplikacje czytników mobilnych) bez aktywnego połączenia z internetem w momencie prezentacji.
Omówienie prezentacji offline (ISO/IEC 18013-5)
Standard ISO/IEC 18013-5 określa ustandaryzowany, interoperacyjny protokół prezentacji offline między posiadaczem (urządzeniem mobilnym użytkownika z Portfelem Google) a czytnikiem lub weryfikatorem (terminalem fizycznym lub towarzyszącą aplikacją mobilną).
Proces prezentacji przebiega w kilku etapach:
- Nawiązanie połączenia z urządzeniem: czytnik i portfel nawiązują wstępny kontakt za pomocą technologii zbliżeniowej NFC (przekazanie statyczne lub negocjowane) lub zeskanowania kodu QR. Na tym etapie następuje wymiana metadanych dotyczących zaangażowania urządzenia oraz efemerycznego klucza publicznego czytnika (
EReaderKey). - Połączenie do przesyłania danych: negocjowany jest bezpieczny, zaszyfrowany kanał Bluetooth Low Energy (BLE) (czytnik działa w trybie klienta centralnego lub serwera peryferyjnego).
- Żądanie urządzenia: czytnik przesyła zakodowane w formacie CBOR żądanie
DeviceRequestokreślające żądany typ dokumentu (np.org.iso.18013.5.1.mDL) oraz konkretne przestrzenie nazw i elementy danych. - Zgoda użytkownika i uwierzytelnianie urządzenia: Portfel Google prosi użytkownika o sprawdzenie żądanych elementów danych i potwierdzenie udostępniania za pomocą uwierzytelniania biometrycznego lub blokady ekranu.
- Odpowiedź urządzenia i weryfikacja kryptograficzna: portfel odsyła zakodowaną w formacie CBOR odpowiedź
DeviceResponsezawierającą podpisany obiekt Mobile Security Object (MSO) i elementy danych podpisane przez urządzenie. Czytnik weryfikuje podpisy kryptograficzne na podstawie zaufanych certyfikatów głównych.
Pakiet Multipaz Open-Source SDK
Aby wdrożyć aplikację czytnika lub weryfikatora, Google zaleca użycie Multipaz, pakietu SDK Kotlin Multiplatform (KMP) o otwartym kodzie źródłowym, który został pierwotnie opracowany przez Google i przekazany do OpenWallet Foundation (OWF).
Multipaz zapewnia gotową do użycia implementację protokołów czytnika i portfela ISO/IEC 18013-5, potoków weryfikacji kryptograficznej, kodowania i dekodowania CBOR oraz rozszerzalnych schematów typów dokumentów.
Integracja pakietu Multipaz z aplikacją czytnika
Poniższe czynności pokazują, jak zintegrować pakiet Multipaz SDK z aplikacją czytnika na Androida.
Krok 1. Dodaj zależności
Biblioteki Multipaz są publikowane w Centralnym repozytorium Maven. Dodaj wymagane moduły do pliku build.gradle.kts aplikacji:
// 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")
}
Krok 2. Skonfiguruj uprawnienia na Androida
Weryfikacja stacjonarna wymaga uprawnień sprzętowych do nawiązywania połączenia NFC, skanowania aparatem (w przypadku nawiązywania połączenia za pomocą kodu QR) i przesyłania danych przez Bluetooth Low Energy. Dodaj te uprawnienia do pliku AndroidManifest.xml:
<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>
Krok 3. Zainicjuj silnik weryfikacji
Użyj abstrakcji VerificationHelper lub połączenia czytnika w Multipaz, aby obsługiwać zdarzenia zaangażowania i zarządzać cyklem życia komunikacji BLE:
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()
}
}
Krok 4. Utwórz żądanie urządzenia
Określ typ dokumentu i poszczególne elementy danych, które aplikacja musi zweryfikować. Zawsze stosuj zasadę minimalnego ujawniania (np. podczas weryfikacji wieku żądaj tylko age_over_21, a nie pełnej birth_date):
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)
}
}
Obsługa dodatkowych typów dokumentów (DocTypes)
Multipaz obsługuje standardowe dokumenty tożsamości od razu po zainstalowaniu i udostępnia rozszerzalną architekturę do żądania niestandardowych lub specyficznych dla domeny typów dokumentów.
1. Wbudowane standardowe typy dokumentów
Biblioteka multipaz-doctypes zawiera predefiniowane modele schematów standardowych dokumentów tożsamości:
| Identyfikator typu dokumentu | Standard / zakres | Typowa przestrzeń nazw | Typowe elementy |
|---|---|---|---|
org.iso.18013.5.1.mDL |
Cyfrowe prawo jazdy ISO/IEC 18013-5 | 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 |
Dane identyfikacyjne osoby w europejskim portfelu tożsamości cyfrowej (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 |
Cyfrowy dokument tożsamości w Portfelu Google / testowe dokumenty tożsamości | com.google.wallet.idcard.1 |
given_name, family_name, birth_date, document_number, portrait |
2. Żądanie niestandardowych typów dokumentów
Aby zażądać niestandardowych typów dokumentów, podczas tworzenia żądania DeviceRequest zdefiniuj docelowy ciąg docType i odpowiednie mapowania przestrzeni nazw:
// 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)
)
)
)
Weryfikacja kryptograficzna i zarządzanie zaufaniem
Otrzymanie ładunku odpowiedzi to dopiero pierwszy krok. Czytniki muszą przeprowadzić 4-etapową weryfikację kryptograficzną, aby sprawdzić autentyczność i integralność prezentowanego dokumentu tożsamości.
| Etap weryfikacji | Cel weryfikacji |
|---|---|
| 1. Uwierzytelnianie wydawcy | Sprawdź IssuerAuth (COSE_Sign1) na podstawie zaufanych certyfikatów głównych IACA. |
| 2. Sprawdzanie okresu ważności | Sprawdź, czy validFrom ≤ bieżący czas ≤ validUntil. |
| 3. Sprawdzanie integralności danych | Oblicz skróty SHA-256 zwróconych elementów i porównaj je z ValueDigests w obiekcie MSO. |
| 4. Uwierzytelnianie urządzenia | Sprawdź podpis DeviceSigned lub MAC za pomocą DeviceKey powiązanego z SessionTranscript. |
1. 4-etapowy potok weryfikacji
- Uwierzytelnianie wydawcy (
IssuerAuth):- Obiekt Mobile Security Object (MSO) jest podpisany przez urząd wydający (
IssuerAuthpayload). - Czytnik weryfikuje podpis
COSE_Sign1za pomocą certyfikatu podpisującego dokument i sprawdza, czy łańcuch certyfikatów prowadzi do zaufanego certyfikatu głównego urzędu certyfikacji (IACA).
- Obiekt Mobile Security Object (MSO) jest podpisany przez urząd wydający (
- Weryfikacja okresu ważności:
- Czytnik sprawdza sygnatury czasowe
validityInfo.validFromivalidityInfo.validUntilw obiekcie MSO na podstawie bieżącego zegara czytnika, aby upewnić się, że dokument tożsamości nie stracił ważności.
- Czytnik sprawdza sygnatury czasowe
- Weryfikacja integralności danych (
ValueDigests):- W przypadku każdego otrzymanego elementu
IssuerSignedItemczytnik oblicza jego skrót (np. SHA-256) i sprawdza, czy pasuje on do odpowiedniego wpisu skrótu w słownikuValueDigestsobiektu MSO.
- W przypadku każdego otrzymanego elementu
- Uwierzytelnianie urządzenia (
DeviceSigned):- Czytnik sprawdza, czy urządzenie prezentujące dokument tożsamości ma klucz prywatny odpowiadający kluczowi
DeviceKeyopublikowanemu w podpisanym obiekcie MSO. - Odbywa się to przez sprawdzenie
DeviceAuth(DeviceSignaturelubDeviceMac) wSessionTranscript, powiązanie sesji z efemerycznym kluczem czytnika i zapobieganie atakom typu replay i „man in the middle”.
- Czytnik sprawdza, czy urządzenie prezentujące dokument tożsamości ma klucz prywatny odpowiadający kluczowi
2. Zarządzanie zaufanymi certyfikatami głównymi IACA
Czytniki produkcyjne muszą utrzymywać bezpieczny, lokalny magazyn zaufania zawierający zaufane certyfikaty główne IACA:
- Certyfikaty IACA w środowisku produkcyjnym: pobierz i skonfiguruj certyfikaty główne z oficjalnych urzędów wydających. Zapoznaj się z listą obsługiwanych wydawców i certyfikatów IACA.
- AAMVA VICAL: w przypadku jurysdykcji w Stanach Zjednoczonych systemy czytników mogą integrować się z usługą American Association of Motor Vehicle Administrators (AAMVA) Verified Issuer Certificate Authority List (VICAL), aby automatycznie synchronizować kotwice zaufania stanu.
- Główne certyfikaty testowe w piaskownicy: podczas testowania na podstawie dokumentów tożsamości w piaskownicy upewnij się, że czytnik ufa głównemu certyfikatowi IACA w piaskownicy Google.
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
}
Uwierzytelnianie czytnika (zalecane)
Uwierzytelnianie czytnika umożliwia aplikacji czytnika kryptograficzne potwierdzenie swojej tożsamości w Portfelu Google przez podpisanie struktury ReaderAuthentication za pomocą autoryzowanego certyfikatu czytnika X.509.
- Dlaczego jest to zalecane: uwierzytelnianie czytnika umożliwia aplikacji czytnika lub terminalowi przedstawienie użytkownikowi zaufanej tożsamości. Chociaż jest to opcjonalne w przypadku podstawowych atrybutów publicznych (np.weryfikacji
age_over_21), zdecydowanie zalecamy integrację czytnika, aby zwiększyć zaufanie użytkowników. Może to być wymagane przez prawo lub zasady w przypadku żądania atrybutów wrażliwych (takich jak pełny numer ubezpieczenia społecznego, adres zamieszkania lub konkretne potwierdzenia stanu). - Jak to działa: czytnik zawiera łańcuch certyfikatów i podpisuje transkrypcję sesji. Przed udostępnieniem danych Portfel Google wyświetla użytkownikowi na ekranie zgody zweryfikowaną tożsamość i nazwę organizacji czytnika.
Narzędzia do testowania i tworzenia aplikacji
Aby przyspieszyć integrację, użyj tych narzędzi dla deweloperów i implementacji referencyjnych:
- Aplikacje referencyjne Multipaz:
- Skopiuj repozytorium Multipaz i uruchom przykładową aplikację na Androida
IdentityReader, aby przetestować procesy weryfikacji fizycznej.
- Skopiuj repozytorium Multipaz i uruchom przykładową aplikację na Androida
- Utwórz testowy dokument tożsamości w Portfelu Google:
- Aby udostępnić symulowany testowy dokument tożsamości w Portfelu Google za pomocą symulatora ePassport Utopia, postępuj zgodnie z instrukcjami w przewodniku Tworzenie testowego dokumentu tożsamości w Portfelu Google.
- Testowanie weryfikatora internetowego:
- Użyj verifier.multipaz.org, aby sprawdzić żądania CBOR, zapoznać się z zapytaniami o dane i przetestować prezentacje internetowe W3C / ISO 18013-7.
Rozwiązywanie problemów i diagnostyka w terenie
W tabeli poniżej znajdziesz typowe problemy występujące podczas weryfikacji offline oraz zalecane rozwiązania:
| Problem / objaw | Główna przyczyna | Zalecane rozwiązanie |
|---|---|---|
| Przekroczenie limitu czasu połączenia BLE / nieudane połączenie |
|
|
| Nawiązanie połączenia za pomocą technologii zbliżeniowej NFC nie powiodło się lub zostało przerwane | Użytkownik odsuwa urządzenie mobilne od anteny czytnika, zanim rekord przekazania BLE zostanie w pełni przesłany. |
|
UNTRUSTED_ISSUER / błąd łańcucha certyfikatów |
Certyfikat podpisujący dokument nie jest powiązany z żadnym zaufanym certyfikatem IACA w lokalnym magazynie zaufania czytnika. |
|
INVALID_VALIDITY_INFO / obiekt MSO stracił ważność |
|
|
DEVICE_AUTHENTICATION_FAILED |
Niezgodność transkrypcji sesji między czytnikiem a portfelem lub nieprawidłowy efemeryczny podpis urządzenia. |
|
| Awaria uprawnień na Androidzie 12 lub nowszym | Aplikacja próbowała skanować lub reklamować się przez BLE bez uprawnień w czasie działania. |
|
Wskazówki dotyczące UX i prywatności w przypadku czytników stacjonarnych
Podczas projektowania czytników fizycznych i aplikacji towarzyszących:
- Stosuj selektywne ujawnianie w interfejsie: operatorowi wyświetlaj tylko decyzję lub minimalny wymagany atrybut (np. wyświetlaj widoczne zielone oznaczenie i "Wiek 21+ potwierdzony" zamiast pełnej daty urodzenia, adresu i numeru licencji użytkownika).
- Wyraźne wskaźniki interakcji fizycznej: wyraźnie oznacz strefę docelową NFC i wyświetlaj wskazówki wizualne (np. animacje lub paski postępu) pokazujące każdy etap: Dotknij / zeskanuj → Łączenie → Weryfikacja → Gotowe.
- Obsługa danych efemerycznych: nie przechowuj ani nie rejestruj elementów danych osobowych otrzymanych z portfela, chyba że jest to wyraźnie wymagane przez obowiązujące prawo i ujawnione za pomocą
intentToRetain = true.