В данном руководстве объясняется, как проверяющие стороны (RP) и разработчики считывателей могут реализовать личную (офлайн) проверку цифровых учетных данных, предоставленных из Google Wallet, в соответствии с международным стандартом ISO/IEC 18013-5 .
Цифровые учетные данные в Google Wallet можно безопасно проверять в физических условиях (например, на POS-терминалах, в местах проведения мероприятий, на турникетах, считывающих устройствах правоохранительных органов и мобильных приложениях для чтения) без необходимости активного подключения к интернету в момент предъявления.
Обзор офлайн-презентаций (ISO/IEC 18013-5)
Стандарт ISO/IEC 18013-5 определяет стандартизированный, совместимый протокол для автономной презентации между держателем (мобильным устройством пользователя с запущенным Google Wallet) и считывателем/верификатором (физическим терминалом или сопутствующим мобильным приложением).
Процесс презентации проходит в несколько этапов:
- Взаимодействие устройства: Считыватель и кошелек устанавливают первоначальный контакт посредством NFC-соединения (статическая или согласованная передача) или сканирования QR-кода . На этом этапе происходит обмен метаданными взаимодействия устройства и временным открытым ключом считывателя (
EReaderKey). - Соединение для передачи данных: устанавливается защищенный, зашифрованный канал Bluetooth Low Energy (BLE) (при этом считывающее устройство работает в режиме центрального клиента или периферийного сервера).
- Запрос устройства: Считыватель передает
DeviceRequestв кодировке CBOR, указывающий запрашиваемый тип документа (например,org.iso.18013.5.1.mDL), а также конкретные пространства имен и элементы данных. - Согласие пользователя и аутентификация устройства: Google Wallet предлагает пользователю просмотреть запрашиваемые элементы данных и подтвердить их предоставление с помощью биометрической аутентификации или блокировки экрана.
- Ответ устройства и криптографическая проверка: Кошелек отправляет обратно
DeviceResponseв кодировке CBOR, содержащий подписанный объект мобильной безопасности (MSO) и подписанные устройством элементы данных. Считыватель проверяет криптографические подписи по доверенным корневым сертификатам.
Комплект разработки программного обеспечения Multipaz с открытым исходным кодом
Для реализации приложения-считывателя или верификатора Google рекомендует использовать Multipaz — SDK с открытым исходным кодом для многоплатформенной разработки на Kotlin (KMP), первоначально разработанный Google и переданный в фонд OpenWallet Foundation (OWF) .
Multipaz предоставляет готовую к использованию реализацию протоколов чтения и хранения данных ISO/IEC 18013-5, конвейеров криптографической верификации, кодирования/декодирования CBOR и расширяемых схем типов документов.
Интеграция Multipaz в ваше приложение для чтения электронных книг.
Следующие шаги демонстрируют, как интегрировать SDK Multipaz в приложение для чтения Android.
Шаг 1: Добавьте зависимости
Библиотеки Multipaz опубликованы в Maven Central . Добавьте необходимые модули в файл build.gradle.kts вашего приложения:
// 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")
}
Шаг 2: Настройка разрешений Android
Для подтверждения личности при личном контакте требуются аппаратные разрешения на использование NFC, сканирование камерой (для QR-кода) и передачу данных по Bluetooth Low Energy. Добавьте следующие разрешения в файл 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>
Шаг 3: Инициализация механизма проверки
Используйте абстракции подключения VerificationHelper или Reader в Multipaz для обработки событий взаимодействия и управления жизненным циклом 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()
}
}
Шаг 4: Формирование запроса устройства (DeviceRequest).
Укажите тип документа и отдельные элементы данных, которые ваше приложение должно проверить. Всегда применяйте принцип минимального раскрытия информации (например, запрашивайте только age_over_21 а не полную 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)
}
}
Поддержка дополнительных типов документов (DocTypes)
Multipaz поддерживает стандартизированные учетные данные «из коробки» и предоставляет расширяемую архитектуру для запроса пользовательских или предметно-ориентированных типов документов.
1. Встроенные стандартные типы документов
Библиотека multipaz-doctypes предоставляет предопределенные модели схем для стандартных учетных данных:
| Идентификатор типа документа | Стандарт / Область применения | Типичное пространство имен | Общие элементы |
|---|---|---|---|
org.iso.18013.5.1.mDL | Мобильное водительское удостоверение 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 | Данные идентификации личности в цифровом кошельке идентификации ЕС (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 / Пароль / Тестовые идентификаторы | com.google.wallet.idcard.1 | given_name , family_name , birth_date , document_number , portrait |
2. Запрос пользовательских типов документов
Для запроса пользовательских типов документов укажите целевую строку docType и соответствующие сопоставления пространств имен при создании объекта 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)
)
)
)
Криптографическая верификация и управление доверием
Получение ответного содержимого — это только первый шаг. Читателям необходимо выполнить четырехэтапную криптографическую проверку для подтверждения подлинности и целостности представленных учетных данных.
| Этап проверки | Цель проверки |
|---|---|
| 1. Аутентификация эмитента | Проверьте IssuerAuth ( COSE_Sign1 ) на соответствие доверенным корневым сертификатам IACA. |
| 2. Проверка окна действия | Убедитесь, validFrom ≤ current time ≤ validUntil |
| 3. Проверка целостности данных | Вычислить дайджесты SHA-256 возвращенных элементов и сопоставить их с ValueDigests MSO. |
| 4. Аутентификация устройства | Проверьте подпись DeviceSigned или MAC-адрес, используя DeviceKey , привязанный к SessionTranscript |
1. Четырехэтапный конвейер верификации
- Аутентификация эмитента (
IssuerAuth):- Объект мобильной безопасности (MSO) подписывается органом-эмитентом (полезная нагрузка
IssuerAuth). - Читатель проверяет подпись
COSE_Sign1используя сертификат подписанта документа, и убеждается, что цепочка сертификатов ведет к доверенному корневому сертификату центра сертификации (IACA) .
- Объект мобильной безопасности (MSO) подписывается органом-эмитентом (полезная нагрузка
- Проверка окна действия:
- Считыватель проверяет метки времени
validityInfo.validFromиvalidityInfo.validUntilв MSO, сверяя их с текущими часами считывателя, чтобы убедиться, что учетные данные не истекли.
- Считыватель проверяет метки времени
- Проверка целостности данных (
ValueDigests):- Для каждого полученного
IssuerSignedItemсчитыватель вычисляет его дайджест (например, SHA-256) и проверяет, совпадает ли он с соответствующей записью хеша в словареValueDigestsоператора MSO.
- Для каждого полученного
- Аутентификация устройства (
DeviceSigned):- Читатель подтверждает, что устройство, предъявляющее учетные данные, содержит закрытый ключ, соответствующий
DeviceKeyопубликованному внутри подписанного MSO. - Это достигается путем проверки
DeviceAuth(либоDeviceSignature, либоDeviceMac) черезSessionTranscript, привязки сессии к временному ключу читателя и предотвращения повторного воспроизведения и атак типа «человек посередине».
- Читатель подтверждает, что устройство, предъявляющее учетные данные, содержит закрытый ключ, соответствующий
2. Управление доверенными корневыми сертификатами IACA
Читатели, работающие в производственной среде, должны поддерживать защищенное локальное хранилище доверенных сертификатов, содержащее доверенные корневые сертификаты IACA:
- Производственные сертификаты IACA: Загрузите и настройте корневые сертификаты от официальных центров выдачи. См. наш список поддерживаемых центров выдачи и сертификатов IACA .
- AAMVA VICAL: Для юрисдикций США системы считывания могут интегрироваться со службой Verified Issuer Certificate Authority List (VICAL) Американской ассоциации администраторов транспортных средств (AAMVA) для автоматической синхронизации государственных доверенных точек.
- Корневые ресурсы для тестирования в песочнице: При тестировании с использованием учетных данных песочницы убедитесь, что читатель доверяет корневому ресурсу IACA песочницы 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
}
Аутентификация читателя (рекомендуется)
Аутентификация читателя позволяет приложению для чтения криптографически подтвердить свою личность Google Wallet, подписав структуру ReaderAuthentication с использованием авторизованного сертификата читателя X.509.
- Почему это рекомендуется: Аутентификация читателя позволяет вашему приложению или терминалу для чтения представлять пользователю доверенную личность. Хотя она необязательна для основных общедоступных атрибутов (например, проверка
age_over_21), настоятельно рекомендуется для интеграции с приложениями для чтения, чтобы повысить доверие пользователей, и может быть обязательной по закону или в соответствии с политикой компании при запросе конфиденциальных атрибутов (таких как полный номер социального страхования, адрес проживания или рекомендации конкретного штата). - Как это работает: устройство чтения предоставляет цепочку сертификатов и подписывает протокол сеанса. Google Wallet отображает пользователю подтвержденную личность и название организации устройства чтения на экране согласия перед передачей данных.
Инструменты тестирования и разработки
Для ускорения интеграции используйте следующие инструменты разработчика и эталонные реализации:
- Справочные приложения Multipaz:
- Клонируйте репозиторий Multipaz и запустите демонстрационное приложение
IdentityReaderдля Android, чтобы протестировать процессы физической верификации.
- Клонируйте репозиторий Multipaz и запустите демонстрационное приложение
- Создайте тестовый ID в Google Wallet:
- Следуйте нашему руководству по созданию тестового пропуска в Google Wallet , чтобы добавить имитированные тестовые учетные данные в Google Wallet с помощью симулятора Utopia ePassport.
- Веб-тестирование с помощью верификатора:
- Используйте verifier.multipaz.org для проверки запросов CBOR, изучения запросов по претензиям и тестирования веб-презентаций W3C / ISO 18013-7.
Поиск и устранение неисправностей и диагностика в полевых условиях
В таблице ниже перечислены распространенные проблемы, возникающие при проверке в автономном режиме, и рекомендуемые способы их решения:
| Проблема / Симптом | Первопричина | Рекомендуемое разрешение |
|---|---|---|
| Истекло время ожидания соединения BLE / Сбой подключения |
|
|
| Срабатывание NFC-метки при касании не удается или прерывается. | Пользователь отводит мобильное устройство от антенны считывателя до того, как запись о передаче BLE будет полностью передана. |
|
UNTRUSTED_ISSUER / Сбой цепочки сертификатов | Сертификат подписанта документа не связан ни с одним доверенным сертификатом IACA в локальном хранилище доверенных сертификатов читателя. |
|
INVALID_VALIDITY_INFO / Срок действия MSO истек |
|
|
DEVICE_AUTHENTICATION_FAILED | Несоответствие стенограммы сессии между считывателем и кошельком или недействительная подпись временного устройства. |
|
| Сбой при получении разрешений в Android 12+ | Приложение попыталось выполнить сканирование или передачу данных по BLE без необходимых разрешений во время выполнения. |
|
Рекомендации по пользовательскому опыту и конфиденциальности для читателей, присутствующих лично.
При разработке печатных электронных книг и сопутствующих приложений:
- Практикуйте выборочное раскрытие информации в пользовательском интерфейсе: отображайте оператору только решение или минимально необходимую информацию (например, покажите заметную зеленую галочку и надпись « Возраст 21+ подтвержден », вместо отображения полной даты рождения пользователя, адреса и номера водительского удостоверения).
- Четкие индикаторы физического взаимодействия: Четко обозначьте целевую зону NFC и отобразите визуальные подсказки (например, анимацию или индикаторы выполнения), показывающие каждый этап: Касание / Сканирование → Подключение → Проверка → Завершено .
- Обработка временных данных: Не сохраняйте и не регистрируйте элементы персональных данных, полученные из кошелька, если это явно не требуется применимым законодательством и не указано параметром
intentToRetain = true.