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:
- Recupera las claves de firma raíz de Google.
- 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.
- Verifica que la clave de firma intermedia de la carga útil no haya vencido.
- Verifica que la firma de la carga útil sea válida con la clave de firma intermedia.
- Desencripta el contenido de la carga útil después de verificar la firma.
- Verifica que el mensaje no haya vencido. Para ello, debes verificar que la hora actual sea anterior al campo
messageExpirationen el contenido descifrado. - 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.
|
| Función de derivación de claves | Basado en HMAC con SHA-256 (
|
| 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:
- Usa tu clave privada y el
ephemeralPublicKeyproporcionado 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,SingleHashModeyCofactorModeson0.- 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.
- Divide la clave generada en dos claves de 256 bits de longitud:
symmetricEncryptionKeyymacKey. Verifica que el campo
tagsea una dirección MAC válida paraencryptedMessage.Para generar el MAC esperado, usa HMAC (RFC 5869) con la función hash SHA256 y el
macKeyobtenido en el paso 2.Desencripta
encryptedMessagecon el modo AES-256-CTR y con lo siguiente:- Un IV de cero.
- Sin relleno.
- El
symmetricEncryptionKeyderivado 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:
- Prueba:
https://payments.developers.google.com/paymentmethodtoken/test/keys.json - Producción:
https://payments.developers.google.com/paymentmethodtoken/keys.json
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:
- Usa OpenSSL para generar un par de claves nuevo.
- 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.
- 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.
- 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.
- Haz clic en Guardar claves de encriptación.
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.
Cambia la clave pública que se usa en tu código.
Reemplaza el valor del atributo
publicKeyen la propiedadparametersdePaymentMethodTokenizationSpecification:/** * @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; }
- 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.
Confirma que la clave pública anterior ya no se usa para encriptar ninguna transacción.
- Quita la clave privada anterior.
- 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.
- 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:
En tu
pom.xml, agrega la app depaymentmethodtokende 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>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();
Descifra el mensaje con el siguiente código, que supone que
paymentMethodTokense almacena en la variableencryptedMessage, y reemplaza las secciones en negrita según tu situación.Para las pruebas que no son de producción, reemplaza
INSTANCE_PRODUCTIONporINSTANCE_TESTy, 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);
Reemplaza
PrivateKey1por 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 objetoECPrivateKey. 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.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