Paso 3: Realiza la integración como registrador de verificadores

En esta guía, se explica cómo los registradores de verificadores (VR), es decir, los proveedores de verificación de identidad (IDV), los agregadores y las plataformas que verifican a los usuarios en nombre de varios clientes downstream (RP finales), configuran su autoridad certificadora (CA), codifican los metadatos de CBOR y, además, integran la API de Digital Credentials.

Cómo funciona la integración del registrador de verificadores

Como registrador de verificadores, actúas como tu propia autoridad certificadora (CA) y firmas las solicitudes de identidad para las partes de confianza (RP) finales que administras. Selecciona cualquier paso para ir a los detalles de su implementación:

Envía tu CA raíz de la zona de pruebas antes de realizar pruebas en ella

A diferencia de las partes de confianza directas, los registradores de verificadores firman las solicitudes de la zona de pruebas con su propia CA raíz de la zona de pruebas:

Registra primero las AC raíz

Envía el formulario de admisión del registrador verificador para que Google agregue tu CA raíz de la zona de pruebas al almacén de confianza de la zona de pruebas.

Firma certificados de hoja

Emite certificados de entidad final de RP con el OID 1.3.6.1.4.1.11129.10.1 encadenado a tu CA raíz de la zona de pruebas.

Valida las respuestas

Valida issuerAuth con el certificado de la zona de pruebas de Utopia durante las pruebas de la zona de pruebas.


Paso A: Configura tu perfil de certificado de la AC y los metadatos de CBOR

1. Perfil de certificado

Las solicitudes deben estar firmadas por certificados de hoja estándar X.509 v3 (encadenados a tu CA raíz de producción o de zona de pruebas registrada) generados con P-256 / ECDSA y que contengan una extensión personalizada de Google:

  • OID de extensión personalizada: 1.3.6.1.4.1.11129.10.1
  • Importancia: No es crítica.
  • Contenido: Es un hash SHA-256 del RelyingPartyMetadataBytes, codificado en un OCTET STRING ASN.1.
  • Sujeto (nombre distinguido): Debe ser único a nivel global para cada RP final descendente para el que firmes.

Se aplica solo a los certificados de hoja: Estos requisitos de extensión y asunto personalizados se aplican solo a los certificados de entidad final (hoja) que representan a tus RP finales downstream. Tus ACs raíz de zona de pruebas y producción, así como cualquier AC intermedia que uses, no requieren la extensión de OID de Google personalizada ni necesitan un asunto específico de End-RP.

Jerarquía de certificados del registrador del verificador, desde la AC raíz y la AC intermedia opcional hasta el certificado de hoja de la RP final y los metadatos de CBOR

2. Esquema de metadatos (CBOR)

Los metadatos de cada RP final descendente (y la marca opcional del agregador) deben codificarse en formato CBOR:

; in CDDL for CBOR encoding
; schemaVersion = "v1"

RelyingPartyMetadataBytes = #6.24(bstr .cbor RelyingPartyMetadata)


RelyingPartyMetadata = {
  "schema_version": tstr,
  "display": DisplayInfo,
  "aggregator_info": DisplayInfo  ; Optional: include to show your branding alongside the RP
}

DisplayInfo = {
  "display_name": tstr,
  "logo_uri": tstr,             ; See brand guidelines link in following paragraph
  "privacy_policy_uri": tstr
}

El logo_uri debe seguir los lineamientos de desarrollo de la marca de la Billetera de Google.

3. Integración de metadatos de OpenID4VP

Cuando des formato a tu solicitud de credencial de OpenID4VP firmada en el paso B, incluye el RelyingPartyMetadataBytes codificado en Base64URL en el campo gw_rp_metadata_bytes dentro del objeto client_metadata, y, además, incluye tu cadena de certificados de hoja de RP final en el encabezado x5c de JWS.


Formatos, credenciales y funciones compatibles

La Billetera de Google admite IDs digitales basados en mdoc ISO a través de OpenID4VP (versión 1.0) en Android 9 (nivel de API 28) y versiones posteriores, y en navegadores compatibles.

