Criptografía de datos de pago para comerciantes

La API Google Pay devuelve los métodos de pago en una carga útil PaymentMethodToken firmada y cifrada. Los métodos de pago devueltos son tarjetas que constan de PAN o tarjetas tokenizadas que constan de PAN del dispositivo y criptogramas.

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

En esta guía se explica cómo generar una clave pública para solicitar un token de método de pago cifrado y firmado por Google, así como los pasos que debes seguir para verificar y descifrar el token.

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

Como recibes directamente información de tarjetas de pago, asegúrate de que tu aplicación cumpla el estándar de seguridad de los datos (DSS) del sector de las tarjetas de pago (PCI) y de que tus servidores tengan la infraestructura necesaria para gestionar 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 la API de Google Pay ECv2 PaymentMethodToken:

  1. Obtén 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 caducado.
  3. Verifica que la clave de firma intermedia de la carga útil no haya caducado.
  4. Verifica que la firma de la carga útil sea válida mediante la clave de firma intermedia.
  5. Descifra el contenido de la carga útil después de verificar la firma.
  6. Comprueba que el mensaje no haya caducado. Para ello, debe comprobar que la hora actual sea inferior al campo messageExpiration del contenido descifrado.
  7. Usa el método de pago de los contenidos descifrados y cóbralo.

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

Estructura del token del método 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 Cadena Identifica el esquema de cifrado o firma con el que se crea el mensaje. Permite que el protocolo evolucione con el tiempo, si es necesario.
signature Cadena Verifica que el mensaje procede de Google. Está codificada en base64 y se crea con ECDSA mediante la clave de firma intermedia.
intermediateSigningKey Objeto 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 Cadena 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 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

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

Nombre Tipo Descripción
signedKey Cadena Mensaje codificado en Base64 que contiene la descripción del pago de la clave.
signatures Cadena Verifica que la clave de firma intermedia procede de Google. Está codificada en base64 y se ha creado 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 Cadena Versión en base64 de la clave codificada en tipo ASN.1. SubjectPublicKeyInfo se define en el estándar X.509.
keyExpiration Cadena Fecha y hora en las que caduca la clave intermedia en milisegundos UTC desde el inicio del registro de tiempo. Los integradores rechazan cualquier clave que haya caducado.

Mensaje firmado

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

Nombre Tipo Descripción
encryptedMessage Cadena Un mensaje cifrado codificado en base64 que contiene información de pago y algunos campos de seguridad adicionales.
ephemeralPublicKey Cadena Clave pública efímera codificada en base64 asociada a la clave privada para cifrar el mensaje en formato de punto sin comprimir. Para obtener más información, consulta el artículo sobre el formato de clave pública de cifrado.
tag Cadena Un MAC codificado en base64 de encryptedMessage.

Mensaje cifrado

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

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

Nombre Tipo Descripción
messageExpiration Cadena Fecha y hora en las que caduca el mensaje en milisegundos UTC desde el inicio del registro de tiempo. Los integradores deben rechazar cualquier mensaje que haya caducado.
messageId Cadena Un ID único que identifica el mensaje por si es necesario revocarlo o localizarlo más adelante.
paymentMethod Cadena El tipo de credencial de pago. Actualmente, solo se admite CARD.
paymentMethodDetails Objeto La propia credencial de pago. El formato de este objeto se determina mediante el elemento paymentMethod y se describe en las siguientes tablas.

Tarjeta

Las siguientes propiedades componen una credencial de pago para el método de pago CARD:

Nombre Tipo Descripción
pan Cadena El número de cuenta personal a la que se le ha aplicado el cargo. Esta cadena solo contiene dígitos.
expirationMonth Número El mes de vencimiento de la tarjeta, donde 1 representa enero, 2 representa febrero y así sucesivamente.
expirationYear Número El año de vencimiento de la tarjeta, expresado como un número de cuatro dígitos (por ejemplo, 2020).
authMethod Cadena Método de autenticación de la transacción con tarjeta.

PAN_ONLY

El siguiente fragmento de JSON es un ejemplo del encryptedMessage completo de 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

Una CARD autenticada con un criptograma 3-D Secure. CRYPTOGRAM_3DS authMethod. Incluye los siguientes campos adicionales:

Nombre Tipo Descripción
cryptogram Cadena Un criptograma 3-D Secure.
eciIndicator Cadena Esta cadena no siempre está presente. Solo se devuelve en las transacciones con tokens de dispositivo autenticados en Android (CRYPTOGRAM_3DS). Este valor debe transferirse a lo largo del flujo de procesamiento de pagos.

El siguiente fragmento de JSON es un ejemplo del encryptedMessage completo de 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"
    
  },
  
  "messageId": "some-message-id",
  "messageExpiration": "1759309000000"
}

eciIndicator

