Criptografía de datos de pagos para comercios

La API de Google Pay devuelve formas de pago en una carga útil PaymentMethodToken firmada y encriptada. Las formas de pago que se devuelven son tarjetas que constan de un PAN o tarjetas tokenizadas que constan de un PAN del dispositivo y criptogramas.

La carga útil contiene un campo llamado protocolVersion que le indica al destinatario de la carga útil qué primitivas criptográficas se están usando y el formato esperado.

En esta guía, se proporciona información para generar una clave pública y solicitar un token de forma de pago encriptado y firmado por Google, y se detallan los pasos para verificar y desencriptar el token.

Esta guía solo se aplica a protocolVersion = ECv2.

Como recibes la información de la tarjeta de pago directamente, asegúrate de que tu app cumpla con los estándares de PCI DSS y de que tus servidores tengan la infraestructura necesaria para controlar de forma segura las credenciales de pago del usuario antes de continuar.

En los siguientes pasos, se describe lo que debe hacer un integrador para consumir la carga útil ECv2 PaymentMethodToken de la API de Google Pay:

  1. Recupera las claves de firma raíz de Google.
  2. Verifica que la firma de la clave de firma intermedia sea válida con cualquiera de las claves de firma raíz que no hayan vencido.
  3. Verifica que la clave de firma intermedia de la carga útil no haya vencido.
  4. Verifica que la firma de la carga útil sea válida con la clave de firma intermedia.
  5. Desencripta el contenido de la carga útil después de verificar la firma.
  6. Verifica que el mensaje no haya vencido. Para ello, debes verificar que la hora actual sea anterior al campo messageExpiration en el contenido descifrado.
  7. Usar la forma de pago en el contenido desencriptado y cobrarla

El código de muestra de nuestra biblioteca de Tink realiza los pasos del 1 al 6.

Estructura del token de la forma de pago

El mensaje que devuelve Google en la respuesta PaymentData es un objeto JSON serializado y codificado en UTF-8 con las claves especificadas en la siguiente tabla:

Nombre Tipo Descripción
protocolVersion String Identifica el esquema de firma o encriptación con el que se crea el mensaje. Permite que el protocolo evolucione con el tiempo, si es necesario.
signature String Verifica que el mensaje provenga de Google. Está codificada en Base64 y se crea con ECDSA a través de la clave de firma intermedia.
intermediateSigningKey Objeto Es un objeto JSON que contiene la clave de firma intermedia de Google. Contiene el signedKey con keyValue, keyExpiration y signatures. Se serializa para simplificar el proceso de verificación de la firma de la clave de firma intermedia.
signedMessage String Objeto JSON serializado como una cadena segura para HTML que contiene encryptedMessage, ephemeralPublicKey y tag. Se serializa para simplificar el proceso de verificación de la firma.

Ejemplo

A continuación, se muestra una respuesta de token de método de pago en JSON:

{
  "protocolVersion":"ECv2",
  "signature":"MEQCIH6Q4OwQ0jAceFEkGF0JID6sJNXxOEi4r+mA7biRxqBQAiAondqoUpU/bdsrAOpZIsrHQS9nwiiNwOrr24RyPeHA0Q\u003d\u003d",
  "intermediateSigningKey":{
    "signedKey": "{\"keyExpiration\":\"1542323393147\",\"keyValue\":\"MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAE/1+3HBVSbdv+j7NaArdgMyoSAM43yRydzqdg1TxodSzA96Dj4Mc1EiKroxxunavVIvdxGnJeFViTzFvzFRxyCw\\u003d\\u003d\"}",
    "signatures": ["MEYCIQCO2EIi48s8VTH+ilMEpoXLFfkxAwHjfPSCVED/QDSHmQIhALLJmrUlNAY8hDQRV/y1iKZGsWpeNmIP+z+tCQHQxP0v"]
  },
  "signedMessage":"{\"tag\":\"jpGz1F1Bcoi/fCNxI9n7Qrsw7i7KHrGtTf3NrRclt+U\\u003d\",\"ephemeralPublicKey\":\"BJatyFvFPPD21l8/uLP46Ta1hsKHndf8Z+tAgk+DEPQgYTkhHy19cF3h/bXs0tWTmZtnNm+vlVrKbRU9K8+7cZs\\u003d\",\"encryptedMessage\":\"mKOoXwi8OavZ\"}"
}