Credenciales y atributos compatibles

Busca los identificadores de doctype_value (mDL, pase de identificación, documento de identidad con foto) y las rutas de reclamación de atributos que puedes solicitar a la Billetera de Google.

Entidades emisoras y certificados de IACA admitidos

Descarga los certificados raíz oficiales de la IACA para los estados de EE.UU., las entidades emisoras internacionales y la entidad emisora de Sandbox Utopia para validar issuerAuth.


Paso B: Aplica formato a la solicitud y fírmala (del lado del servidor)

Para solicitar credenciales de cualquier billetera, debes dar formato a tu solicitud con OpenID4VP en tu servidor. Puedes solicitar una credencial específica o varias credenciales en un solo objeto dcql_query.

Ejemplo de solicitud JSON

A continuación, se muestra un ejemplo de una solicitud requestJson de mdoc para obtener credenciales de identidad de cualquier billetera en un dispositivo Android o en la Web.

{
      "requests" : [
        {
          "protocol": "openid4vp-v1-signed",
          "data": {<signed_credential_request>} // This is an object, shouldn't be a string.
        }
      ]
}

Solicitar encriptación

El client_metadata contiene la clave pública de encriptación para cada solicitud. Deberás almacenar las claves privadas de cada solicitud y usarlas para autenticar y autorizar el token que recibas de la app de la billetera.

Metadatos de OpenID4VP integrados

Cuando des formato a tu solicitud de credenciales, debes incluir el campo gw_rp_metadata_bytes dentro del objeto client_metadata (como se muestra en el código de solicitud de ejemplo que aparece a continuación). Este campo contiene los metadatos de la parte de confianza codificados en Base64URL que requiere la Billetera de Google para verificar tu identidad y mostrar tu desarrollo de la marca al usuario.

Carga útil de la solicitud de credenciales

El parámetro credential_request en requestJson contiene los siguientes campos.

Credencial específica

{
  "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": {
          // Use org.iso.18013.5.1.mDL for mDL,
          // com.google.wallet.idcard.1 for ID pass, or
          // org.iso.23220.photoid.1 for ID pass (using photo ID document type)
          "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
          }
        ]
      }
    ]
  },
  "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
        ],
        "issuerauth_alg_values": [
          -7
        ]
      }
    },
    "gw_rp_metadata_bytes": "<base64url encoded metadata string>"
  }
}

Cualquier credencial elegible

Este es el ejemplo de solicitud para mDL, pase de identificación y pase de identificación (con el tipo de documento de identidad con foto). El usuario puede continuar con cualquiera de las dos opciones.

{
  "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 pass
        "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 type 3: ID pass (using photo ID document type)
        "id": "photo_id-request",
        "format": "mso_mdoc",
        "meta": {
          "doctype_value": "org.iso.23220.photoid.1"
        },
        "claims": [
          {
            "path": [
              "org.iso.23220.1",
              "family_name"
            ],
            "intent_to_retain": false // set this to true if you are saving the value of the field
          },
          {
            "path": [
              "org.iso.23220.1",
              "given_name"
            ],
            "intent_to_retain": false
          },
          {
            "path": [
              "org.iso.23220.1",
              "age_over_18"
            ],
            "intent_to_retain": false
          }
        ]
      }
    ]
    credential_sets : [
      {
        "options": [
          [ "mdl-request" ],
          [ "id_pass-request" ],
          [ "photo_id-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
        ],
        "issuerauth_alg_values": [
          -7
        ]
      }
    },
    "gw_rp_metadata_bytes": "<base64url encoded metadata string>"
  }
}

Puedes solicitar cualquier cantidad de atributos admitidos de cualquier credencial de identidad almacenada en la Billetera de Google.

Solicitudes firmadas

Las solicitudes firmadas (solicitudes de autorización protegidas con JWT) encapsulan tu solicitud de presentación verificable dentro de un token web JSON (JWT) firmado criptográficamente con tu infraestructura de PKI, lo que garantiza la integridad de la solicitud y demuestra tu identidad a la Billetera de Google.

Requisitos previos

