Aceptación en línea de credenciales digitales

Los IDs digitales se pueden aceptar en los flujos en la aplicación y web. Para aceptar credenciales de la Billetera de Google, deberás hacer lo siguiente:

  1. Realiza la integración con la app o la Web siguiendo las instrucciones proporcionadas.
  2. Completa este formulario para solicitar y aceptar las condiciones del servicio de aceptación de credenciales de la Billetera de Google.

Requisitos previos

Para probar la presentación de IDs de forma digital, primero debes inscribirte en el programa beta público con la cuenta de prueba que quieras (debe ser una cuenta de Gmail). Luego, proporciona los siguientes detalles a tu contacto de Google designado.

  • Vínculo a las Condiciones del Servicio
  • Logotipo
  • Sitio web
  • IDs de paquetes de aplicaciones (para integraciones de apps para Android)
    • Incluye compilaciones de depuración o de desarrollador
  • Firma de la app
    • $ $ANDROID_SDK/build-tools/$BUILD_TOOLS_VERSION/apksigner verify --print-certs -v $APK
  • El ID de Gmail que se usó para unirse a la versión beta pública

Formatos de credenciales compatibles

Existen varios estándares propuestos que definen el formato de datos de los documentos de identidad digital, y dos de ellos están ganando una gran aceptación en la industria:

  1. mdocs: Es definido por ISO.
  2. Credenciales verificables de W3C: definidas por W3C.

Si bien el Administrador de credenciales de Android admite ambos formatos, por el momento, la Billetera de Google solo admite IDs digitales basados en mdoc.

Credenciales admitidas

La Billetera de Google admite 2 tipos de credenciales:

  1. Licencia de Conducir Digital (mDL)
  2. Pase de ID

Puedes solicitar cualquiera de las credenciales en tu flujo con un solo cambio de parámetro.

Experiencia del usuario

En esta sección, se explica el flujo de presentación en línea recomendado. El flujo muestra la presentación de la edad a una app para la entrega de bebidas alcohólicas, pero la UX es similar para la Web y otros tipos de presentaciones.

Se le solicita al usuario que verifique su edad en la aplicación o el sitio web El usuario ve las credenciales aptas disponibles El usuario ve la página de confirmación en la Billetera de Google El usuario se autentica para confirmar el uso compartido Datos enviados a la app o al sitio web
Se le solicita al usuario que verifique su edad en la aplicación o el sitio web El usuario ve las credenciales aptas disponibles El usuario ve la página de confirmación en la Billetera de Google El usuario se autentica para confirmar el uso compartido Datos enviados a la app o al sitio web

Notas clave

  1. La app o el sitio web tienen flexibilidad en la forma en que crean el punto de entrada a la API. Como se muestra en el paso 1, te recomendamos que muestres un botón genérico, como "Verificar con ID digital", ya que, con el tiempo, esperamos que haya opciones más allá de la Billetera de Google disponibles a través de la API.
  2. Android renderiza la pantalla del selector en el paso 2. Las credenciales aptas se determinan mediante una coincidencia entre la lógica de registro que proporciona cada billetera y la solicitud que envía la parte de confianza.
  3. La Billetera de Google renderiza el paso 3. En esta pantalla, la Billetera de Google mostrará el nombre, el logotipo y la política de privacidad que proporciona el desarrollador.

Agrega un flujo de ID digital

En caso de que el usuario no tenga una credencial, te recomendamos que proporciones un vínculo junto al botón "Verificar con ID digital", que establecerá un vínculo directo con la Billetera de Google para permitir que el usuario agregue un ID digital.

Se le solicita al usuario que verifique su edad en la aplicación o el sitio web Se dirige al usuario a la Billetera de Google para obtener un ID digital
Se le solicita al usuario que verifique su edad en la aplicación o el sitio web Se dirige al usuario a la Billetera de Google para obtener un ID digital

No hay un ID digital disponible

Si el usuario selecciona la opción "Verificar con ID digital" sin tener un ID digital, se le mostrará este mensaje de error.

Se le solicita al usuario que verifique su edad en la aplicación o el sitio web Se muestra un error al usuario si no tiene un ID digital
Se le solicita al usuario que verifique su edad en la aplicación o el sitio web Se muestra un error al usuario si no tiene un ID digital

La API no admite una función para obtener información en silencio sobre si el usuario tiene alguna ID digital disponible para preservar su privacidad. Por lo tanto, te recomendamos que incluyas la opción de vínculo de integración como se muestra.

Formato de solicitud para solicitar credenciales de ID desde la billetera

Este es un ejemplo de una solicitud de requestJson de mdoc para obtener credenciales de identidad desde cualquier billetera en un dispositivo Android o la Web.

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

Solicitar encriptación

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

El parámetro credential_request en requestJson consistiría en los siguientes campos:

{
  "response_type": "vp_token",
  "response_mode": "dc_api.jwt",
  "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"
            ]
          },
          {
            "path": [
              "org.iso.18013.5.1",
              "given_name"
            ]
          },
          {
            "path": [
              "org.iso.18013.5.1",
              "age_over_18"
            ]
          }
        ]
      }
    ]
  },
  "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",
          "alg" : "ECDH-ES",
        }
      ]
    },
    "authorization_encrypted_response_alg": "ECDH-ES",
    "authorization_encrypted_response_enc": "A128GCM"
  }
}

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

En la app

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")
    // optional - needed for credentials support from play services, for devices running Android 13 and below.
    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)

Solicita atributos de identidad

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 a los socios que creen sus solicitudes en el servidor incluso para las integraciones de apps para Android.

