Criptografía de datos de pagos para comercios

La API de Google Pay devuelve las formas de pago en una carga útil de PaymentMethodToken firmada y encriptada. Las formas de pago que se devuelven son tarjetas que constan de un PAN o tarjetas asignadas con token 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 usan y el formato esperado.

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

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

Como recibes información de tarjetas de pago directamente, asegúrate de que tu app cumpla con el estándar PCI DSS y de que tus servidores tengan la infraestructura necesaria para administrar 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 de 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 a la del campo messageExpiration en el contenido desencriptado.
  7. Usa la forma de pago que se encuentra en el contenido desencriptado y realiza el cobro.

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

Estructura del token de 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 que se especifican en la siguiente tabla:

Nombre Tipo Descripción
protocolVersion String Identifica el esquema de encriptación o firma 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 creó 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 elemento 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 Es un 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 firmas.

Ejemplo

A continuación, se muestra una respuesta de token de forma 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 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 Es la fecha y hora en que vence la clave intermedia, 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 Una clave pública efímera codificada en Base64 asociada con 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 una MAC codificada en Base64 de encryptedMessage.

Mensaje encriptado

El encryptedMessage desencriptado es un objeto JSON serializado y codificado en UTF-8. El JSON contiene dos niveles. El nivel exterior contiene metadatos y campos incluidos por seguridad, mientras que el nivel interior 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 Es la fecha y hora en 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 La credencial de pago en sí El formato de este objeto se determina por el paymentMethod y se describe en las siguientes tablas.

Tarjeta

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 cuenta personal al que se le cobró. Esta cadena solo contiene dígitos.
expirationMonth Número Es el mes de vencimiento de la tarjeta, en el que 1 representa enero, 2 representa febrero, etcétera.
expirationYear Número Es el 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.
merchantTokenId String

Es el ID del token de comercio de la transacción con tarjeta. Este campo opcional solo se devuelve si se cumplen todas las condiciones que se indican a continuación:

  • El comercio especifica un objeto MIT *TransactionInfo en la solicitud.
  • El comercio incluye un tokenUpdateUrl en la solicitud.
  • El usuario selecciona un instrumento con asignación de token (p.ej., DPAN).

PAN_ONLY

El siguiente fragmento de 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 Un criptograma de 3-D Secure.
eciIndicator String Esta cadena no siempre está presente. Solo se devuelve para las transacciones de tokens de dispositivos autenticados en Android (CRYPTOGRAM_3DS). Este valor se debe pasar por el flujo de procesamiento de pagos.

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

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

eciIndicator

La red de tarjetas puede proporcionar el eciIndicator para las transacciones de tokens de dispositivos autenticados (CRYPTOGRAM_3DS).

Debes pasar el valor eciIndicator en la transacción de autorización sin que se altere ni se codifique de forma fija; 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 Comercio/adquirente CRYPTOGRAM_3DS
02 Mastercard Entidad emisora de la tarjeta CRYPTOGRAM_3DS
06 Mastercard Comercio/adquirente CRYPTOGRAM_3DS
05 Visa Entidad emisora de la tarjeta CRYPTOGRAM_3DS
07 Visa Comercio/adquirente CRYPTOGRAM_3DS
""(empty) Otras redes Comercio/adquirente CRYPTOGRAM_3DS

No se devolverán otros valores de ECI para VISA y Mastercard que no estén presentes en esta tabla.

Verificación de firmas

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.
  • 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 la función hash, como se define en FIPS 186-4.

La firma

La firma se incluye en el nivel más externo del mensaje. Está codificada 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 de 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 la extensión de criptografía de Java (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 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 y signedKey) debe tener codificación 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 forma 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 el sender_id es Google, el 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. Para el protocolo ECv2, debes iterar todas las firmas en intermediateSigningKey.signatures y tratar de validar cada una con las claves de firma de Google que no hayan vencido en keys.json. Si funciona al menos una validación de firma, considera que la verificación se completó. Usa el intermediateSigningKey.signedKey.keyValue más adelante para verificar el signedStringForMessageSignature. Google recomienda 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 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 forma 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. El 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 el sender_id es Google y el recipient_id es merchant:12345, el 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

El algoritmo de verificación de ECDSA estándar se usa cuando se ensambla la cadena firmada. La intermediateSigningKey.signedKey.keyValue verificada en el paso anterior se usa para verificar la signedMessage. Google recomienda 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 muestra 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, como 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 la sal.
  • La información debe ser "Google", codificada 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, como 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, como se deriva de la función de derivación de claves.

Formato de la clave pública de encriptación

La clave pública de encriptación y el ephemeralPublicKey que se devuelven en las cargas útiles de Google tienen el formato de la representación en 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 adecuada 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 públicas y privadas

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 privada y pública que se genera en el paso opcional anterior está codificada en formato 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 verificar que sea así, 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 desencriptar el token:

  1. Usa tu clave privada y el ephemeralPublicKey proporcionado para derivar una clave compartida de 512 bits 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 la 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 un MAC válido 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 VI 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 descritas 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 indiquen los encabezados de caché HTTP que muestra 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 durante el tiempo de ejecución, recupera la keys.json de nuestra URL de producción, guárdala en tu sistema y actualízala de forma manual periódicamente. 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 larga. En caso de que se vulneren las 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. Para asegurarte de no perderte la rotación habitual, 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"
    }
  ]
}

La keyValue es una versión en base64, no unida ni rellenada, de la clave codificada en el tipo ASN.1 SubjectPublicKeyInfo definido en el estándar X.509. En Java, la codificación ASN.1 mencionada se representa con la clase X509EncodedKeySpec. Se puede obtener con ECPublicKey.getEncoded().

Las URLs de los entornos de prueba y de producción se proporcionan en los siguientes vínculos:

Rotación de claves

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

Sigue estos pasos para rotar las claves de encriptación:

  1. Usa OpenSSL para generar un par de claves nuevo.
  2. Abre la Consola de Google Pay y la Billetera de Google con la Cuenta de Google que usabas 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 la clave pública que acabas de generar, codificada 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 la desencriptación 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 para la desencriptación se implemente en producción y de que supervises 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 pares de claves nuevos.
  9. Confirma que la clave pública anterior ya no se use para encriptar ninguna transacción.

  10. Quita la clave privada anterior.
  11. Abre la Consola de Google Pay y la Billetera de Google con la Cuenta de Google que usaste anteriormente para registrarte como desarrollador en 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 PaymentMethodTokenizationSpecification parameters, 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 firmas y la desencriptación de mensajes, usa la biblioteca paymentmethodtoken de Tink. Esta biblioteca solo está disponible en Java. Para usarla, completa los siguientes pasos:

  1. En tu pom.xml, agrega la app de Tink paymentmethodtoken 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. Cuando se inicie el servidor, recupera 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. Desencripta el mensaje con el siguiente código, que supone que paymentMethodToken se almacena en la variable encryptedMessage, y reemplaza las secciones en negrita de acuerdo con 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 una clave de encriptación configurada, reemplaza [YOUR MERCHANT ID] por .

    • Activo
    • Tiene habilitada la integración DIRECTA
    • Tiene una clave de encriptación configurada

    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 con Google de Prepara tus claves y regístrate en Google. Puedes agregar varios 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 producir 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 claves. 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.

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

    • Se recuperaron las claves de firma de Google y se almacenaron en caché en la memoria
    • Verificación de firmas
    • Desencriptación