Antes de implementar los cambios de código para las solicitudes firmadas, asegúrate de tener lo siguiente:

  • Clave privada: Necesitas una clave privada (por ejemplo, curva elíptica ES256) para firmar la solicitud, que se administra en tu servidor. Para realizar pruebas de zona de pruebas como parte usuaria, usa la clave privada de prueba de la zona de pruebas.
  • Certificado: Necesitas un certificado X.509 estándar derivado de tu par de claves.
  • Registro: Asegúrate de que la Billetera de Google confíe en tu certificado público (o AC raíz para los registradores de verificadores).

Lógica de construcción de solicitudes

Cuando construyas y firmes tu solicitud de OpenID4VP, presta mucha atención al formato de codificación exacto que se espera para cada campo. Si mezclas base64 y base64url o el padding (=), fallará la validación de la firma o del origen:

Campo Codificación o formato obligatorios Caracteres y relleno
client_id (x509_hash:<hash>) base64url sin relleno Usa un alfabeto seguro para URLs (-, _); quita el = final
expected_origins (android:apk-key-hash:<hash>) Base64 estándar sin relleno Usa el alfabeto estándar (+, /); quita el = final
expected_origins (https://...) URL en minúsculas sin barras finales Ejemplo: https://example.com (sin / al final)
Encabezado JWS de x5c Base64 estándar con relleno Usa el alfabeto estándar (+, /); conserva el relleno = final

Para crear una solicitud, usa tu clave privada y une la carga útil en un JWS:

def construct_openid4vp_request(
    doctypes: list[str],
    requested_fields: list[dict],
    nonce_base64: str,
    jwe_encryption_public_jwk: jwk.JWK,
    is_zkp_request: bool,
    is_signed_request: bool,
    state: dict,
    origin: str
) -> dict:

    # ... [Existing logic to build 'presentation_definition' and basic 'request_payload'] ...

    # ------------------------------------------------------------------
    # SIGNED REQUEST IMPLEMENTATION (JAR)
    # ------------------------------------------------------------------
    if is_signed_request:
        try:
            # 1. Load the Verifier's Certificate
            # We must load the PEM string into a cryptography x509 object
            verifier_cert_obj = x509.load_pem_x509_certificate(
                CERTIFICATE.encode('utf-8'),
                backend=default_backend()
            )

            # 2. Calculate Client ID (x509_hash)
            # We calculate the SHA-256 hash of the DER-encoded certificate.
            cert_der = verifier_cert_obj.public_bytes(serialization.Encoding.DER)
            verifier_fingerprint_bytes = hashlib.sha256(cert_der).digest()

            # Important: Use URL-safe Base64 (base64url: '-', '_') without padding ('=') for client_id
            verifier_fingerprint_b64 = base64.urlsafe_b64encode(verifier_fingerprint_bytes).decode('utf-8').rstrip("=")

            # Format the client_id as required by the spec
            client_id = f'x509_hash:{verifier_fingerprint_b64}'

            # 3. Update Request Payload with JAR specific fields
            request_payload["client_id"] = client_id

            # Explicitly set expected origins to prevent relay attacks
            # Important: Use standard Base64 (not URL-safe) without padding for android:apk-key-hash:
            # origin = f"android:apk-key-hash:{base64.b64encode(apk_cert_hash).decode('utf-8').rstrip('=')}"
            # Format for web origin (lowercase URL without trailing slash): origin = "https://example.com"
            if origin:
                request_payload["expected_origins"] = [origin]

            # 4. Create Signed JWT (JWS)
            # Load the signing private key
            signing_key = jwk.JWK.from_pem(PRIVATE_KEY.encode('utf-8'))

            # Initialize JWS with the JSON payload
            jws_token = jws.JWS(json.dumps(request_payload).encode('utf-8'))

            # Construct the JOSE Header
            # 'x5c' (X.509 Certificate Chain) is critical: it allows the wallet
            # to validate your key against the one registered in the console.
            # Important: Use standard Base64 ('+', '/') WITH padding ('=') for x5c
            x5c_value = base64.b64encode(cert_der).decode('utf-8')

            protected_header = {
                "alg": "ES256",                 # Algorithm (e.g., ES256 or RS256)
                "typ": "oauth-authz-req+jwt",   # Standard type for JAR
                "kid": "1",                     # Key ID
                "x5c": [x5c_value]              # Embed the certificate
            }

            # Sign the token
            jws_token.add_signature(
                key=signing_key,
                alg=None,
                protected=json_encode(protected_header)
            )

            # 5. Return the Request Object
            # Instead of returning the raw JSON, we return the signed JWT string
            # under the 'request' key.
            return {"request": jws_token.serialize(compact=True)}

        except Exception as e:
            print(f"Error signing OpenID4VP request: {e}")
            return None

    # ... [Fallback for unsigned requests] ...
    return request_payload

Paso C: Activa la API (del lado del cliente)

Toda la solicitud a la API se debe generar del lado del servidor. Según la plataforma, pasa el JSON generado a las APIs de la plataforma.

En la app (Android)

Para solicitar credenciales de identidad desde tus apps para Android, sigue estos pasos:

Actualiza las dependencias

En el archivo build.gradle de tu proyecto, actualiza las dependencias para usar el Administrador de credenciales (beta):

dependencies {
    implementation("androidx.credentials:credentials:1.5.0-beta01")
    implementation("androidx.credentials:credentials-play-services-auth:1.5.0-beta01")
}

Cómo configurar el Administrador de credenciales

Para configurar e inicializar un objeto CredentialManager, agrega una lógica similar a la siguiente:

// Use your app or activity context to instantiate a client instance of CredentialManager.
val credentialManager = CredentialManager.create(context)

Atributos de identidad de la solicitud

En lugar de especificar parámetros individuales para las solicitudes de identidad, la app los proporciona todos juntos como una cadena JSON dentro de CredentialOption. El Administrador de credenciales pasa esta cadena JSON a las billeteras digitales disponibles sin examinar su contenido. Cada billetera es responsable de lo siguiente:

  • Analizar la cadena JSON para comprender la solicitud de identidad
  • Determinar cuáles de sus credenciales almacenadas, si las hay, satisfacen la solicitud

Recomendamos que los socios creen sus solicitudes en el servidor, incluso para las integraciones de apps para Android.

Usarás el requestJson de Asigna un formato a la solicitud y fírmala como el request en la llamada a función GetDigitalCredentialOption().

// 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)
    }
}