Clave de firma intermedia

El intermediateSigningKey es un objeto JSON serializado y codificado en UTF-8 que contiene los siguientes valores:

Nombre Tipo Descripción
signedKey String Es un mensaje codificado en Base64 que contiene la descripción del pago de la clave.
signatures String Verifica que la clave de firma intermedia provenga de Google. Está codificada en Base64 y se creó con ECDSA.

Clave firmada

El signedKey es un objeto JSON serializado y codificado en UTF-8 que contiene los siguientes valores:

Nombre Tipo Descripción
keyValue String Es una versión en Base64 de la clave codificada en el tipo ASN.1. SubjectPublicKeyInfo se define en el estándar X.509.
keyExpiration String Fecha y hora en que vence la clave intermedia, expresada en milisegundos UTC desde la época. Los integradores rechazan cualquier clave que haya vencido.

Mensaje firmado

El signedMessage es un objeto JSON serializado y codificado en UTF-8 que contiene los siguientes valores:

Nombre Tipo Descripción
encryptedMessage String Es un mensaje encriptado codificado en Base64 que contiene información de pago y algunos campos de seguridad adicionales.
ephemeralPublicKey String Es una clave pública efímera codificada en Base64 asociada a la clave privada para encriptar el mensaje en formato de punto sin comprimir. Para obtener más información, consulta Formato de clave pública de encriptación.
tag String Es un MAC de encryptedMessage codificado en base64.

Mensaje encriptado

El objeto encryptedMessage desencriptado es un objeto JSON serializado y codificado en UTF-8. El JSON contiene dos niveles. El nivel externo contiene metadatos y campos incluidos por motivos de seguridad, mientras que el nivel interno es otro objeto JSON que representa la credencial de pago real.

Para obtener más detalles sobre encryptedMessage, consulta las siguientes tablas y ejemplos de objetos JSON:

Nombre Tipo Descripción
messageExpiration String Fecha y hora en la que vence el mensaje, expresada en milisegundos UTC desde la época. Los integradores deben rechazar cualquier mensaje que haya vencido.
messageId String Es un ID único que identifica el mensaje en caso de que deba revocarse o ubicarse en un momento posterior.
paymentMethod String Es el tipo de credencial de pago. Actualmente, solo se admite CARD.
paymentMethodDetails Objeto Es la credencial de pago en sí. El formato de este objeto se determina según paymentMethod y se describe en las siguientes tablas.

Cartas

Las siguientes propiedades conforman una credencial de pago para la forma de pago CARD:

Nombre Tipo Descripción
pan String Es el número de la cuenta personal a la que se le cobró el cargo. Esta cadena solo contiene dígitos.
expirationMonth Número Mes de vencimiento de la tarjeta, donde 1 representa enero, 2 representa febrero, y así sucesivamente.
expirationYear Número Año de vencimiento de la tarjeta con formato de cuatro dígitos, p. ej., 2020.
authMethod String Es el método de autenticación de la transacción con tarjeta.

PAN_ONLY

El siguiente fragmento de código JSON es un ejemplo del encryptedMessage completo para un CARD paymentMethod con un PAN_ONLY authMethod.

{
  "paymentMethod": "CARD",
  "paymentMethodDetails": {
    "authMethod": "PAN_ONLY",
    "pan": "1111222233334444",
    "expirationMonth": 10,
    "expirationYear": 2025
  },
  "gatewayMerchantId": "some-merchant-id",
  "messageId": "some-message-id",
  "messageExpiration": "1759309000000"
}

CRYPTOGRAM_3DS

Un CARD autenticado con el uso de un criptograma de 3-D Secure, CRYPTOGRAM_3DS authMethod. Incluye los siguientes campos adicionales:

Nombre Tipo Descripción
cryptogram String Es un criptograma de 3-D Secure.
eciIndicator String Esta cadena no siempre está presente. Solo se devuelve para las transacciones con tokens de dispositivos autenticados en Android (CRYPTOGRAM_3DS). Este valor se debe pasar por el flujo de procesamiento de pagos.

