Цифровые удостоверения личности принимаются как в приложении, так и на веб- сайте. Для приема учетных данных из Google Wallet вам потребуется:
- Интеграция осуществляется через приложение или веб-сайт в соответствии с предоставленными инструкциями.
- Используйте идентификатор теста , чтобы протестировать свой процесс в песочнице Google Wallet.
- Для запуска сервиса заполните эту форму , чтобы запросить доступ и принять условия использования учетных данных Google Wallet. Это необходимо сделать для каждого из ваших бизнес-подразделений. После заполнения формы наша команда свяжется с вами.
- Если у вас возникнут вопросы, вы можете обратиться по адресу
wallet-identity-rp-support@google.com.
Поддерживаемые форматы учетных данных
Существует несколько предложенных стандартов, определяющих формат данных цифровых удостоверений личности, два из которых получили значительную поддержку в отрасли:
- mdocs - определены стандартом ISO.
- Проверяемые учетные данные W3C — определены организацией W3C.
Хотя Android Credential Manager поддерживает оба формата, Google Wallet в настоящее время поддерживает только цифровые удостоверения личности на основе mdoc.
Поддерживаемые учетные данные
Google Wallet поддерживает 2 типа учетных данных:
- Мобильное водительское удостоверение (mDL)
- Пропуск ID
Вы можете запросить любые из этих учетных данных в своем рабочем процессе, изменив всего один параметр.
пользовательский опыт
В этом разделе обсуждается рекомендуемая схема онлайн-презентации. Схема демонстрирует отображение возраста для приложения по доставке алкоголя, но пользовательский опыт аналогичен как для веб-презентаций, так и для других типов презентаций.
![]() | ![]() | ![]() | ![]() | ![]() |
| Пользователю предлагается подтвердить свой возраст в приложении или на веб-сайте. | Пользователь видит доступные подходящие учетные данные. | Пользователь видит страницу подтверждения в Google Wallet. | Пользователь проходит аутентификацию для подтверждения предоставления доступа. | Данные отправляются в приложение или на веб-сайт. |
Основные положения
- Приложение или веб-сайт имеют возможность гибко настраивать точку входа в API. Как показано на шаге 1, мы рекомендуем отображать универсальную кнопку, например, «Подтвердить с помощью цифрового удостоверения личности», поскольку со временем мы ожидаем, что через API станут доступны варианты, выходящие за рамки Google Wallet.
- Экран выбора на шаге 2 отображается Android. Подходящие учетные данные определяются на основе соответствия логики регистрации, предоставляемой каждым кошельком, и запроса, отправленного проверяющей стороной.
- Шаг 3 выполняется с помощью Google Wallet. На этом экране Google Wallet отобразит название, логотип и политику конфиденциальности разработчика.
Добавить поток цифровой идентификации
В случае, если у пользователя нет учетных данных, мы рекомендуем разместить рядом с кнопкой «Подтвердить с помощью цифрового удостоверения личности» ссылку, которая будет вести на страницу Google Wallet, где пользователь сможет добавить цифровое удостоверение личности.
![]() | ![]() |
| Пользователю предлагается подтвердить свой возраст в приложении или на веб-сайте. | Пользователя перенаправили в Google Wallet для получения цифрового удостоверения личности. |
Цифровое удостоверение личности недоступно
Если пользователь выберет опцию «Подтвердить с помощью цифрового удостоверения личности», не имея при этом цифрового удостоверения личности, ему будет показано это сообщение об ошибке.
![]() | ![]() |
| Пользователю предлагается подтвердить свой возраст в приложении или на веб-сайте. | Пользователю отображается ошибка, если у него нет цифрового удостоверения личности. |
API не поддерживает функцию скрытого определения наличия у пользователя цифровых идентификаторов для сохранения его конфиденциальности. Поэтому мы рекомендуем добавить опцию ссылки для регистрации, как показано на рисунке.
Формат запроса для получения идентификационных данных из кошелька.
Вот пример запроса mdoc requestJson для получения учетных данных Identity из любого кошелька на устройстве Android или в веб-браузере.
{
"requests" : [
{
"protocol": "openid4vp-v1-unsigned",
"data": {<credential_request>} // This is an object, shouldn't be a string.
}
]
}
Запрос на шифрование
Параметр client_metadata содержит открытый ключ шифрования для каждого запроса. Вам потребуется хранить закрытые ключи для каждого запроса и использовать их для аутентификации и авторизации токена, полученного от приложения-кошелька.
Параметр credential_request в requestJson будет содержать следующие поля.
Специальная квалификация
{
"response_type": "vp_token",
"response_mode": "dc_api.jwt", // change this to dc_api if you want to demo with a non encrypted response.
"nonce": "1234",
"dcql_query": {
"credentials": [
{
"id": "cred1",
"format": "mso_mdoc",
"meta": {
"doctype_value": "org.iso.18013.5.1.mDL" // this is for mDL. Use com.google.wallet.idcard.1 for ID pass
},
"claims": [
{
"path": [
"org.iso.18013.5.1",
"family_name"
],
"intent_to_retain": false // set this to true if you are saving the value of the field
},
{
"path": [
"org.iso.18013.5.1",
"given_name"
],
"intent_to_retain": false
},
{
"path": [
"org.iso.18013.5.1",
"age_over_18"
],
"intent_to_retain": false
}
]
}
]
},
"client_metadata": {
"jwks": {
"keys": [ // sample request encryption key
{
"kty": "EC",
"crv": "P-256",
"x": "pDe667JupOe9pXc8xQyf_H03jsQu24r5qXI25x_n1Zs",
"y": "w-g0OrRBN7WFLX3zsngfCWD3zfor5-NLHxJPmzsSvqQ",
"use": "enc",
"kid" : "1", // This is required
"alg" : "ECDH-ES", // This is required
}
]
},
"vp_formats_supported": {
"mso_mdoc": {
"deviceauth_alg_values": [
-7
],
"isserauth_alg_values": [
-7
]
}
}
}
}
Любой соответствующий требованиям сертификат
Здесь приведён пример запроса как для mDL, так и для idpass, и пользователь может продолжить с любым из них.
{
"response_type": "vp_token",
"response_mode": "dc_api.jwt", // change this to dc_api if you want to demo with a non encrypted response.
"nonce": "1234",
"dcql_query": {
"credentials": [
{
"id": "mdl-request",
"format": "mso_mdoc",
"meta": {
"doctype_value": "org.iso.18013.5.1.mDL"
},
"claims": [
{
"path": [
"org.iso.18013.5.1",
"family_name"
],
"intent_to_retain": false // set this to true if you are saving the value of the field
},
{
"path": [
"org.iso.18013.5.1",
"given_name"
],
"intent_to_retain": false
},
{
"path": [
"org.iso.18013.5.1",
"age_over_18"
],
"intent_to_retain": false
}
]
},
{ // Credential type 2
"id": "id_pass-request",
"format": "mso_mdoc",
"meta": {
"doctype_value": "com.google.wallet.idcard.1"
},
"claims": [
{
"path": [
"org.iso.18013.5.1",
"family_name"
],
"intent_to_retain": false // set this to true if you are saving the value of the field
},
{
"path": [
"org.iso.18013.5.1",
"given_name"
],
"intent_to_retain": false
},
{
"path": [
"org.iso.18013.5.1",
"age_over_18"
],
"intent_to_retain": false
}
]
}
]
credential_sets : [
{
"options": [
[ "mdl-request" ],
[ "id_pass-request" ]
]
}
]
},
"client_metadata": {
"jwks": {
"keys": [ // sample request encryption key
{
"kty": "EC",
"crv": "P-256",
"x": "pDe667JupOe9pXc8xQyf_H03jsQu24r5qXI25x_n1Zs",
"y": "w-g0OrRBN7WFLX3zsngfCWD3zfor5-NLHxJPmzsSvqQ",
"use": "enc",
"kid" : "1", // This is required
"alg" : "ECDH-ES", // This is required
}
]
},
"vp_formats_supported": {
"mso_mdoc": {
"deviceauth_alg_values": [
-7
],
"isserauth_alg_values": [
-7
]
}
}
}
}
Вы можете запросить любое количество поддерживаемых атрибутов для любых учетных данных, хранящихся в Google Wallet.
В приложении
Чтобы запросить учетные данные для доступа к вашим приложениям Android, выполните следующие шаги:
Обновить зависимости
В файле build.gradle вашего проекта обновите зависимости, чтобы использовать Credential Manager (бета-версия):
dependencies {
implementation("androidx.credentials:credentials:1.5.0-beta01")
implementation("androidx.credentials:credentials-play-services-auth:1.5.0-beta01")
}
Настройте диспетчер учетных данных.
Для настройки и инициализации объекта CredentialManager добавьте логику, аналогичную следующей:
// Use your app or activity context to instantiate a client instance of CredentialManager.
val credentialManager = CredentialManager.create(context)
Запрос атрибутов идентификации
Вместо указания отдельных параметров для запросов на идентификацию, приложение предоставляет их все вместе в виде строки JSON в параметре CredentialOption. Менеджер учетных данных передает эту строку JSON доступным цифровым кошелькам, не анализируя ее содержимое. Затем каждый кошелек отвечает за: - Анализ строки JSON для понимания запроса на идентификацию. - Определение того, какие из его сохраненных учетных данных, если таковые имеются, удовлетворяют запросу.
Мы рекомендуем партнерам создавать запросы на сервере, даже для интеграции с Android-приложениями.
В вызове функции GetDigitalCredentialOption() вы будете использовать requestJson из Request Format, содержащий сам request .
// The request in the JSON format to conform with
// the JSON-ified Digital Credentials API request definition.
val requestJson = generateRequestFromServer()
val digitalCredentialOption =
GetDigitalCredentialOption(requestJson = requestJson)
// Use the option from the previous step to build the `GetCredentialRequest`.
val getCredRequest = GetCredentialRequest(
listOf(digitalCredentialOption)
)
coroutineScope.launch {
try {
val result = credentialManager.getCredential(
context = activityContext,
request = getCredRequest
)
verifyResult(result)
} catch (e : GetCredentialException) {
handleFailure(e)
}
}
Проверьте и подтвердите ответ.
После получения ответа от кошелька необходимо проверить, является ли ответ успешным и содержит ли он данные в credentialJson .
// Handle the successfully returned credential.
fun verifyResult(result: GetCredentialResponse) {
val credential = result.credential
when (credential) {
is DigitalCredential -> {
val responseJson = credential.credentialJson
validateResponseOnServer(responseJson) // make a server call to validate the response
}
else -> {
// Catch any unrecognized credential type here.
Log.e(TAG, "Unexpected type of credential ${credential.type}")
}
}
}
// Handle failure.
fun handleFailure(e: GetCredentialException) {
when (e) {
is GetCredentialCancellationException -> {
// The user intentionally canceled the operation and chose not
// to share the credential.
}
is GetCredentialInterruptedException -> {
// Retry-able error. Consider retrying the call.
}
is NoCredentialException -> {
// No credential was available.
}
else -> Log.w(TAG, "Unexpected exception type ${e::class.java}")
}
}
В ответе credentialJson содержится зашифрованный токен идентификации (JWT), определенный W3C. За формирование этого ответа отвечает приложение Wallet.
Пример:
{
"protocol" : "openid4vp-v1-unsigned",
"data" : {
<encrpted_response>
}
}
Вы передадите этот ответ обратно на сервер для проверки его подлинности. Инструкции по проверке ответа с учетными данными можно найти здесь.
Веб
Для запроса учетных данных с помощью API цифровых учетных данных в Chrome или других поддерживаемых браузерах выполните следующий запрос.
const credentialResponse = await navigator.credentials.get({
digital : {
requests : [
{
protocol: "openid4vp-v1-unsigned",
data: {<credential_request>} // This is an object, shouldn't be a string.
}
]
}
})
Отправьте ответ от этого API обратно на свой сервер для проверки учетных данных.
Этапы проверки ответа об учетных данных
После получения зашифрованного identityToken от вашего приложения или веб-сайта вам потребуется выполнить несколько проверок, прежде чем доверять полученному ответу.
Расшифровать ответ с помощью закрытого ключа
Первый шаг — расшифровать токен, используя сохраненный закрытый ключ, и получить ответ в формате JSON.
Пример на Python:
from jwcrypto import jwe, jwk # Retrieve the Private Key from Datastore reader_private_jwk = jwk.JWK.from_json(jwe_private_key_json_str) # Save public key thumbprint for session transcript encryption_public_jwk_thumbprint = reader_private_jwk.thumbprint() # Decrypt the JWE encrypted response from Google Wallet jwe_object = jwe.JWE() jwe_object.deserialize(encrypted_jwe_response_from_wallet) jwe_object.decrypt(reader_private_jwk) decrypted_payload_bytes = jwe_object.payload decrypted_data = json.loads(decrypted_payload_bytes)Результатом выполнения
decrypted_dataстанет JSON-объектvp_token, содержащий учетные данные.{ "vp_token": { "cred1": ["<base64UrlNoPadding_encoded_credential>"] // This applies to OpenID4VP 1.0 spec. } }Создайте стенограмму сессии.
Следующий шаг — создание записи сеанса (SessionTranscript) в соответствии со стандартом ISO/IEC 18013-5:2021 с учетом структуры передачи данных, специфичной для Android или веб-версии:
SessionTranscript = [ null, // DeviceEngagementBytes not available null, // EReaderKeyBytes not available [ "OpenID4VPDCAPIHandover", AndroidHandoverDataBytes // BrowserHandoverDataBytes for Web ] ]Для обоих вариантов передачи данных между Android и веб-версией вам потребуется использовать тот же nonce, который вы использовали для генерации
credential_request.Передача управления Android
AndroidHandoverData = [ origin, // "android:apk-key-hash:<base64SHA256_ofAppSigningCert>", nonce, // nonce that was used to generate credential request, encryption_public_jwk_thumbprint, // Encryption public key (JWK) Thumbprint ] AndroidHandoverDataBytes = hashlib.sha256(cbor2.dumps(AndroidHandoverData)).digest()
Передача управления браузером
BrowserHandoverData =[ origin, // Origin URL nonce, // nonce that was used to generate credential request encryption_public_jwk_thumbprint, // Encryption public key (JWK) Thumbprint ] BrowserHandoverDataBytes = hashlib.sha256(cbor2.dumps(BrowserHandoverData)).digest()
При использовании
SessionTranscriptнеобходимо выполнить проверку DeviceResponse в соответствии с пунктом 9 стандарта ISO/IEC 18013-5:2021. Это включает в себя несколько этапов, таких как:Проверьте сертификат эмитента в конкретном штате. Ознакомьтесь с сертификатами IACA поддерживаемого эмитента.
Проверьте подпись MSO (18013-5 Раздел 9.1.2)
Вычислите и проверьте ValueDigest для элементов данных (18013-5 Раздел 9.1.2)
Проверка подписи
deviceSignature(18013-5 Раздел 9.1.3)
{
"version": "1.0",
"documents": [
{
"docType": "org.iso.18013.5.1.mDL",
"issuerSigned": {
"nameSpaces": {...}, // contains data elements
"issuerAuth": [...] // COSE_Sign1 w/ issuer PK, mso + sig
},
"deviceSigned": {
"nameSpaces": 24(<< {} >>), // empty
"deviceAuth": {
"deviceSignature": [...] // COSE_Sign1 w/ device signature
}
}
}
],
"status": 0
}
Создайте своё решение
Для создания собственного решения вы можете обратиться к эталонной реализации Identity Verifiers на GitHub.
Верификация на основе доказательства с нулевым разглашением (ZKP).
Доказательство с нулевым разглашением (ZKP) — это криптографический метод, позволяющий человеку (доказывающему) доказать проверяющему, что он обладает определенной информацией, позволяющей установить его личность, или соответствует определенному критерию (например, старше 18 лет, имеет действительные учетные данные), не раскрывая при этом сами исходные данные. По сути, это способ подтвердить истинность утверждения о личности, сохраняя при этом конфиденциальные данные в тайне.
Системы цифровой идентификации, основанные на прямом обмене данными, часто требуют от пользователей предоставления чрезмерного количества личной информации, что увеличивает риск утечки данных и кражи личных данных. Системы с нулевым раскрытием информации (ZKP) предлагают кардинальное изменение парадигмы, позволяя проводить верификацию с минимальным раскрытием информации.
Ключевые понятия ZKP в цифровой идентификации:
- Доказывающий: Человек, пытающийся доказать какой-либо аспект своей личности.
- Верификатор: Субъект, запрашивающий подтверждение атрибута, указывающего на его личность.
- Доказательство: криптографический протокол, позволяющий доказывающему убедить проверяющего в истинности своего утверждения, не раскрывая секретной информации.
Основные свойства доказательств с нулевым разглашением:
- Полнота: Если утверждение истинно и доказывающий, и проверяющий честны, то проверяющий будет убежден.
- Достоверность: Если утверждение ложно, нечестный доказывающий не сможет (с очень высокой вероятностью) убедить честного проверяющего в его истинности.
- Нулевое знание: проверяющий не узнает ничего, кроме того факта, что утверждение истинно. Никакие фактические данные о личности доказывающего не раскрываются.
Чтобы получить подтверждение с нулевым разглашением (Zero Knowledge proof) от Google Wallet, необходимо изменить формат запроса на mso_mdoc_zk и добавить zk_system_type к вашему запросу.
...
"dcql_query": {
"credentials": [{
"id": "cred1",
"format": "mso_mdoc_zk",
"meta": {
"doctype_value": "org.iso.18013.5.1.mDL"
"zk_system_type": [
{
"system": "longfellow-libzk-v1",
"circuit_hash": "f88a39e561ec0be02bb3dfe38fb609ad154e98decbbe632887d850fc612fea6f", // This will differ if you need more than 1 attribute.
"num_attributes": 1, // number of attributes (in claims) this has can support
"version": 5,
"block_enc_hash": 4096,
"block_enc_sig": 2945,
}
{
"system": "longfellow-libzk-v1",
"circuit_hash": "137e5a75ce72735a37c8a72da1a8a0a5df8d13365c2ae3d2c2bd6a0e7197c7c6", // This will differ if you need more than 1 attribute.
"num_attributes": 1, // number of attributes (in claims) this has can support
"version": 6,
"block_enc_hash": 4096,
"block_enc_sig": 2945,
}
],
"verifier_message": "challenge"
},
"claims": [{
...
"client_metadata": {
"jwks": {
"keys": [ // sample request encryption key
{
...
В результате вы получите из кошелька зашифрованное доказательство с нулевым разглашением. Вы можете проверить это доказательство, используя сертификаты IACA эмитентов и библиотеку longfellow-zk от Google.
Сервис проверки содержит готовый к развертыванию сервер на основе Docker, который позволяет проверять ответ на соответствие определенным сертификатам IACA от эмитента.
Вы можете изменить файл certs.pem , чтобы управлять сертификатами эмитентов IACA, которым хотите доверять.
Для получения более подробной информации вы можете обратиться в службу поддержки по адресу wallet-identity-rp-support@google.com