Controla la respuesta de credenciales

Una vez que recibas una respuesta de la billetera, verifica si es correcta y contiene la respuesta 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}")
    }
}

La respuesta credentialJson contiene un identityToken (JWT) encriptado, definido por el W3C. La app de la Billetera es responsable de crear esta respuesta.

Ejemplo:

{
  "protocol" : "openid4vp-v1-signed",
  "data" : {
    <encrypted_response>
  }
}

Pasa esta respuesta a tu servidor para validar su autenticidad. Consulta Cómo desencriptar y validar la respuesta.

Web

Para solicitar credenciales de identidad con la API de Digital Credentials en Chrome o en otros navegadores compatibles, realiza la siguiente solicitud:

const credentialResponse = await navigator.credentials.get({
          digital : {
          requests : [
            {
              protocol: "openid4vp-v1-signed",
              data: {<credential_request>} // This is an object, shouldn't be a string.
            }
          ]
        }
      })

Envía la respuesta de esta API a tu servidor para validar la respuesta de la credencial.


Paso D: Desencripta y valida la respuesta (en el servidor)

Una vez que la billetera devuelva el identityToken (JWT) encriptado, debes realizar una validación del servidor estricta antes de confiar en los datos.

Desencripta la respuesta

Usa la clave privada correspondiente a la clave pública enviada en el client_metadata de la solicitud para desencriptar el JWE. Esto genera un vp_token.

Ejemplo de 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 dará como resultado un JSON vp_token que contiene la credencial:

{
  "vp_token":
  {
    "cred1": ["<base64UrlNoPadding_encoded_credential>"] // This applies to OpenID4VP 1.0 spec.
  }
}