El siguiente fragmento de código JSON es un ejemplo del objeto encryptedMessage completo para un objeto CARD paymentMethod con un objeto CRYPTOGRAM_3DS authMethod:

{
  "paymentMethod": "CARD",
  "paymentMethodDetails": {
    "authMethod": "CRYPTOGRAM_3DS",
    "pan": "1111222233334444",
    "expirationMonth": 10,
    "expirationYear": 2025,
    "cryptogram": "AAAAAA...",
    "eciIndicator": "eci indicator"
    
  },
  
  "messageId": "some-message-id",
  "messageExpiration": "1759309000000"
}

eciIndicator

Es posible que la red de tarjetas proporcione el eciIndicator para las transacciones con tokens de dispositivos autenticados (CRYPTOGRAM_3DS).

Debes pasar el valor de eciIndicator en la transacción de autorización sin que se altere ni se codifique de forma rígida; de lo contrario, la transacción fallará. En la siguiente tabla, se detallan los valores de eciIndicator.

Valor de eciIndicator Red de tarjetas Parte responsable authMethod
""(empty) Mastercard Comerciante/adquirente CRYPTOGRAM_3DS
02 Mastercard Entidad emisora de la tarjeta CRYPTOGRAM_3DS
06 Mastercard Comerciante/adquirente CRYPTOGRAM_3DS
05 Visa Entidad emisora de la tarjeta CRYPTOGRAM_3DS
07 Visa Comerciante/adquirente CRYPTOGRAM_3DS
""(empty) Otras redes Comerciante/adquirente CRYPTOGRAM_3DS

No se devolverá ningún otro valor de ECI para VISA y Mastercard que no esté presente en esta tabla.

Verificación de la firma

Para verificar las firmas, que incluyen la clave intermedia y las firmas de mensajes, se requieren los siguientes elementos:

  • Es el algoritmo que se usa para crear la firma.
  • Es la cadena de bytes que se usa para crear la firma.
  • Es la clave pública que corresponde a la privada que se usó para crear la firma.
  • La firma en sí

El algoritmo de firma

Google usa el algoritmo de firma digital de curva elíptica (ECDSA) para firmar los mensajes con los siguientes parámetros: ECDSA sobre NIST P-256 con SHA-256 como función hash, según se define en FIPS 186-4.

La firma

La firma se incluye en el nivel más externo del mensaje. Se codifica con base64 en formato de bytes ASN.1. Para obtener más información sobre ASN.1, consulta el Apéndice A de las herramientas de IETF. La firma consta de los números enteros r y s del ECDSA. Para obtener más información, consulta Algoritmo de generación de firmas.

A continuación, se muestra un ejemplo del formato de bytes ASN.1 especificado, que es el formato estándar que producen las implementaciones de ECDSA de Java Cryptography Extension (JCE).

ECDSA-Sig-Value :: = SEQUENCE {
 r INTEGER,
 s INTEGER
}

Cómo construir la cadena de bytes para la firma de la clave de firma intermedia

Para validar la firma de la clave de firma intermedia en el token de la forma de pago de muestra, construye el signedStringForIntermediateSigningKeySignature con la siguiente fórmula:

signedStringForIntermediateSigningKeySignature =
length_of_sender_id || sender_id || length_of_protocol_version || protocol_version || length_of_signed_key || signed_key

La notación "||" significa concatenar. Cada componente (sender_id, protocolVersion, signedKey) debe estar codificado en UTF-8. El signedKey debe ser la cadena de intermediateSigningKey.signedKey. La longitud en bytes de cada componente es de 4 bytes en formato little-endian.

Ejemplo

En este ejemplo, se usa el siguiente token de método de pago de muestra:

{
  "protocolVersion":"ECv2",
  "signature":"MEQCIH6Q4OwQ0jAceFEkGF0JID6sJNXxOEi4r+mA7biRxqBQAiAondqoUpU/bdsrAOpZIsrHQS9nwiiNwOrr24RyPeHA0Q\u003d\u003d",
  "intermediateSigningKey":{
    "signedKey": "{\"keyExpiration\":\"1542323393147\",\"keyValue\":\"MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAE/1+3HBVSbdv+j7NaArdgMyoSAM43yRydzqdg1TxodSzA96Dj4Mc1EiKroxxunavVIvdxGnJeFViTzFvzFRxyCw\\u003d\\u003d\"}",
    "signatures": ["MEYCIQCO2EIi48s8VTH+ilMEpoXLFfkxAwHjfPSCVED/QDSHmQIhALLJmrUlNAY8hDQRV/y1iKZGsWpeNmIP+z+tCQHQxP0v"]
  },
  "signedMessage":"{\"tag\":\"jpGz1F1Bcoi/fCNxI9n7Qrsw7i7KHrGtTf3NrRclt+U\\u003d\",\"ephemeralPublicKey\":\"BJatyFvFPPD21l8/uLP46Ta1hsKHndf8Z+tAgk+DEPQgYTkhHy19cF3h/bXs0tWTmZtnNm+vlVrKbRU9K8+7cZs\\u003d\",\"encryptedMessage\":\"mKOoXwi8OavZ\"}"
}

El sender_id siempre es Google y el protocol_version es ECv2.

Si sender_id es Google, signedString aparece como se muestra en el siguiente ejemplo:

signedStringForIntermediateSigningKeySignature =
\x06\x00\x00\x00 || Google || | \x04\x00\x00\x00 || ECv2 || \xb5\x00\x00\x00 || {"keyExpiration":"1542323393147","keyValue":"MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAE/1+3HBVSbdv+j7NaArdgMyoSAM43yRydzqdg1TxodSzA96Dj4Mc1EiKroxxunavVIvdxGnJeFViTzFvzFRxyCw\u003d\u003d"}

Cómo verificar la firma en signedStringForIntermediateSigningKeySignature

El algoritmo de verificación de ECDSA estándar se usa cuando se ensambla la cadena firmada para la firma de la clave de firma intermedia. En el caso del protocolo ECv2, debes iterar sobre todas las firmas en intermediateSigningKey.signatures y tratar de validar cada una con las claves de firma de Google no vencidas en keys.json. Si funciona al menos una validación de firma, considera que la verificación se completó. Usa intermediateSigningKey.signedKey.keyValue más adelante para verificar signedStringForMessageSignature. Google recomienda encarecidamente que uses una biblioteca criptográfica existente en lugar de tu propio código de verificación.

Cómo construir la cadena de bytes para la firma del mensaje

Para validar la firma en el token de la forma de pago de muestra, construye el signedStringForMessageSignature con la siguiente fórmula:

signedStringForMessageSignature =
length_of_sender_id || sender_id || length_of_recipient_id || recipient_id || length_of_protocolVersion || protocolVersion || length_of_signedMessage || signedMessage

La notación "||" significa concatenar. Cada componente (sender_id, recipient_id, protocolVersion, signedMessage) debe estar codificado en UTF-8. La longitud en bytes de cada componente es de 4 bytes en formato little-endian. Cuando construyas la cadena de bytes, no analices ni modifiques signedMessage. Por ejemplo, no reemplaces \u003d por el carácter =.

Ejemplo

El siguiente ejemplo es un token de método de pago de muestra:

{
  "protocolVersion":"ECv2",
  "signature":"MEQCIH6Q4OwQ0jAceFEkGF0JID6sJNXxOEi4r+mA7biRxqBQAiAondqoUpU/bdsrAOpZIsrHQS9nwiiNwOrr24RyPeHA0Q\u003d\u003d",
  "intermediateSigningKey":{
    "signedKey": "{\"keyExpiration\":\"1542323393147\",\"keyValue\":\"MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAE/1+3HBVSbdv+j7NaArdgMyoSAM43yRydzqdg1TxodSzA96Dj4Mc1EiKroxxunavVIvdxGnJeFViTzFvzFRxyCw\\u003d\\u003d\"}",
    "signatures": ["MEYCIQCO2EIi48s8VTH+ilMEpoXLFfkxAwHjfPSCVED/QDSHmQIhALLJmrUlNAY8hDQRV/y1iKZGsWpeNmIP+z+tCQHQxP0v"]
  },
  "signedMessage":"{\"tag\":\"jpGz1F1Bcoi/fCNxI9n7Qrsw7i7KHrGtTf3NrRclt+U\\u003d\",\"ephemeralPublicKey\":\"BJatyFvFPPD21l8/uLP46Ta1hsKHndf8Z+tAgk+DEPQgYTkhHy19cF3h/bXs0tWTmZtnNm+vlVrKbRU9K8+7cZs\\u003d\",\"encryptedMessage\":\"mKOoXwi8OavZ\"}"
}