La red de la tarjeta puede proporcionar el eciIndicator para las transacciones de tokens de dispositivo autenticados (CRYPTOGRAM_3DS).

Debe transferir el valor eciIndicator en la transacción de autorización sin que se modifique 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 o adquirente CRYPTOGRAM_3DS
02 Mastercard Entidad emisora de la tarjeta CRYPTOGRAM_3DS
06 Mastercard Comerciante o adquirente CRYPTOGRAM_3DS
05 Visa Entidad emisora de la tarjeta CRYPTOGRAM_3DS
07 Visa Comerciante o adquirente CRYPTOGRAM_3DS
""(empty) Otras cadenas Comerciante o adquirente CRYPTOGRAM_3DS

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

Verificación de firmas

Para verificar las firmas, incluidas las firmas de la clave intermedia y del mensaje, se necesitan los siguientes elementos:

  • Algoritmo usado para crear la firma
  • La cadena de bytes usada para crear la firma
  • La clave pública que corresponde a la privada utilizada para crear la firma
  • La propia firma

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, tal 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 generado por las implementaciones de ECDSA de la extensión de criptografía de Java (JCE).

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

Cómo crear la cadena de bytes de la firma de la clave de firma intermedia

Para validar la firma de la clave de firma intermedia en el token de método de pago de muestra, crea 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 estar codificado en UTF-8. El valor de 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 ECDSA estándar se usa cuando se ensambla la cadena firmada de la firma de la clave de firma intermedia. En el protocolo ECv2, debes iterar sobre todas las firmas de intermediateSigningKey.signatures e intentar validar cada una con las claves de firma de Google no caducadas de keys.json. Si funciona al menos una validación de firma, considera que la verificación se ha completado. Usa el intermediateSigningKey.signedKey.keyValue más adelante para verificar el signedStringForMessageSignature. Google te recomienda que uses una biblioteca criptográfica en lugar de tu propio código de verificación.

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

Para validar la firma del token de método de pago de ejemplo, crea 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 y 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 sustituyas \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. El merchantId coincide con el valor que se encuentra en la Consola de Google Pay y Wallet para los comerciantes con acceso de producción.

Si sender_id es Google y recipient_id es merchant:12345, signedString aparece como 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 ECDSA estándar se usa cuando se ensambla la cadena firmada. El intermediateSigningKey.signedKey.keyValue verificado en el paso anterior se usa para verificar el signedMessage. Google te recomienda que uses una biblioteca criptográfica en lugar de tu propio código de verificación.

Especificación del esquema de cifrado

Google usa el esquema de cifrado integrado de curva elíptica (ECIES) para proteger el token del método de pago devuelto en la respuesta de la API de Google Pay. El esquema de cifrado usa los siguientes parámetros:

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

ECIES-KEM, tal 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 los puntos no está comprimido.
Función de derivación de claves

Basado en HMAC con SHA-256 (HKDFwithSHA256).

  • No se debe proporcionar el salt.
  • 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 cifrado simétrico

DEM2, tal como se define en la norma ISO 18033-2

Algoritmo de cifrado: AES-256-CTR con IV cero y sin relleno.

Algoritmo 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 cifrado

La clave pública de cifrado y el ephemeralPublicKey devueltos 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 dos elementos siguientes:

  • 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 de 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.

Usar 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 usarla con NIST P-256 y se escribe en key.pem:

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

Opcional: Ver las claves privada y pública

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

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

El comando genera 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: Generar una clave pública codificada en base64

La clave privada y la pública que se generan en el ejemplo del paso opcional anterior están codificadas 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 genera un archivo publicKey.txt cuyo contenido, la versión en base64 de la clave en formato de punto sin comprimir, es similar 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 Tink espera que tu clave privada esté codificada en base64 con el 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 genera un resultado similar al siguiente:

MIGHAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBG0wawIBAQQgWV4oK8c/MZkCLk4qSCNjW0Zm6H0CBCtSYxkXkC9FBHehRANCAAQPldOnhO2/oXjdJD1dwlFPiNs6fcdoRgFu3/Z0iKj24SjTGyLRGAtYWLGXBZcDdPj3T2bJRHRVhE8Bc2AjkT7n

Cómo descifrar el token del método 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 que utilice 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, tal como se describe en RFC 5869, con el siguiente parámetro:
      • No se debe proporcionar el salt. Según el RFC, debe ser equivalente a una sal de 32 bytes con valor cero.
  2. Divide la clave generada en dos claves de 256 bits: 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. Descifra encryptedMessage con el modo AES-256-CTR y con lo siguiente:

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

Gestión de claves

Claves de cifrado de comerciantes

Los comerciantes generan una clave pública según las especificaciones descritas en Especificación del esquema de cifrado.

Claves de firma raíz de Google

