Ce guide explique comment les parties de confiance et les développeurs de lecteurs peuvent implémenter la validation en personne (hors connexion) des identifiants numériques présentés depuis Google Wallet conformément à la norme internationale ISO/IEC 18013-5.
Les justificatifs numériques dans Google Wallet peuvent être vérifiés de manière sécurisée dans des environnements physiques (tels que des terminaux de point de vente, des lieux d'événements, des portiques de transport, des lecteurs des forces de l'ordre et des applications de lecture mobile) sans nécessiter de connexion Internet active au moment de la présentation.
Présentation de la présentation hors connexion (ISO/IEC 18013-5)
La norme ISO/CEI 18013-5 définit un protocole standardisé et interopérable pour la présentation hors connexion entre un titulaire (l'appareil mobile de l'utilisateur exécutant Google Wallet) et un lecteur / vérificateur (un terminal physique ou une application mobile associée).
Le flux de présentation se déroule en plusieurs phases distinctes :
- Engagement de l'appareil : le lecteur et le portefeuille établissent un premier contact via un appui NFC (transfert statique ou négocié) ou en scannant un QR code. Au cours de cette phase, les métadonnées d'engagement de l'appareil et la clé publique éphémère du lecteur (
EReaderKey) sont échangées. - Connexion de transport de données : un canal Bluetooth à basse consommation (BLE) sécurisé et chiffré est négocié (le lecteur agissant en mode client central ou serveur périphérique).
- Requête de l'appareil : le lecteur transmet un
DeviceRequestencodé en CBOR spécifiant le type de document demandé (par exemple,org.iso.18013.5.1.mDL), ainsi que les espaces de noms et les éléments de données spécifiques demandés. - Consentement de l'utilisateur et authentification de l'appareil : Google Wallet invite l'utilisateur à examiner les éléments de données demandés et à confirmer le partage à l'aide de l'authentification biométrique ou du verrouillage de l'écran.
- Réponse de l'appareil et validation cryptographique : le portefeuille renvoie un
DeviceResponseencodé en CBOR contenant l'objet de sécurité mobile (MSO) signé et les éléments de données signés par l'appareil. Le lecteur vérifie les signatures cryptographiques par rapport aux certificats racine de confiance.
SDK Multipaz Open-Source
Pour implémenter une application de lecture ou de vérification, Google recommande d'utiliser Multipaz, un SDK Kotlin Multiplatform (KMP) Open Source développé à l'origine par Google et fourni à l'OpenWallet Foundation (OWF).
Multipaz fournit une implémentation prête pour la production des protocoles de lecteur et de portefeuille ISO/CEI 18013-5, des pipelines de vérification cryptographique, de l'encodage/décodage CBOR et des schémas de types de documents extensibles.
Intégrer Multipaz à votre application de lecture
Les étapes suivantes montrent comment intégrer le SDK Multipaz dans une application de lecture Android.
Étape 1 : Ajoutez des dépendances
Les bibliothèques Multipaz sont publiées sur Maven Central. Ajoutez les modules requis au fichier build.gradle.kts de votre application :
// 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")
}
Étape 2 : Configurer les autorisations Android
La validation en personne nécessite des autorisations matérielles pour l'engagement NFC, la lecture par l'appareil photo (pour l'engagement QR) et le transport de données Bluetooth à basse consommation. Ajoutez les autorisations suivantes à votre 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>
Étape 3 : Initialiser le moteur de validation
Utilisez les abstractions de connexion VerificationHelper ou Reader de Multipaz pour gérer les événements d'engagement et le cycle de vie de la communication 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()
}
}
Étape 4 : Créez la DeviceRequest
Spécifiez le type de document et les éléments de données individuels que votre application doit valider. Appliquez toujours le principe de divulgation minimale (par exemple, en ne demandant que age_over_21 plutôt que birth_date complet lors de la validation de l'âge) :
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)
}
}
Prise en charge de types de documents supplémentaires (DocTypes)
Multipaz est compatible avec les identifiants standardisés et fournit une architecture extensible pour demander des types de documents personnalisés ou spécifiques à un domaine.
1. Types de documents standards intégrés
La bibliothèque multipaz-doctypes fournit des modèles de schéma prédéfinis pour les identifiants standards :
| Identifiant du type de document | Norme / Champ d'application | Espace de noms standard | Éléments communs |
|---|---|---|---|
org.iso.18013.5.1.mDL |
Permis de conduire mobile ISO/CEI 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 |
Données d'identification de la personne du portefeuille d'identité numérique de l'UE (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 |
Pass d'identité Google Wallet / ID de test | com.google.wallet.idcard.1 |
given_name, family_name, birth_date, document_number, portrait |
2. Demander des types de documents personnalisés
Pour demander des types de documents personnalisés, définissez la chaîne docType cible et les mappages d'espaces de noms correspondants lors de la construction de DeviceRequest :
// 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)
)
)
)
Validation cryptographique et gestion de la confiance
La réception d'une charge utile de réponse n'est que la première étape. Les lecteurs doivent effectuer une vérification cryptographique en quatre étapes pour valider l'authenticité et l'intégrité de l'identifiant présenté.
| Étape de validation | Cible de validation |
|---|---|
| 1. Authentification de l'émetteur | Valider IssuerAuth (COSE_Sign1) par rapport aux certificats racine IACA de confiance |
| 2. Vérification de la période de validité | Assurez-vous que validFrom ≤ heure actuelle ≤ validUntil. |
| 3. Contrôle de l'intégrité des données | Calculez les condensés SHA-256 des éléments renvoyés et comparez-les à l'ValueDigests du MSO. |
| 4. Authentification de l'appareil | Vérifier la signature ou le MAC de DeviceSigned à l'aide de DeviceKey lié à SessionTranscript |
1. Le pipeline de validation en quatre étapes
- Authentification de l'émetteur (
IssuerAuth) :- L'objet de sécurité mobile (MSO) est signé par l'autorité de certification (charge utile
IssuerAuth). - Le lecteur vérifie la signature
COSE_Sign1à l'aide du certificat du signataire du document et s'assure que le certificat est associé à un certificat racine d'autorité de certification émettrice (IACA) de confiance.
- L'objet de sécurité mobile (MSO) est signé par l'autorité de certification (charge utile
- Vérification de la période de validité :
- Le lecteur vérifie les codes temporels
validityInfo.validFrometvalidityInfo.validUntildans l'objet de signature mobile par rapport à l'horloge actuelle du lecteur pour s'assurer que l'identifiant n'a pas expiré.
- Le lecteur vérifie les codes temporels
- Vérification de l'intégrité des données (
ValueDigests) :- Pour chaque
IssuerSignedItemreçu, le lecteur calcule son condensé (par exemple, SHA-256) et vérifie qu'il correspond à l'entrée de hachage correspondante dans le dictionnaireValueDigestsde l'OSM.
- Pour chaque
- Authentification de l'appareil (
DeviceSigned) :- Le lecteur vérifie que l'appareil présentant la pièce d'identité détient la clé privée correspondant à la
DeviceKeypubliée dans l'objet de signature mobile (MSO) signé. - Pour ce faire, il vérifie le
DeviceAuth(DeviceSignatureouDeviceMac) sur leSessionTranscript, en associant la session à la clé éphémère du lecteur et en empêchant les attaques par rejeu et de type "man-in-the-middle".
- Le lecteur vérifie que l'appareil présentant la pièce d'identité détient la clé privée correspondant à la
2. Gérer les certificats racines IACA de confiance
Les lecteurs de production doivent disposer d'un magasin de confiance local sécurisé contenant des certificats racine IACA de confiance :
- Certificats IACA de production : téléchargez et configurez les certificats racine des autorités de certification officielles. Consultez notre liste d'émetteurs et de certificats IACA compatibles.
- AAMVA VICAL : pour les juridictions américaines, les systèmes de lecture peuvent s'intégrer au service VICAL (Verified Issuer Certificate Authority List) de l'AAMVA (American Association of Motor Vehicle Administrators) pour synchroniser automatiquement les ancres de confiance des États.
- Racines de test du bac à sable : lorsque vous effectuez des tests avec des identifiants de bac à sable, assurez-vous que le lecteur fait confiance à la racine IACA du bac à sable 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
}
Authentification du lecteur (recommandé)
L'authentification du lecteur permet à l'application de lecture de prouver son identité à Google Wallet par cryptographie en signant la structure ReaderAuthentication à l'aide d'un certificat de lecteur X.509 autorisé.
- Pourquoi est-ce recommandé ? L'authentification du lecteur permet à votre application ou terminal de lecture de présenter une identité de confiance à l'utilisateur. Bien qu'elle soit facultative pour les attributs publics de base (par exemple, la validation de
age_over_21), elle est vivement recommandée pour les intégrations de lecteurs afin de renforcer la confiance des utilisateurs. Elle peut être requise par la loi ou par les règles lorsque vous demandez des attributs sensibles (comme le numéro de sécurité sociale complet, l'adresse résidentielle ou des mentions spécifiques de l'État). - Fonctionnement : le lecteur inclut sa chaîne de certificats et signe la transcription de la session. Google Wallet affiche l'identité et le nom de l'organisation validés du lecteur sur l'écran de consentement avant la divulgation des données.
Outils de test et de développement
Pour accélérer l'intégration, utilisez les outils pour les développeurs et les implémentations de référence suivants :
- Applications de référence Multipaz :
- Clonez le dépôt Multipaz et exécutez l'application exemple Android
IdentityReaderpour tester les flux de validation physique.
- Clonez le dépôt Multipaz et exécutez l'application exemple Android
- Créer une pièce d'identité de test dans Google Wallet :
- Suivez notre guide Créer un pass d'identité de test dans Google Wallet pour provisionner un identifiant de test simulé dans Google Wallet à l'aide du simulateur de passeport électronique Utopia.
- Test du vérificateur Web :
- Utilisez verifier.multipaz.org pour inspecter les requêtes CBOR, explorer les requêtes de revendication et tester les présentations Web W3C / ISO 18013-7.
Dépannage et diagnostics sur le terrain
Le tableau suivant liste les problèmes courants rencontrés lors de la validation hors connexion et les solutions recommandées :
| Problème / Symptôme | possible | Solution recommandée |
|---|---|---|
| Délai d'expiration de la connexion BLE / Échec de la connexion |
|
|
| L'engagement par paiement sans contact NFC échoue ou diminue | L'utilisateur éloigne l'appareil mobile de l'antenne du lecteur avant que l'enregistrement de transfert BLE ne soit entièrement transféré. |
|
UNTRUSTED_ISSUER / Échec de la chaîne de certificats |
Le certificat du signataire du document n'est associé à aucun certificat IACA de confiance dans le magasin de confiance local du lecteur. |
|
INVALID_VALIDITY_INFO / Expired MSO |
|
|
DEVICE_AUTHENTICATION_FAILED |
Incohérence de la transcription de la session entre le lecteur et le portefeuille, ou signature d'appareil éphémère non valide. |
|
| Plantage lié aux autorisations sur Android 12 ou version ultérieure | L'application a tenté de scanner ou de diffuser des annonces via BLE sans autorisations d'exécution. |
|
Consignes d'expérience utilisateur et consignes en matière de confidentialité pour les lecteurs en personne
Lorsque vous concevez des lecteurs physiques et des applications associées :
- Pratiquez la divulgation sélective dans l'interface utilisateur : n'affichez que la décision ou l'attribut minimal requis pour l'opérateur (par exemple, affichez une coche verte bien visible et "Âge vérifié : 21 ans et plus" plutôt que d'afficher la date de naissance complète, l'adresse et le numéro de permis de l'utilisateur).
- Indicateurs d'interaction physique clairs : indiquez clairement la zone cible NFC et affichez des repères visuels (comme des animations ou des barres de progression) montrant chaque étape : Appuyer / Scanner → Connexion → Vérification → Terminé.
- Traitement des données éphémères : ne stockez pas et n'enregistrez pas les éléments de données à caractère personnel reçus du portefeuille, sauf si la loi applicable l'exige explicitement et que cela est indiqué via
intentToRetain = true.