El sender_id siempre es Google y el recipient_id es merchant:merchantId. merchantId coincide con el valor que se encuentra en la Consola de Google Pay y la Billetera de Google para los comercios con acceso a producción.

Si sender_id es Google y recipient_id es merchant:12345, signedString aparece como se muestra en el siguiente ejemplo:

signedStringForMessageSignature =
\x06\x00\x00\x00 || Google || \x0e\x00\x00\x00 || merchant:12345 || | \x04\x00\x00\x00 || ECv2 || \xd2\x00\x00\x00 || {"tag":"jpGz1F1Bcoi/fCNxI9n7Qrsw7i7KHrGtTf3NrRclt+U\u003d","ephemeralPublicKey":"BJatyFvFPPD21l8/uLP46Ta1hsKHndf8Z+tAgk+DEPQgYTkhHy19cF3h/bXs0tWTmZtnNm+vlVrKbRU9K8+7cZs\u003d","encryptedMessage":"mKOoXwi8OavZ"}

Cómo verificar la firma en signedStringForMessageSignature

Cuando se ensambla la cadena firmada, se usa el algoritmo de verificación de ECDSA estándar. El intermediateSigningKey.signedKey.keyValue verificado en el paso anterior se usa para verificar el signedMessage. Google recomienda encarecidamente que uses una biblioteca criptográfica existente en lugar de tu propio código de verificación.

Especificación del esquema de encriptación

Google usa el esquema de encriptación integrada de curva elíptica (ECIES) para proteger el token de la forma de pago que se devuelve en la respuesta de la API de Google Pay. El esquema de encriptación usa los siguientes parámetros:

Parámetro Definición
Método de encapsulamiento de claves

ECIES-KEM, según se define en ISO 18033-2.

  • Curva elíptica: NIST P-256 (también conocida en OpenSSL como prime256v1).
  • CheckMode, OldCofactorMode, SingleHashMode y CofactorMode son 0.
  • El formato de punto no está comprimido.
Función de derivación de claves

Basado en HMAC con SHA-256 (HKDFwithSHA256).

  • No se debe proporcionar sal.
  • La información debe estar codificada por Google en ASCII para la versión del protocolo ECv2.
  • Se deben derivar 256 bits para la clave AES256 y otros 256 bits para la clave HMAC_SHA256.
Algoritmo de encriptación simétrica

DEM2, según se define en ISO 18033-2

Algoritmo de encriptación: AES-256-CTR con IV cero y sin relleno.

Algoritmo de MAC HMAC_SHA256 con una clave de 256 bits derivada de la función de derivación de claves.

Formato de clave pública de encriptación

La clave pública de encriptación y el ephemeralPublicKey que se muestran en las cargas útiles de Google tienen el formato de la representación base64 de la clave en formato de punto sin comprimir. Consta de los siguientes dos elementos:

  • Un número mágico que especifica el formato (0x04).
  • Dos números enteros grandes de 32 bytes que representan las coordenadas X e Y en la curva elíptica.

Este formato se describe con más detalle en "Public Key Cryptography For The Financial Services Industry: The Elliptic Curve Digital Signature Algorithm (ECDSA)", ANSI X9.62, 1998.

Usa OpenSSL para generar una clave pública

Paso 1: Genera una clave privada

En el siguiente ejemplo, se genera una clave privada de curva elíptica apta para usar con NIST P-256 y se escribe en key.pem:

openssl ecparam -name prime256v1 -genkey -noout -out key.pem

Opcional: Visualiza las claves privadas y públicas

Usa el siguiente comando para ver la clave privada y la pública:

openssl ec -in key.pem -pubout -text -noout

El comando produce un resultado similar al siguiente:

read EC key
Private-Key: (256 bit)
priv:
    08:f4:ae:16:be:22:48:86:90:a6:b8:e3:72:11:cf:
    c8:3b:b6:35:71:5e:d2:f0:c1:a1:3a:4f:91:86:8a:
    f5:d7