.

Google publica el conjunto de claves públicas de firma raíz válidas que se pueden obtener de una URL pública. Las claves son válidas mientras lo indiquen los encabezados de caché HTTP devueltos por la URL. Se almacenan en caché hasta que caducan, lo que se determina mediante el campo keyExpiration. Te recomendamos que, cuando caduque una obtención, vuelvas a obtener 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 obtener las claves de Google en tiempo de ejecución, obtén el 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 vean comprometidas las claves, Google notificará a todos los comerciantes 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 asegurarse de que no se pierde la rotación periódica, recomendamos que los comerciantes que decidan guardar las claves de Google en el contenido de keys.json las renueven anualmente como parte de su propia rotación anual de claves.

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

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

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

En los siguientes enlaces se proporcionan las URLs de los entornos de prueba y de producción:

Rotación de claves

Si descifras un token de método de pago directamente en tus servidores con una integración directa, debes cambiar las claves cada año.

Sigue estos pasos para rotar las claves de cifrado:

  1. Usa OpenSSL para generar un nuevo par de claves.
  2. Abre la Consola de Google Pay y Wallet con la cuenta de Google que usaste anteriormente para registrarte como desarrollador de Google Pay.
  3. En la pestaña API Google Pay, en el panel Integración directa, haz clic en Gestionar junto a tu clave pública. Haz clic en Añadir otra clave.
  4. Selecciona el campo de entrada de texto Clave de cifrado pública y añade la clave pública que acabas de generar codificada en base64 en formato de punto sin comprimir.
  5. Haz clic en Guardar claves de cifrado.
  6. Para que la rotación de claves se realice sin problemas, admite el descifrado con las claves privadas nuevas y antiguas mientras cambias las claves.

    Si usas la biblioteca Tink para descifrar el token, utiliza 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 descifrado se haya desplegado en producción y de que monitorizas los descifrados correctos.

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

    Sustituye el valor del atributo publicKey en la propiedad PaymentMethodTokenizationSpecification parameters:

    const tokenizationSpecification = {
      "type": "DIRECT",
      "parameters": {
        "protocolVersion": "ECv2",
        "publicKey": "BOdoXP1aiNp.....kh3JUhiSZKHYF2Y="
      }
    }
  8. Despliega el código del paso 4 en producción. Una vez que se haya implementado el código, las transacciones de cifrado y descifrado usarán los nuevos pares de claves.
  9. Confirma que la clave pública antigua ya no se usa para cifrar ninguna transacción.

  10. Elimina la clave privada antigua.
  11. Abre la Consola de Google Pay y Wallet con la cuenta de Google que usaste anteriormente para registrarte como desarrollador de Google Pay.
  12. En la pestaña API de Google Pay, en el panel Integración directa, haz clic en Gestionar junto a tu clave pública. Haz clic en Eliminar junto a tu clave pública antigua y, a continuación, en Guardar claves de cifrado.

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

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

Usa la biblioteca Tink para gestionar la respuesta cifrada.

Para verificar la firma y descifrar el mensaje, usa la biblioteca paymentmethodtoken de Tink. Esta biblioteca solo está disponible en Java. Para usarla, sigue estos pasos:

  1. En tu pom.xml, añade la aplicación paymentmethodtoken de Tink como 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, prefetch las claves de firma de Google para que estén disponibles en la memoria. De esta forma, se evita que el usuario vea la latencia de la red mientras el proceso de descifrado obtiene las claves.

    GooglePaymentsPublicKeysManager.INSTANCE_PRODUCTION.refreshInBackground();
  3. Descifra el mensaje con el siguiente código, que da por hecho que paymentMethodToken está almacenado en la variable encryptedMessage. Sustituye las secciones en negrita según tu situación.

    En el caso de las pruebas que no sean de producción, sustituye INSTANCE_PRODUCTION por INSTANCE_TEST. Si tu integración está inactiva o no tiene configurada una clave de cifrado, sustituye [YOUR MERCHANT ID] por 12345678901234567890.

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

    No sustituyas [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. Sustituye PrivateKey1 por el valor de clave privada adecuado que se asocie con el valor de clave pública registrado en Google en Prepara tus claves y regístrate en Google. Puede añadir otros valores de clave privada más adelante cuando se le pida que rote 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 descifres claves, descifra con el siguiente código y sustituye 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 del entorno de producción es válida hasta el 14/04/2038 en circunstancias normales, excepto en caso de que la clave se vea comprometida. En caso de que se produzcan vulneraciones de claves, Google se pondrá en contacto con todos los comerciantes 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 gestiona los siguientes detalles de seguridad para que puedas centrarte en el consumo de la carga útil:

    • Claves de firma de Google obtenidas y almacenadas en caché en la memoria
    • Verificación de firmas
    • Descifrado