Usarás el requestJson del Formato de solicitud, que consiste en el request en la llamada a la 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)
    }
}

Verifica y valida la respuesta

Una vez que recibas una respuesta de la billetera, verificarás si la respuesta es exitosa 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, que define el W3C. La app de Wallet es responsable de crear esta respuesta.

Ejemplo:

"response" : {
  <encrpted_response>
}

Volverás a pasar esta respuesta al servidor para validar su autenticidad. Puedes encontrar los pasos para validar la respuesta de la credencial.

Web

Para solicitar credenciales de identidad con la API de Digital Credentials en Chrome, deberás registrarte en la prueba de origen de la API de Digital Credentials.

const credentialResponse = await navigator.credentials.get({
          digital : {
          requests : [
            {
              protocol: "openid4vp",
              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.

Pasos para validar la respuesta de la credencial

Cuando recibas el identityToken encriptado de tu app o sitio web, deberás realizar varias validaciones antes de confiar en la respuesta.

  1. Cómo desencriptar una respuesta con una clave privada

    El primer paso es desencriptar el token con la clave privada guardada y obtener una respuesta JSON.

    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)
    
    # 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 generará un JSON vp_token que contendrá la credencial.

    {
      "vp_token":
      {
        "cred1": "<credential_token>"
      }
    }
    
  2. Crea la transcripción de la sesión

    El siguiente paso es crear el SessionTranscript de la norma 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
      [
        "OID4VPDCAPIHandover",
        AndroidHandoverDataBytes   // BrowserHandoverDataBytes for Web
      ]
    ]
    

    Para las transferencias web y de Android, deberás usar el mismo nonce que usaste para generar credential_request.

    Transferencia de Android

        AndroidHandoverData = [
          origin,             // "android:apk-key-hash:<base64SHA256_ofAppSigningCert>",
          clientId,           // "android-origin:<app_package_name>",
          nonce,              // nonce that was used to generate credential request
        ]
    
        AndroidHandoverDataBytes = hashlib.sha256(cbor2.dumps(AndroidHandoverData)).digest()
        

    Transferencia del navegador

        BrowserHandoverData =[
          origin,               // Origin URL
          clientId,             // "web-origin:<origin>"
          nonce,               //  nonce that was used to generate credential request
        ]
    
        BrowserHandoverDataBytes = hashlib.sha256(cbor2.dumps(BrowserHandoverData)).digest()
        

    Con SessionTranscript, DeviceResponse se debe validar de acuerdo con el artículo 9 de la norma ISO/IEC 18013-5:2021. Esto incluye varios pasos, como los siguientes:

  3. Verifica el certificado de la entidad emisora estatal. Consulta los certificados de la IACA de la entidad emisora compatible.

  4. Verifica la firma del MSO (Sección 9.1.2 del estándar 18013-5)

  5. Calcula y verifica los ValueDigests de los elementos de datos (Sección 9.1.2 de la norma 18013-5)

  6. Verifica la firma de deviceSignature (Sección 9.1.3 de la norma 18013-5)

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

Prueba tu solución

Para probar tu solución, compila y ejecuta nuestra aplicación para Android de referencia de código abierto. Estos son los pasos para compilar y ejecutar la app del contenedor de referencia:

  • Clona el repositorio de apps de referencia
  • Abre el proyecto en Android Studio.
  • Compila y ejecuta el destino appholder en tu dispositivo Android o emulador.

Verificación basada en la prueba de conocimiento cero (ZKP)

La prueba de conocimiento cero (ZKP) es un método criptográfico que permite a una persona (el verificador) demostrarle a un verificador que posee una determinada información de identidad o que cumple con un criterio específico (p.ej., que tiene más de 18 años o que tiene una credencial válida) sin revelar los datos subyacentes reales. En esencia, es una forma de confirmar la veracidad de una declaración sobre la identidad de una persona mientras se mantienen en privado los detalles sensibles.

Los sistemas de identidad digital que dependen del uso compartido directo de datos de identidad a menudo requieren que los usuarios compartan información personal excesiva, lo que aumenta el riesgo de violaciones de la seguridad de los datos y el robo de identidad. Las ZKP ofrecen un cambio de paradigma, lo que permite la verificación con una divulgación mínima.

Conceptos clave de las ZKP en la identidad digital:

  • Probador: La persona que intenta probar un aspecto de su identidad.
  • Verificador: Es la entidad que solicita un comprobante de un atributo de identidad.
  • La prueba: Un protocolo criptográfico que permite al verificador convencer al verificador de la veracidad de su declaración sin revelar la información secreta.

Propiedades principales de las pruebas de conocimiento cero:

  • Completitud: Si la declaración es verdadera y tanto el verificador como el verificador son honestos, el verificador se convencerá.
  • Solidez: Si la declaración es falsa, un verificador deshonesto no puede (con una probabilidad muy alta) convencer a un verificador honesto de que es verdadera.
  • Sin conocimiento: El verificador no aprende nada más que el hecho de que la declaración es verdadera. No se exponen datos reales de la identidad del verificador.

Para obtener una prueba de conocimiento cero de la Billetera de Google, debes cambiar el formato de la solicitud a mso_mdoc_zk y agregar zk_system_type a tu solicitud.

  ...
  "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": "2b8e0c49b08eb1801b9bd7a82aa9eb3736a7519fc2b409asdhj1237034", // This will differ if you need more than 1 attribute.
           "num_attributes": 1,
           "version": 1
         }
       ],
       "verifier_message": "challenge"
      },
     "claims": [{
         ...