pub:
    04:e7:68:5c:ff:bd:02:ae:3b:dd:29:c6:c2:0d:c9:
    53:56:a2:36:9b:1d:f6:f1:f6:a2:09:ea:e0:fb:43:
    b6:52:c6:6b:72:a3:f1:33:df:fa:36:90:34:fc:83:
    4a:48:77:25:48:62:4b:42:b2:ae:b9:56:84:08:0d:
    64:a1:d8:17:66
ASN1 OID: prime256v1

Paso 2: Genera una clave pública codificada en Base64

La clave pública y privada que se genera en el ejemplo del paso opcional anterior está codificada en hexadecimal. Para obtener una clave pública codificada en base64 en formato de punto sin comprimir, usa el siguiente comando:

openssl ec -in key.pem -pubout -text -noout 2> /dev/null | grep "pub:" -A5 | sed 1d | xxd -r -p | base64 | paste -sd "\0" - | tr -d '\n\r ' > publicKey.txt

El comando produce un archivo publicKey.txt cuyo contenido, la versión en base64 de la clave en formato de punto sin comprimir, se parece al siguiente:

BOdoXP+9Aq473SnGwg3JU1aiNpsd9vH2ognq4PtDtlLGa3Kj8TPf+jaQNPyDSkh3JUhiS0KyrrlWhAgNZKHYF2Y=

El contenido del archivo no debe tener espacios vacíos ni retornos de carro adicionales. Para verificarlo, ejecuta el siguiente comando en Linux o macOS:

od -bc publicKey.txt

Paso 3: Genera una clave privada codificada en base64 en formato PKCS #8

La biblioteca de Tink espera que tu clave privada esté codificada en Base64 en formato PKCS #8. Usa el siguiente comando para generar la clave privada en este formato a partir de la clave privada generada en el primer paso:

openssl pkcs8 -topk8 -inform PEM -outform DER -in key.pem -nocrypt | base64 | paste -sd "\0" -

El comando produce un resultado similar al siguiente:

MIGHAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBG0wawIBAQQgWV4oK8c/MZkCLk4qSCNjW0Zm6H0CBCtSYxkXkC9FBHehRANCAAQPldOnhO2/oXjdJD1dwlFPiNs6fcdoRgFu3/Z0iKj24SjTGyLRGAtYWLGXBZcDdPj3T2bJRHRVhE8Bc2AjkT7n

Cómo desencriptar el token de la forma de pago

Sigue estos pasos para descifrar el token:

  1. Usa tu clave privada y el ephemeralPublicKey proporcionado para derivar una clave compartida de 512 bits de longitud que use ECIES-KEM. Usa los siguientes parámetros:
    • Curva elíptica: NIST P-256, también conocida en OpenSSL como prime256v1.
    • CheckMode, OldCofactorMode, SingleHashMode y CofactorMode son 0.
    • Función de codificación: Formato de punto sin comprimir.
    • Función de derivación de claves: HKDFwithSHA256, como se describe en RFC 5869, con el siguiente parámetro:
      • No se debe proporcionar sal. Según el RFC, esto debe ser equivalente a una sal de 32 bytes con valor cero.
  2. Divide la clave generada en dos claves de 256 bits de longitud: symmetricEncryptionKey y macKey.
  3. Verifica que el campo tag sea una dirección MAC válida para encryptedMessage.

    Para generar el MAC esperado, usa HMAC (RFC 5869) con la función hash SHA256 y el macKey obtenido en el paso 2.

  4. Desencripta encryptedMessage con el modo AES-256-CTR y con lo siguiente:

    • Un IV de cero.
    • Sin relleno.
    • El symmetricEncryptionKey derivado en el paso 2

Administración de claves

Claves de encriptación del comercio

Los comercios generan una clave pública según las especificaciones que se describen en Especificación del esquema de encriptación.

Claves de firma raíz de Google

Google publica el conjunto de claves públicas de firma raíz válidas actualmente que se pueden recuperar desde una URL pública. Las claves son válidas durante el tiempo que indican los encabezados de caché HTTP que devuelve la URL. Se almacenan en caché hasta que vencen, lo que se determina con el campo keyExpiration. Te recomendamos que, cuando venza una recuperación, vuelvas a recuperar las claves de la URL pública para recibir la lista actual de claves válidas.