Reconstruye el SessionTranscript y valida la DeviceResponse

  1. Crea el SessionTranscript a partir de ISO/IEC 18013-5:2021 con una estructura de transferencia específica para Android o la Web:
SessionTranscript = [
  null,                // DeviceEngagementBytes not available
  null,                // EReaderKeyBytes not available
  [
    "OpenID4VPDCAPIHandover",
    AndroidHandoverDataBytes   // BrowserHandoverDataBytes for Web
  ]
]

Para las transferencias de Android y de la Web, usa el mismo nonce que usaste para generar credential_request.

Transferencia de 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()

Transferencia del navegador

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()

Con la SessionTranscript, la respuesta del dispositivo se debe validar de acuerdo con la cláusula 9 de la norma ISO/IEC 18013-5:2021.

Esta validación incluye cuatro verificaciones:

  1. Verifica el certificado de la entidad emisora: Extrae la cadena de certificados de firma de la entidad emisora de issuerAuth y valídala con los certificados raíz de la IACA de confianza. Consulta Entidades emisoras y certificados de IACA admitidos (y agrega el certificado de la zona de pruebas de Utopia cuando realices pruebas con un ID de prueba de zona de pruebas).
  2. Verifica la firma de MSO (ISO/IEC 18013-5, sección 9.1.2).
  3. Calcula y verifica ValueDigests para los elementos de datos (ISO/IEC 18013-5, sección 9.1.2).
  4. Verifica la firma de deviceSignature (ISO/IEC 18013-5, sección 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
}

Paso E (opcional): Verificación de edad que preserva la privacidad (ZKP)

Para admitir pruebas de conocimiento cero (por ejemplo, verificar que un usuario sea mayor de 18 años sin ver su fecha de nacimiento exacta), cambia el formato de la solicitud a mso_mdoc_zk y proporciona la configuración de zk_system_type requerida.

Para obtener una descripción general de alto nivel de qué es ZKP y sus capacidades, consulta las Preguntas frecuentes.

  ...
  "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
            {
              ...

Recibirás una prueba de conocimiento cero encriptada de la billetera. Puedes validar esta prueba con los certificados de la IACA de la entidad emisora usando la biblioteca longfellow-zk de Google.

El verifier-service contiene un servidor basado en Docker listo para la implementación que te permite validar la respuesta con los certificados de IACA de la entidad emisora.

Puedes modificar certs.pem para administrar los certificados de la entidad emisora de IACA en los que deseas confiar.


SDKs, implementaciones de referencia y asistencia

Usa nuestras bibliotecas de código abierto, el servidor de referencia y la herramienta de verificación web para acelerar la implementación:

Recurso / biblioteca Qué proporciona Vínculo
Implementación de referencia de los verificadores de identidad Servidor y cliente de muestra que demuestran la generación de solicitudes de OpenID4VP, la firma de JWS, la desencriptación de JWE y la validación de respuestas de mdoc. Repositorio de GitHub
SDK de Multipaz Biblioteca de código abierto (OpenWallet Foundation) que incluye DeviceResponseParser para la decodificación de CBOR, la reconstrucción de SessionTranscript y la validación de firmas ISO/IEC 18013-5/7. openwallet-foundation/multipaz
Biblioteca de ZKP y servicio de verificador de Longfellow Biblioteca de código abierto y servidor basado en Docker para verificar las aserciones de edad y atributos de la prueba de conocimiento cero (mso_mdoc_zk). google/longfellow-zk
Herramienta interactiva de verificación web Verificador web alojado para probar las solicitudes de la API de credenciales digitales de W3C y OpenID4VP en la Billetera de Google. verifier.multipaz.org
Especificación de OpenID4VP 1.0 Especificación oficial del protocolo OpenID para presentaciones verificables 1.0. Especificación de OpenID4VP
Preguntas frecuentes y asistencia para desarrolladores Respuestas a preguntas comunes sobre la integración técnica o envía un correo electrónico a wallet-identity-rp-support@google.com para obtener asistencia con la depuración. Preguntas frecuentes sobre la integración técnica

Próximos pasos