Ce guide explique comment les parties de confiance et les développeurs de lecteurs peuvent mettre en œuvre la validation en personne (hors connexion) des identifiants numériques présentés depuis Google Wallet conformément à la norme internationale ISO/CEI 18013-5.
Les identifiants numériques dans Google Wallet peuvent être validés de manière sécurisée dans des environnements physiques (tels que les terminaux de point de vente, les lieux d'événements, les portiques de transport, les lecteurs des organismes chargés de l'application des lois et les applications de lecteur mobile) sans nécessiter de connexion Internet active au moment de la présentation.
Présentation hors connexion (ISO/CEI 18013-5)
La norme ISO/CEI 18013-5 définit un protocole normalisé 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 / outil de validation (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 NFC Tap (transfert statique ou négocié) ou QR Code scan. 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 une
DeviceRequestencodée 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 une
DeviceResponseencodée 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 valide les signatures cryptographiques par rapport aux certificats racine de confiance.
SDK Open Source Multipaz
Pour implémenter une application de lecteur ou de validation, Google recommande d'utiliser Multipaz, un SDK Kotlin Multiplatform (KMP) Open Source initialement développé par Google et mis à disposition de 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 validation cryptographique, de l'encodage/décodage CBOR et des schémas de type de document extensibles.
Intégrer Multipaz à votre application de lecteur
Les étapes suivantes montrent comment intégrer le SDK Multipaz à une application de lecteur Android.
Étape 1 : Ajouter 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, l'analyse par caméra (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 : Construire 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, ne demandez que age_over_21 plutôt que birth_date complète lorsque vous vérifiez 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)
}
}
Compatibilité avec d'autres types de documents (DocTypes)
Multipaz est compatible avec les identifiants normalisés prêts à l'emploi 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 | Standard / Périmètre | Espace de noms type | Éléments courants |
|---|---|---|---|
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 des personnes dans le 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 |
Pièce d'identité Google Wallet / Pièces d'identité 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'espace de noms correspondants lors de la construction de la 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 validation 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é | S'assurer que validFrom ≤ heure actuelle ≤ validUntil |
| 3. Vérification de l'intégrité des données | Calculer les condensés SHA-256 des éléments renvoyés et les comparer aux ValueDigests MSO |
| 4. Authentification de l'appareil | Valider la signature DeviceSigned ou le MAC à l'aide de la DeviceKey liée à la SessionTranscript |
1. Pipeline de validation en quatre étapes
- Authentification de l'émetteur (
IssuerAuth):- L'objet de sécurité mobile (MSO) est signé par l'autorité émettrice (charge utile
IssuerAuth). - Le lecteur valide la signature
COSE_Sign1à l'aide du certificat du signataire du document et s'assure que la chaîne de certificats remonte jusqu'à un certificat racine de l'autorité de certification émettrice (IACA) de confiance.
- L'objet de sécurité mobile (MSO) est signé par l'autorité émettrice (charge utile
- Vérification de la période de validité
- Le lecteur vérifie les codes temporels
validityInfo.validFrometvalidityInfo.validUntildans le MSO 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 dictionnaireValueDigestsdu MSO.
- Pour chaque
- Authentification de l'appareil (
DeviceSigned):- Le lecteur valide que l'appareil présentant l'identifiant détient la clé privée correspondant à la
DeviceKeypubliée dans le MSO signé. - Pour ce faire, il valide la
DeviceAuth(soitDeviceSignature, soitDeviceMac) sur laSessionTranscript, en liant la session à la clé éphémère du lecteur et en empêchant les attaques de relecture et de type "homme du milieu".
- Le lecteur valide que l'appareil présentant l'identifiant détient la clé privée correspondant à la
2. Gérer les certificats racine IACA de confiance
Les lecteurs de production doivent maintenir un magasin de confiance local et sécurisé contenant des certificats racine IACA de confiance :
- Certificats IACA de production : téléchargez et configurez les certificats racine auprès des autorités émettrices officielles. Consultez la liste des émetteurs compatibles et des certificats IACA.
- AAMVA VICAL : pour les juridictions américaines, les systèmes de lecteur peuvent s'intégrer au service VICAL (Verified Issuer Certificate Authority List) de l'American Association of Motor Vehicle Administrators (AAMVA) afin de synchroniser automatiquement les ancres de confiance de l'État.
- Racines de test en bac à sable : lorsque vous effectuez des tests par rapport à des identifiants de bac à sable, assurez-vous que le lecteur approuve 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 lecteur de prouver de manière cryptographique son identité à Google Wallet 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 lecteur de présenter une identité de confiance à l'utilisateur. Bien qu'elle soit facultative pour les attributs publics de base (par exemple, pour vérifier
age_over_21), elle est fortement recommandée pour les intégrations de lecteur afin d'accroître la confiance des utilisateurs et peut être légalement ou réglementairement requise lors de la demande d'attributs sensibles (tels que le numéro de sécurité sociale complet, l'adresse de résidence 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é validée et le nom de l'organisation du lecteur à l'utilisateur sur l'écran de consentement avant la publication des données.
Outils de test et de développement
Pour accélérer l'intégration, utilisez les outils de développement 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 une pièce 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.
- Tests de l'outil de validation 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 répertorie 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'inactivité de la connexion BLE / Échec de la connexion |
|
|
| L'engagement NFC Tap échoue ou est interrompu | 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 lié à aucun certificat IACA de confiance dans le magasin de confiance local du lecteur. |
|
INVALID_VALIDITY_INFO / MSO expiré |
|
|
DEVICE_AUTHENTICATION_FAILED |
Incompatibilité de la transcription de la session entre le lecteur et le portefeuille, ou signature d'appareil éphémère non valide. |
|
| Plantage d'autorisation Android 12 ou version ultérieure | L'application a tenté d'analyser ou de diffuser des annonces via BLE sans autorisations d'exécution. |
|
Consignes concernant l'expérience utilisateur et la confidentialité pour les lecteurs en personne
Lors de la conception de lecteurs physiques et d'applications associées :
- Appliquez 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 validé : 21 ans et plus" plutôt que la date de naissance complète, l'adresse et le numéro de permis de l'utilisateur).
- Indicateurs d'interaction physique clairs : étiquetez clairement la zone cible NFC et affichez des repères visuels (tels que des animations ou des barres de progression) indiquant chaque étape : Appuyer / Scanner → Connexion → Validation → Terminé.
- Gestion des données éphémères : ne stockez ni n'enregistrez les éléments de données personnelles reçus du portefeuille, sauf si la loi applicable l'exige explicitement et si cela est divulgué via
intentToRetain = true.