Excepción para el protocolo ECv2: Si no puedes recuperar las claves de Google en el tiempo de ejecución, recupera el objeto keys.json de nuestra URL de producción, guárdalo en tu sistema y actualízalo periódicamente de forma manual. En circunstancias normales, Google emite una nueva clave de firma raíz para ECv2 cinco años antes de que venza la clave con la fecha de vencimiento más lejana. En caso de que se vulneren las claves, Google notificará a todos los comercios a través de la información de contacto proporcionada en el portal de autoservicio para solicitar una recarga más rápida de keys.json. Para asegurarte de no perderte la rotación periódica, te recomendamos que los comercios que elijan guardar las claves de Google en el contenido de keys.json las actualicen anualmente como parte de su propia rotación anual de claves.

Las claves proporcionadas a través de la URL pública se asignan en el siguiente formato:

{
  "keys": [
    {
      "keyValue": "encoded public key",
      "protocolVersion": "ECv2"
      "keyExpiration":"2000000000000"
    },
    {
      "keyValue": "encoded public key",
      "protocolVersion": "ECv2"
      "keyExpiration":"3000000000000"
    }
  ]
}

El keyValue es una versión de la clave codificada en ASN.1 de tipo SubjectPublicKeyInfo definida en el estándar X.509, sin ajustar ni rellenar, y codificada en base64. En Java, la codificación ASN.1 a la que se hace referencia se representa con la clase X509EncodedKeySpec. Se puede obtener con ECPublicKey.getEncoded().

En los siguientes vínculos, se proporcionan las URLs para los entornos de prueba y producción:

Rotación de claves

Si desencriptas un token de método de pago directamente en tus servidores con la integración directa, debes rotar las claves anualmente.

Para rotar las claves de encriptación, sigue estos pasos:

  1. Usa OpenSSL para generar un par de claves nuevo.
  2. Abre la Consola de Google Pay y la Billetera mientras accedes con la Cuenta de Google se usaba anteriormente para administrar tu app con Google Play.
  3. En la pestaña API de Google Pay, en el panel Integración directa, haz clic en Administrar junto a tu clave pública existente. Haz clic en Agregar otra clave.
  4. Selecciona el campo de entrada de texto Clave de encriptación pública y agrega tu clave pública recién generada con codificación en Base64 en formato de punto sin comprimir.
  5. Haz clic en Guardar claves de encriptación.
  6. Para garantizar una rotación de claves sin problemas, admite el descifrado de las claves privadas nuevas y antiguas mientras realizas la transición de las claves.

    Si usas la biblioteca de Tink para desencriptar el token, usa el siguiente código Java para admitir varias claves privadas:

    String decryptedMessage =
        new PaymentMethodTokenRecipient.Builder()
            .addRecipientPrivateKey(newPrivateKey)
            .addRecipientPrivateKey(oldPrivateKey);

    Asegúrate de que el código de desencriptación se implemente en producción y de supervisar las desencriptaciones exitosas.

  7. Cambia la clave pública que se usa en tu código.

    Reemplaza el valor del atributo publicKey en la propiedad parameters de PaymentMethodTokenizationSpecification:

    /**
     * @param publicKey public key retrieved from your server
     */
    private static JSONObject getTokenizationSpecification(String publicKey) {
      JSONObject tokenizationSpecification = new JSONObject();
      tokenizationSpecification.put("type", "DIRECT");
      tokenizationSpecification.put(
        "parameters",
        new JSONObject()
            .put("protocolVersion", "ECv2")
            .put("publicKey", publicKey));
      return tokenizationSpecification;
    }
  8. Implementa el código del paso 4 en producción. Una vez que se implementa el código, las transacciones de encriptación y desencriptación usan los nuevos pares de claves.
  9. Confirma que la clave pública anterior ya no se usa para encriptar ninguna transacción.

  10. Quita la clave privada anterior.
  11. Abre la consola de Google Pay y la Billetera mientras accedes con la Cuenta de Google que usaste anteriormente para registrarte como desarrollador con Google Pay.
  12. En la pestaña API de Google Pay, en el panel Integración directa, haz clic en Administrar junto a tu clave pública existente. Haz clic en Borrar junto a tu clave pública anterior y, luego, en Guardar claves de encriptación.

