Akceptacja dokumentów cyfrowych w formie fizycznej (offline)

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:

  1. 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).
  2. 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).
  3. Żądanie urządzenia: czytnik przesyła zakodowane w formacie CBOR żądanie DeviceRequest określające żądany typ dokumentu (np. org.iso.18013.5.1.mDL) oraz konkretne przestrzenie nazw i elementy danych.
  4. 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.
  5. Odpowiedź urządzenia i weryfikacja kryptograficzna: portfel odsyła zakodowaną w formacie CBOR odpowiedź DeviceResponse zawierają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

  1. Uwierzytelnianie wydawcy (IssuerAuth):
    • Obiekt Mobile Security Object (MSO) jest podpisany przez urząd wydający (IssuerAuth payload).
    • Czytnik weryfikuje podpis COSE_Sign1 za pomocą certyfikatu podpisującego dokument i sprawdza, czy łańcuch certyfikatów prowadzi do zaufanego certyfikatu głównego urzędu certyfikacji (IACA).
  2. Weryfikacja okresu ważności:
    • Czytnik sprawdza sygnatury czasowe validityInfo.validFrom i validityInfo.validUntil w obiekcie MSO na podstawie bieżącego zegara czytnika, aby upewnić się, że dokument tożsamości nie stracił ważności.
  3. Weryfikacja integralności danych (ValueDigests):
    • W przypadku każdego otrzymanego elementu IssuerSignedItem czytnik oblicza jego skrót (np. SHA-256) i sprawdza, czy pasuje on do odpowiedniego wpisu skrótu w słowniku ValueDigests obiektu MSO.
  4. Uwierzytelnianie urządzenia (DeviceSigned):
    • Czytnik sprawdza, czy urządzenie prezentujące dokument tożsamości ma klucz prywatny odpowiadający kluczowi DeviceKey opublikowanemu w podpisanym obiekcie MSO.
    • Odbywa się to przez sprawdzenie DeviceAuth (DeviceSignature lub DeviceMac) w SessionTranscript, powiązanie sesji z efemerycznym kluczem czytnika i zapobieganie atakom typu replay i „man in the middle”.

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:

  1. Aplikacje referencyjne Multipaz:
    • Skopiuj repozytorium Multipaz i uruchom przykładową aplikację na Androida IdentityReader, aby przetestować procesy weryfikacji fizycznej.
  2. Utwórz testowy dokument tożsamości w Portfelu Google:
  3. 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
  • Zakłócenia RF w środowiskach o dużej gęstości.
  • Niezgodność trybu peryferyjnego i centralnego na określonym sprzęcie czytnika.
  • Przekroczenie limitu czasu skanowania.
  • Sprawdź, czy czytnik obsługuje tryby klienta centralnego BLE i serwera peryferyjnego.
  • Dostosuj okno i interwał skanowania BLE, aby agresywnie skanować podczas aktywnego nawiązywania połączenia.
  • Sprawdź, czy negocjacja rozmiaru MTU zakończyła się pomyślnie.
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.
  • Gdy tylko rozpocznie się nawiązywanie połączenia NFC, natychmiast wyświetlaj na terminalu informacje wizualne, dźwiękowe lub haptyczne.
  • Poproś użytkowników, aby przytrzymali telefon przy celu NFC, aż zostanie nawiązane połączenie BLE.
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.
  • Sprawdź, czy certyfikat główny wydawcy jest załadowany do magazynu zaufania czytnika.
  • Jeśli testujesz w piaskownicy, sprawdź, czy załadowany jest główny certyfikat IACA w piaskownicy Google.
  • Sprawdź, czy listy certyfikatów IACA (np. AAMVA VICAL) są okresowo aktualizowane.
INVALID_VALIDITY_INFO / obiekt MSO stracił ważność
  • Zegar systemowy czytnika jest niezsynchronizowany.
  • Podpis MSO stracił ważność.
  • Sprawdź, czy urządzenie czytnika regularnie synchronizuje czas systemowy za pomocą protokołu NTP.
  • Poproś użytkownika, aby otworzył Portfel Google, gdy jest połączony z internetem, aby odświeżyć tokeny dokumentów tożsamości.
DEVICE_AUTHENTICATION_FAILED Niezgodność transkrypcji sesji między czytnikiem a portfelem lub nieprawidłowy efemeryczny podpis urządzenia.
  • Sprawdź, czy dokładne surowe bajty DeviceEngagementBytes i EReaderKeyBytes są zachowane w strukturze SessionTranscript bez ponownego kodowania.
Awaria uprawnień na Androidzie 12 lub nowszym Aplikacja próbowała skanować lub reklamować się przez BLE bez uprawnień w czasie działania.
  • Przed rozpoczęciem sesji czytnika sprawdź i poproś o uprawnienia BLUETOOTH_SCAN, BLUETOOTH_CONNECT i BLUETOOTH_ADVERTISE 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.