Criptografia de dados de pagamento para comerciantes

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:

  1. Buscar as chaves de assinatura raiz do Google.
  2. 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.
  3. Verificar se a chave de assinatura intermediária do payload não expirou.
  4. Verificar se a assinatura do payload é válida de acordo com a chave de assinatura intermediária.
  5. Descriptografar o conteúdo do payload depois de verificar a assinatura.
  6. Verificar se a mensagem não expirou. Para isso, verifique se a hora atual é anterior à do campo messageExpiration no conteúdo descriptografado.
  7. 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 PaymentMethodTokenizationSpecification no Android ou na Web.

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_3DSauthMethod:

{
  "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).

  • Curva elíptica: NIST P-256 (também conhecida no OpenSSL como prime256v1).
  • CheckMode, OldCofactorMode, SingleHashMode e CofactorMode são 0.
  • O formato do ponto é descompactado.
Função de derivação de chaves

Baseado em HMAC com SHA-256 (HKDFwithSHA256).

  • Não forneça o sal.
  • As informações precisam ser codificadas pelo Google em ASCII para ECv2 da versão do protocolo.
  • 256 bits precisam ser derivados para a chave AES256 e outros 256 bits para a chave HMAC_SHA256.
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:

  1. 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 e CofactorMode são 0.
    • 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.
  2. Divida a chave gerada em duas de 256 bits: symmetricEncryptionKey e macKey.
  3. Verifique se o campo tag é um MAC válido para encryptedMessage.

    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.

  4. 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:

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:

  1. Use o OpenSSL para gerar um novo par de chaves.
  2. 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.
  3. 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.
  4. 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.
  5. Clique em Salvar chaves de criptografia.
  6. 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.

  7. Altere a chave pública usada no seu código.

    Substitua o valor do atributo publicKey na propriedade PaymentMethodTokenizationSpecification 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;
    }
  8. 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.
  9. Confirme se a chave pública antiga não é mais usada para criptografar transações.

  10. Remova a chave privada antiga.
  11. 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.
  12. 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:

  1. No pom.xml, adicione o app paymentmethodtoken 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>
    
  2. 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();
  3. Descriptografe as chaves com o seguinte código, que presume que paymentMethodToken está armazenado na variável encryptedMessage e substitui as seções em negrito de acordo com o caso.

    Para testes de ambiente, substitua INSTANCE_PRODUCTION por INSTANCE_TEST e [YOUR MERCHANT ID] por 12345678901234567890.

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

  5. 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