Google usa la clave especificada en la propiedad publicKey dentro del objeto parameters PaymentMethodTokenizationSpecification, como se muestra en el siguiente ejemplo:

{
  "protocolVersion": "ECv2",
  "publicKey": "BOdoXP+9Aq473SnGwg3JU1..."
}

Usa la biblioteca de Tink para administrar la respuesta encriptada.

Para realizar la verificación de la firma y el descifrado del mensaje, usa la biblioteca Tink paymentmethodtoken. Esta biblioteca solo está disponible en Java. Para usarla, completa los siguientes pasos:

  1. En tu pom.xml, agrega la app de paymentmethodtoken de Tink como una dependencia:

    <dependencies>
      <!-- other dependencies ... -->
      <dependency>
        <groupId>com.google.crypto.tink</groupId>
        <artifactId>apps-paymentmethodtoken</artifactId>
        <version>1.9.1</version>  <!-- or latest version -->
      </dependency>
    </dependencies>
  2. Al iniciar el servidor, se recuperan previamente las claves de firma de Google para que estén disponibles en la memoria. Esto evita que el usuario vea cualquier latencia de red mientras el proceso de desencriptación recupera las claves.

    GooglePaymentsPublicKeysManager.INSTANCE_PRODUCTION.refreshInBackground();
  3. Descifra el mensaje con el siguiente código, que supone que paymentMethodToken se almacena en la variable encryptedMessage, y reemplaza las secciones en negrita según tu situación.

    Para las pruebas que no son de producción, reemplaza INSTANCE_PRODUCTION por INSTANCE_TEST y, si tu integración está inactiva o no tiene configurada una clave de encriptación, reemplaza [YOUR MERCHANT ID] por .

    • Activa
    • Tiene habilitada la integración DIRECT
    • Tiene configurada una clave de encriptación

    No reemplaces [YOUR MERCHANT ID].

    String decryptedMessage =
        new PaymentMethodTokenRecipient.Builder()
        .fetchSenderVerifyingKeysWith(
            GooglePaymentsPublicKeysManager.INSTANCE_PRODUCTION)
        .recipientId("merchant:[YOUR MERCHANT ID]")
        // This guide applies only to protocolVersion = ECv2
        .protocolVersion("ECv2")
        // Multiple private keys can be added to support graceful
        // key rotations.
        .addRecipientPrivateKey(PrivateKey1)
        .addRecipientPrivateKey(PrivateKey2)
        .build()
        .unseal(encryptedMessage);
  4. Reemplaza PrivateKey1 por el valor de clave privada adecuado que se asocia con el valor de clave pública registrado en Google desde Prepara tus claves y regístrate en Google. Puedes agregar otros valores de clave privada más adelante cuando se te solicite rotar las claves con Google. Las variables pueden ser una cadena PKCS8 codificada en base64 o un objeto ECPrivateKey. Para obtener más información sobre cómo generar una clave privada PKCS8 codificada en Base64, consulta Prepara tus claves y regístrate en Google.

  5. Si no puedes llamar a un servidor de Google cada vez que desencriptas claves, desencripta con el siguiente código y reemplaza las secciones en negrita según tu situación.

    String decryptedMessage =
        new PaymentMethodTokenRecipient.Builder()
        .addSenderVerifyingKey("ECv2 key fetched from test or production url")
        .recipientId("merchant:[YOUR MERCHANT ID]")
        // This guide applies only to protocolVersion = ECv2
        .protocolVersion("ECv2")
        // Multiple private keys can be added to support graceful
        // key rotations.
        .addRecipientPrivateKey(PrivateKey1)
        .addRecipientPrivateKey(PrivateKey2)
        .build()
        .unseal(encryptedMessage);

    La clave actual en el entorno de producción es válida hasta el 14/4/2038 en circunstancias normales, excepto en caso de vulneración de la clave. En caso de que se produzcan vulneraciones de claves, Google notifica a todos los comercios a través de la información de contacto proporcionada en el portal de autoservicio para solicitar una recarga más rápida de keys.json.

    El fragmento de código controla los siguientes detalles de seguridad para que puedas enfocarte en el consumo de la carga útil:

    • Claves de firma de Google recuperadas y almacenadas en caché en la memoria
    • Verificación de la firma
    • Desencriptación