A API Google Pay retorna as formas de pagamento em um payload PaymentMethodToken
assinado e criptografado. As informações retornadas são cartões com PAN ou tokenizados que têm criptogramas e PANs de dispositivos.
O payload contém um campo chamado protocolVersion
que informa ao destinatário do payload quais primitivos criptográficos estão em uso e o formato esperado.
Neste guia, apresentamos informações sobre como gerar uma chave pública para solicitar um token de forma de pagamento assinado e criptografado pelo Google, além de detalharmos as etapas a serem seguidas para verificar e descriptografar o token.
Este guia destina-se apenas a protocolVersion = ECv2
.
Como você receberá as informações do cartão de pagamento diretamente, verifique se o app está em conformidade com o padrão de segurança de dados PCI DSS e se os servidores têm a infraestrutura necessária para o gerenciamento seguro das credenciais de pagamento do usuário antes de prosseguir.
Veja nas etapas a seguir o que um integrador precisa fazer para usar o payload
ECv2
PaymentMethodToken
da API Google Pay:
- Buscar as chaves de assinatura raiz do Google.
- Verificar se a assinatura da chave de assinatura intermediária é válida de acordo com qualquer uma das chaves de assinatura raiz não expiradas.
- Verificar se a chave de assinatura intermediária do payload não expirou.
- Verificar se a assinatura do payload é válida de acordo com a chave de assinatura intermediária.
- Descriptografar o conteúdo do payload depois de verificar a assinatura.
- Verificar se a mensagem não expirou. Para isso, verifique se a hora atual é anterior à do campo
messageExpiration
no conteúdo descriptografado. - Usar a forma de pagamento no conteúdo descriptografado e envie a cobrança para ela.
O exemplo de código da biblioteca Tink realiza as etapas de 1 a 6.
Estrutura de token da forma de pagamento
A mensagem retornada pelo Google na resposta PaymentData
é um objeto
JSON serializado e codificado em UTF-8 com as chaves especificadas na tabela a seguir:
Nome | Tipo | Descrição |
---|---|---|
protocolVersion |
String | Identifica o esquema de criptografia/assinatura em que a mensagem foi criada. Permite que o protocolo seja aprimorado ao longo de tempo, se necessário. |
signature |
String | Verifica se a origem da mensagem é o Google. É codificada em Base64 e criada com ECDSA pela chave de assinatura intermediária. |
intermediateSigningKey |
Objeto | Um objeto JSON que inclui a chave de assinatura intermediária do Google. Ele contém signedKey com keyValue , keyExpiration e signatures . É serializado para simplificar o processo de verificação de assinaturas
da chave de assinatura intermediária. |
signedMessage |
String | Um objeto JSON serializado como uma string de HTML protegido que contém
encryptedMessage , ephemeralPublicKey e tag . É
serializado para simplificar o processo de verificação de assinaturas. |
Exemplo
Veja a seguir uma resposta do token da forma de pagamento no 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\"}" }
Chave de assinatura intermediária
intermediateSigningKey
é um objeto JSON serializado codificado em UTF-8 que contém
os seguintes valores:
Nome | Tipo | Descrição |
---|---|---|
signedKey |
String | Uma mensagem codificada em Base64 com a descrição de pagamento da chave. |
signatures |
String | Verifica se a origem da chave de assinatura intermediária é o Google. É codificada em Base64 e criada usando o ECDSA. |
Chave assinada
signedKey
é um objeto JSON serializado codificado em UTF-8 que contém os
seguintes valores:
Nome | Tipo | Descrição |
---|---|---|
keyValue |
String | Uma versão em Base64 da chave codificada no tipo ASN.1. SubjectPublicKeyInfo é
definida no padrão X.509. |
keyExpiration |
String | Data e hora em que a chave intermediária expira no fuso horário UTC. O tempo é mostrado em milissegundos desde o início da era Unix. Os integradores rejeitam qualquer chave expirada. |
Mensagem assinada
signedMessage
é um objeto JSON serializado codificado em UTF-8 que contém os
seguintes valores:
Nome | Tipo | Descrição |
---|---|---|
encryptedMessage |
String | Uma mensagem criptografada e codificada em Base64 que contém informações de pagamento e outros campos de segurança. |
ephemeralPublicKey |
String | Uma chave pública temporária e codificada em Base64 que está associada à chave privada para criptografar a mensagem no formato de ponto descompactado. Para mais informações, consulte Formato de chave pública de criptografia. |
tag |
String | Um MAC codificado em Base64 de encryptedMessage . |
Mensagem criptografada
A encryptedMessage
descriptografada é um objeto JSON serializado e codificado em UTF-8. O JSON tem dois níveis. O nível externo contém metadados e campos incluídos para segurança, enquanto o interno é outro objeto JSON que representa a credencial de pagamento real.
Para mais detalhes sobre encryptedMessage
, consulte as tabelas e
os exemplos de objeto JSON a seguir:
Nome | Tipo | Descrição |
---|---|---|
gatewayMerchantId |
String | Um identificador exclusivo do comerciante que o processador entende e usa para verificar se
a mensagem foi enviada ao comerciante que a solicitou. Criado pelo processador e transmitido ao Google por um comerciante em |
messageExpiration |
String | Data e hora em que a mensagem expira no fuso horário UTC, em milissegundos desde o início da era Unix. Os integradores precisam recusar todas as mensagens expiradas. |
messageId |
String | Um ID exclusivo que identificará a mensagem caso ela precise ser revogada ou localizada posteriormente. |
paymentMethod |
String | O tipo da credencial de pagamento.
Atualmente, apenas CARD é aceito.
|
paymentMethodDetails |
Objeto | A própria credencial de pagamento. O formato desse objeto é determinado por
paymentMethod e é descrito nas tabelas a seguir. |
Cartão
As propriedades a seguir compõem uma credencial de pagamento para a forma de pagamento CARD
:
Nome | Tipo | Descrição |
---|---|---|
pan |
String | O número da conta pessoal cobrada. Essa string contém apenas dígitos. |
expirationMonth |
Número | O mês de expiração do cartão, em que 1 representa janeiro, 2 refere-se a fevereiro e assim por diante. |
expirationYear |
Número | O ano de expiração do cartão com quatro dígitos, como 2020. |
authMethod |
String | O método de autenticação da transação do cartão. |
assuranceDetails |
AssuranceDetailsSpecifications (em inglês) | Esse objeto apresenta informações sobre a validação realizada na credencial de pagamento retornada. |
PAN_ONLY
O seguinte snippet JSON é um exemplo da encryptedMessage
completa para
CARD
paymentMethod
com um PAN_ONLY
authMethod
.
"paymentMethod": "CARD", "paymentMethodDetails": { "authMethod": "PAN_ONLY", "pan": "1111222233334444", "expirationMonth": 10, "expirationYear": 2020 }, "gatewayMerchantId": "some-merchant-id", "messageId": "some-message-id", "messageExpiration": "1577862000000" }
CRYPTOGRAM_3DS
Um CARD
autenticado com o uso de um criptograma 3-D Secure
(CRYPTOGRAM_3DS
authMethod
). Ele inclui os outros campos
a seguir:
Nome | Tipo | Descrição |
---|---|---|
cryptogram |
String | Um criptograma 3-D Secure. |
eciIndicator |
String | Nem sempre presente. É retornado apenas para tokens das redes Visa. Esse valor é transmitido na solicitação de autorização de pagamento. |
O seguinte snippet JSON é um exemplo da encryptedMessage
completa para
CARD
paymentMethod
com um
CRYPTOGRAM_3DS
authMethod
:
{ "paymentMethod": "CARD", "paymentMethodDetails": { "authMethod": "CRYPTOGRAM_3DS", "pan": "1111222233334444", "expirationMonth": 10, "expirationYear": 2020, "cryptogram": "AAAAAA...", "eciIndicator": "eci indicator" }, "messageId": "some-message-id", "messageExpiration": "1577862000000" }
Verificação de assinatura
Os itens a seguir são necessários para verificar assinaturas, que incluem a chave intermediária e as assinaturas de mensagens:
- O algoritmo usado para criar a assinatura
- A string de bytes usada para criar a assinatura
- A chave pública que corresponde à chave privada usada para criar a assinatura
- A própria assinatura
Algoritmo de assinatura
O Google usa o algoritmo de assinatura digital de curva elíptica ECDSA (link em inglês), para assinar as mensagens com os seguintes parâmetros: ECDSA na NIST P-256 com SHA-256 como função hash, conforme definido na FIPS 186-4.
Assinatura
A assinatura é incluída no nível mais externo da mensagem. Ela é codificada em Base64 no formato de bytes ASN.1. Para mais informações sobre o ASN.1, consulte o apêndice A das ferramentas IETF (em inglês). A assinatura consiste nos números inteiros "r" e "s" do ECDSA. Para mais informações, consulte Algoritmo de geração de assinaturas.
Veja a seguir um exemplo do formato de byte ASN.1 especificado, que é o formato padrão produzido pelas implementações do ECDSA da extensão de criptografia Java (JCE, na sigla em inglês).
ECDSA-Sig-Value :: = SEQUENCE { r INTEGER, s INTEGER }
Como criar a string de bytes para assinatura da chave de assinatura intermediária
Para validar a assinatura da chave de assinatura intermediária no token de forma de pagamento de amostra, crie
a signedStringForIntermediateSigningKeySignature
usando a fórmula a seguir:
signedStringForIntermediateSigningKeySignature = length_of_sender_id || sender_id || length_of_protocol_version || protocol_version || length_of_signed_key || signed_key
A notação "||" significa "concatenar". Cada componente (sender_id
, protocolVersion
, signedKey
) precisa ser codificado em UTF-8. O signedKey
precisa ser a sequência de intermediateSigningKey.signedKey
.
O comprimento de cada componente é de 4 bytes no formato little-endian.
Exemplo
Neste exemplo, usamos o seguinte token de forma de pagamento de amostra:
{ "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\"}" }
O sender_id
é sempre Google
, e o protocol_version
é
ECv2
.
Se sender_id
for Google
, o signedString
aparecerá como
mostrado no exemplo a seguir:
signedStringForIntermediateSigningKeySignature = \x06\x00\x00\x00 || Google || | \x04\x00\x00\x00 || ECv2 || \xb5\x00\x00\x00 || {"keyExpiration":"1542323393147","keyValue":"MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAE/1+3HBVSbdv+j7NaArdgMyoSAM43yRydzqdg1TxodSzA96Dj4Mc1EiKroxxunavVIvdxGnJeFViTzFvzFRxyCw\u003d\u003d"}
Como verificar a assinatura em signedStringForIntermediateSigningKeySignature
O algoritmo de verificação padrão do ECDSA é usado quando a string assinada da chave de assinatura intermediária é criada. Para o protocolo ECv2, é necessário iterar todas as assinaturas em intermediateSigningKey.signatures
e tentar validar cada uma com as chaves de assinatura não expiradas do Google em keys.json
. Se pelo menos uma validação de assinatura funcionar, a verificação estará concluída. Posteriormente, use
intermediateSigningKey.signedKey.keyValue
para verificar a
signedStringForMessageSignature
. O Google recomenda o uso de uma biblioteca criptográfica
existente em vez do seu próprio código de verificação.
Como criar a string de bytes da assinatura da mensagem
Para validar a assinatura do token da forma de pagamento de amostra, crie signedStringForMessageSignature
com a seguinte fórmula:
signedStringForMessageSignature = length_of_sender_id || sender_id || length_of_recipient_id || recipient_id || length_of_protocolVersion || protocolVersion || length_of_signedMessage || signedMessage
A notação "||" significa "concatenar". Cada componente (sender_id
, recipient_id
, protocolVersion
e signedMessage
) precisa ser codificado em UTF-8. O comprimento de cada componente é de 4 bytes no formato little-endian. Ao criar a string de bytes, não analise nem modifique signedMessage
. Por exemplo, não substitua \u003d
pelo caractere =
.
Exemplo
O exemplo a seguir é um token de forma de pagamento de amostra:
{ "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\"}" }
O sender_id
é sempre Google
, e o recipient_id
é
merchant:merchantId
. O merchantId
corresponde ao valor encontrado no
Business Console
do Google Pay para comerciantes com acesso de produção.
Se sender_id
for Google
e recipient_id
for merchant:12345
, a signedString
aparecerá como no exemplo a
seguir:
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"}
Como verificar a assinatura em signedStringForMessageSignature
O algoritmo de verificação padrão do ECDSA é usado quando a string assinada é criada. O
intermediateSigningKey.signedKey.keyValue
verificado na etapa anterior é usado para
verificar signedMessage
. O Google recomenda o uso de uma biblioteca criptográfica
existente em vez do seu próprio código de verificação.
Especificação do esquema de criptografia
O Google usa o esquema de criptografia integrada de curva elíptica ECIES (link em inglês) para proteger o token da forma de pagamento retornado na resposta da API Google Pay. O esquema de criptografia usa os seguintes parâmetros:
Parâmetro | Definição |
---|---|
Método de encapsulamento de chaves | ECIES-KEM, conforme definido no padrão ISO 18033-2 (em inglês).
|
Função de derivação de chaves | Baseado em HMAC com SHA-256 (
|
Algoritmo de criptografia simétrica |
DEM2, conforme definido na norma ISO 18033-2 (em inglês). Algoritmo de criptografia: AES-256-CTR com zero IV e sem preenchimento. |
Algoritmo MAC | HMAC_SHA256 usando uma chave de 256 bits como derivada da função de derivação de chaves. |
Formato da chave pública de criptografia
A chave pública de criptografia e a ephemeralPublicKey
retornada nos payloads do Google são
formatadas com a representação Base64 da chave no formato de ponto descompactado. Ela é composta
pelos dois elementos a seguir:
- Um número mágico que especifica o formato (0x04).
- Dois números inteiros de 32 bytes e alto valor que representam as coordenadas X e Y na curva elíptica.
Esse formato é descrito em mais detalhes na publicação "Public Key Cryptography For The Financial Services Industry: The Elliptic Curve Digital Signature Algorithm (ECDSA)" ("Criptografia de chave pública para o setor de serviços financeiros: algoritmo de assinatura digital de curva elíptica", em tradução livre), ANSI X9.62, 1998.
Usar o OpenSSL para gerar uma chave pública
Etapa 1: gerar uma chave privada
No exemplo a seguir, uma chave privada de curva elíptica adequada para uso com o NIST P-256 é gerada e gravada em key.pem
:
openssl ecparam -name prime256v1 -genkey -noout -out key.pem
Opcional: visualizar as chaves privada e pública
Para visualizar as chaves privada e pública, use o seguinte comando:
openssl ec -in key.pem -pubout -text -noout
A resposta ao comando será parecida com esta:
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
Etapa 2: gerar uma chave pública codificada em Base64
As chaves privada e pública geradas no exemplo da etapa opcional acima são codificadas no formato hexadecimal. Para gerar uma chave pública codificada em Base64 no formato de ponto descompactado, use o 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
O comando produz um arquivo publicKey.txt
que tem como conteúdo a versão Base64 da chave
no formato de ponto descompactado e é semelhante ao seguinte:
BOdoXP+9Aq473SnGwg3JU1aiNpsd9vH2ognq4PtDtlLGa3Kj8TPf+jaQNPyDSkh3JUhiS0KyrrlWhAgNZKHYF2Y=
O conteúdo do arquivo não pode ter espaços vazios ou retornos de carro extras. Para verificar isso, execute o seguinte comando no Linux ou no MacOS:
od -bc publicKey.txt
Etapa 3: gerar uma chave privada codificada em Base64 no formato PKCS #8
A biblioteca do Tink espera que sua chave privada seja codificada em Base64 no formato PKCS #8. Use o seguinte comando para gerar a chave privada nesse formato a partir daquela criada na primeira etapa:
openssl pkcs8 -topk8 -inform PEM -outform DER -in key.pem -nocrypt | base64 | paste -sd "\0" -
A resposta ao comando será parecida com esta:
MIGHAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBG0wawIBAQQgWV4oK8c/MZkCLk4qSCNjW0Zm6H0CBCtSYxkXkC9FBHehRANCAAQPldOnhO2/oXjdJD1dwlFPiNs6fcdoRgFu3/Z0iKj24SjTGyLRGAtYWLGXBZcDdPj3T2bJRHRVhE8Bc2AjkT7n
Como descriptografar o token da forma de pagamento
Siga estas instruções para descriptografar o token:
- Use a chave privada e a
ephemeralPublicKey
para derivar uma chave compartilhada de 512 bits que use o ECIES-KEM (em inglês). Para isso, utilize os parâmetros a seguir: - Curva elíptica: NIST P-256, também conhecida no OpenSSL como prime256v1.
CheckMode
,OldCofactorMode
,SingleHashMode
eCofactorMode
são0
.- Função de codificação: formato de ponto descompactado.
- Função de derivação da chave: HKDFwithSHA256, conforme descrito na RFC 5869 (em inglês), com o parâmetro a seguir:
- Não forneça o sal. De acordo com o RFC, ele precisa ser equivalente a um sal de 32 bytes zerados.
- Divida a chave gerada em duas de 256 bits:
symmetricEncryptionKey
emacKey
. Verifique se o campo
tag
é um MAC válido paraencryptedMessage
.Para gerar o MAC esperado, use o HMAC RFC 5869 (em inglês), com a função de hash SHA256 e a
macKey
recebida na etapa 2.Descriptografe
encryptedMessage
usando o modo AES-256-CTR com as seguintes características:- Zero IV
- Sem preenchimento
- A
symmetricEncryptionKey
derivada na etapa 2.
Gerenciamento de chaves
Chaves de criptografia do comerciante
Os comerciantes geram uma chave pública de acordo com os detalhes descritos na Especificação do esquema de criptografia.
Chaves de assinatura raiz do Google
O Google publica o conjunto de chaves públicas raiz de assinatura atualmente válidas que podem ser coletadas em um URL público. As chaves serão válidas pelo tempo indicado nos cabeçalhos do cache HTTP retornados pelo URL. Elas são armazenadas em cache até expirarem, conforme determinado no campo keyExpiration. Quando uma busca expirar, recomendamos realizar a busca novamente das chaves no URL público para receber a lista atual de chaves válidas.
Exceção para o protocolo ECv2: se não for possível buscar as chaves do Google dentro do ambiente de execução,
busque o keys.json
no nosso URL de produção, salve-o no sistema e atualize manualmente
de tempos em tempos. Em circunstâncias normais, o Google emite uma nova chave de assinatura raiz para o ECv2 cinco anos antes
da data de expiração da chave com a data de expiração mais distante. No caso de comprometimento, o Google
notifica todos os comerciantes usando os dados de contato fornecidos no portal de autoatendimento para
solicitar que atualizem o keys.json
o quanto antes. Para garantir que você não perca a rotação regular,
recomendamos que os comerciantes que optarem por salvar as chaves do Google no conteúdo do
keys.json
façam a atualização anualmente no momento da rotação anual de chaves.
As chaves fornecidas pelo URL público são mapeadas no seguinte formato:
{ "keys": [ { "keyValue": "encoded public key", "protocolVersion": "ECv2" "keyExpiration":"2000000000000" }, { "keyValue": "encoded public key", "protocolVersion": "ECv2" "keyExpiration":"3000000000000" } ] }
O keyValue
é uma versão em Base64 da chave codificada em ASN.1
do tipo SubjectPublicKeyInfo
e definida no padrão X.509, sem quebra de linha nem preenchimento. Em Java, a codificação ASN.1 mencionada
é representada pela
classe X509EncodedKeySpec (em inglês).
Ela pode ser recebida com ECPublicKey.getEncoded()
.
Os links a seguir são URLs para os ambientes de teste e de produção:
- Teste:
https://payments.developers.google.com/paymentmethodtoken/test/keys.json
- Produção:
https://payments.developers.google.com/paymentmethodtoken/keys.json
Rotação de chaves
Se você descriptografar um token de forma de pagamento diretamente nos seus servidores com integração direta, será necessário rotacionar as chaves anualmente.
Conclua as etapas a seguir para rotacionar as chaves de criptografia:
- Use o OpenSSL para gerar um novo par de chaves.
- Abra o Business Console do Google Pay enquanto estiver conectado com a Conta do Google usada anteriormente para gerenciar seu app com o Google Play.
- Na guia API Google Pay, no painel Integração direta, clique em Gerenciar ao lado da sua chave pública atual. Clique em Adicionar outra chave.
- Selecione o campo de texto Chave de criptografia pública e adicione sua chave pública recém-gerada codificada em Base64 no formato de ponto descompactado.
- Clique em Salvar chaves de criptografia.
Para garantir uma rotação de chaves sem problemas, é preciso ter compatibilidade com a descriptografia das chaves privadas novas e antigas durante a transição.
Se você usa a biblioteca Tink (em inglês) para descriptografar o token, utilize o código em Java a seguir para garantir a compatibilidade com várias chaves privadas:
String decryptedMessage = new PaymentMethodTokenRecipient.Builder() .addRecipientPrivateKey(newPrivateKey) .addRecipientPrivateKey(oldPrivateKey);
Verifique se o código de descriptografia está implantado na produção e se você monitora descriptografias bem-sucedidas.
Altere a chave pública usada no seu código.
Substitua o valor do atributo
publicKey
na propriedadePaymentMethodTokenizationSpecification
parameters
:/** * @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; }
- Implante o código da etapa 4 na produção. Depois que o código é implantado, as transações de criptografia e descriptografia usam os novos pares de chaves.
Confirme se a chave pública antiga não é mais usada para criptografar transações.
- Remova a chave privada antiga.
- Abra o Business Console do Google Pay enquanto estiver conectado com a Conta do Google usada anteriormente para se inscrever como desenvolvedor do Google Pay.
- Na guia API Google Pay, no painel Integração direta, clique em Gerenciar ao lado da sua chave pública atual. Clique em Excluir ao lado da sua chave pública antiga e clique em Salvar chaves de criptografia.
O Google usa a chave especificada na propriedade publicKey
dentro do objeto PaymentMethodTokenizationSpecification
parameters
, conforme mostrado
no exemplo a seguir:
{ "protocolVersion": "ECv2", "publicKey": "BOdoXP+9Aq473SnGwg3JU1..." }
Usar a biblioteca Tink (em inglês) para gerenciar a resposta criptografada
Para executar a verificação de assinatura e a descriptografia de mensagens, use a biblioteca de criptografia Tink (em inglês). Para integrar ao Tink e realizar a verificação e a descriptografia, siga as etapas abaixo:
No
pom.xml
, adicione o apppaymentmethodtoken
Tink como uma dependência:<dependencies> <!-- other dependencies ... --> <dependency> <groupId>com.google.crypto.tink</groupId> <artifactId>apps-paymentmethodtoken</artifactId> <version>1.2.0</version> <!-- or latest version --> </dependency> </dependencies>
Durante a inicialização do servidor, faça a pré-busca das chaves de assinatura do Google para disponibilizar a chave na memória. Isso impede que o usuário veja qualquer latência de rede enquanto as chaves são buscadas no processo de descriptografia.
GooglePaymentsPublicKeysManager.INSTANCE_PRODUCTION.refreshInBackground();
Descriptografe as chaves com o seguinte código, que presume que
paymentMethodToken
está armazenado na variávelencryptedMessage
e substitui as seções em negrito de acordo com o caso.Para testes de ambiente, substitua
INSTANCE_PRODUCTION
porINSTANCE_TEST
e [YOUR MERCHANT ID] por12345678901234567890
.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);
Substitua PrivateKey1 e PrivateKey2 por suas chaves. As variáveis podem ser uma string PKCS8 codificada em Base64 ou um objeto
ECPrivateKey
. Para mais informações sobre como produzir uma chave privada PKCS8 codificada em Base64, consulte Usar o OpenSSL para gerar um par de chaves.Se não for possível realizar uma chamada para o servidor do Google sempre que descriptografar chaves, descriptografe com o código a seguir e substitua as seções em negrito de acordo com o caso.
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);
A chave atual do ambiente de produção é válida até 14/04/2038 sob circunstâncias normais, exceto se a chave for comprometida. Nesse caso, o Google notifica todos os comerciantes usando os dados de contato fornecidos no portal de autoatendimento para solicitar que atualizem o
keys.json
o quanto antes.O snippet de código gerencia os seguintes detalhes de segurança para que você possa se concentrar no consumo do payload:
- Chaves de assinatura do Google buscadas e armazenadas em cache na memória
- Verificação de assinatura
- Descriptografia