Crittografia dei dati di pagamento per i commercianti

L'API Google Pay restituisce i metodi di pagamento in un payload PaymentMethodToken firmato e criptato. I metodi di pagamento restituiti sono carte costituite da PAN o carte tokenizzate costituite da PAN del dispositivo e crittogrammi.

Il payload contiene un campo denominato protocolVersion che indica al destinatario del payload quali primitive crittografiche sono in uso e il formato previsto.

Questa guida fornisce informazioni su come generare una chiave pubblica per richiedere un token del metodo di pagamento criptato e firmato da Google e descrive in dettaglio i passaggi da seguire per verificare e decriptare il token.

Questa guida si applica solo a protocolVersion = ECv2.

Poiché ricevi direttamente i dati della carta di pagamento, assicurati che la tua app sia conforme allo standard PCI DSS e che i tuoi server dispongano dell'infrastruttura necessaria per gestire in modo sicuro le credenziali di pagamento dell'utente prima di procedere.

I seguenti passaggi descrivono cosa deve fare un integratore per utilizzare il payload dell'API Google Pay ECv2 PaymentMethodToken:

  1. Recupera le chiavi di firma della radice di Google.
  2. Verifica che la firma della chiave di firma intermedia sia valida per una delle chiavi di firma radice non scadute.
  3. Verifica che la chiave di firma intermedia del payload non sia scaduta.
  4. Verifica che la firma del payload sia valida tramite la chiave di firma intermedia.
  5. Decripta i contenuti del payload dopo aver verificato la firma.
  6. Verifica che il messaggio non sia scaduto. Ciò richiede di verificare che l'ora corrente sia inferiore al campo messageExpiration nei contenuti decriptati.
  7. Utilizza il metodo di pagamento nei contenuti decriptati e addebitalo.

Il codice di esempio nella nostra libreria Tink esegue i passaggi da 1 a 6.

Struttura del token del metodo di pagamento

Il messaggio restituito da Google nella risposta PaymentData è un oggetto JSON serializzato e codificato in UTF-8 con le chiavi specificate nella seguente tabella:

Nome Tipo Descrizione
protocolVersion Stringa Identifica lo schema di crittografia o firma in base al quale viene creato il messaggio. Consente al protocollo di evolversi nel tempo, se necessario.
signature Stringa Verifica che il messaggio provenga da Google. È codificato in base64 e creato con ECDSA dalla chiave di firma intermedia.
intermediateSigningKey Oggetto Un oggetto JSON contenente la chiave di firma intermedia di Google. Contiene signedKey con keyValue, keyExpiration e signatures. Viene serializzata per semplificare la procedura di verifica della firma della chiave di firma intermedia.
signedMessage Stringa Un oggetto JSON serializzato come stringa sicura per HTML contenente encryptedMessage, ephemeralPublicKey e tag. Viene serializzato per semplificare la procedura di verifica della firma.

Esempio

Di seguito è riportata una risposta del token del metodo di pagamento in 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\"}"
}

Chiave di firma intermedia

intermediateSigningKey è un oggetto JSON serializzato con codifica UTF-8 che contiene i seguenti valori:

Nome Tipo Descrizione
signedKey Stringa Un messaggio codificato in base64 che contiene la descrizione del pagamento della chiave.
signatures Stringa Verifica che la chiave di firma intermedia provenga da Google. È codificata in base64 e creata con ECDSA.

Chiave firmata

signedKey è un oggetto JSON serializzato con codifica UTF-8 che contiene i seguenti valori:

Nome Tipo Descrizione
keyValue Stringa Una versione base64 della chiave codificata nel tipo ASN.1. SubjectPublicKeyInfo è definito nello standard X.509.
keyExpiration Stringa Data e ora di scadenza della chiave intermedia in millisecondi UTC dall'epoca. Gli integratori rifiutano qualsiasi chiave scaduta.

Messaggio firmato

signedMessage è un oggetto JSON serializzato con codifica UTF-8 che contiene i seguenti valori:

Nome Tipo Descrizione
encryptedMessage Stringa Un messaggio criptato con codifica Base64 che contiene i dati di pagamento e alcuni campi di sicurezza aggiuntivi.
ephemeralPublicKey Stringa Una chiave pubblica effimera con codifica Base64 associata alla chiave privata per criptare il messaggio in formato punto non compresso. Per saperne di più, vedi Formato della chiave pubblica di crittografia.
tag Stringa Un MAC con codifica base64 di encryptedMessage.

Messaggio criptato

encryptedMessage decriptato è un oggetto JSON serializzato e codificato in UTF-8. Il JSON contiene due livelli. Il livello esterno contiene metadati e campi inclusi per la sicurezza, mentre il livello interno è un altro oggetto JSON che rappresenta la credenziale di pagamento effettiva.

Per ulteriori dettagli su encryptedMessage, consulta le seguenti tabelle e gli esempi di oggetti JSON:

Nome Tipo Descrizione
messageExpiration Stringa Data e ora di scadenza del messaggio in millisecondi UTC dal periodo. Gli integratori devono rifiutare qualsiasi messaggio scaduto.
messageId Stringa Un ID univoco che identifica il messaggio nel caso in cui debba essere revocato o individuato in un secondo momento.
paymentMethod Stringa Il tipo di credenziale di pagamento. Al momento è supportato solo CARD.
paymentMethodDetails Oggetto Le credenziali di pagamento. Il formato di questo oggetto è determinato da paymentMethod ed è descritto nelle tabelle seguenti.

Scheda

Le seguenti proprietà costituiscono una credenziale di pagamento per il metodo di pagamento CARD:

Nome Tipo Descrizione
pan Stringa Il numero dell'account personale a cui è stato addebitato l'importo. Questa stringa contiene solo cifre.
expirationMonth Numero Il mese di scadenza della carta, dove 1 rappresenta gennaio, 2 febbraio e così via.
expirationYear Numero L'anno di scadenza della carta a quattro cifre, ad esempio 2020.
authMethod Stringa Il metodo di autenticazione della transazione con carta.

PAN_ONLY

Il seguente snippet JSON è un esempio del encryptedMessage completo per 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

CARD autenticata con l'utilizzo di un criptogramma 3-D Secure, CRYPTOGRAM_3DS authMethod. Include i seguenti campi aggiuntivi:

Nome Tipo Descrizione
cryptogram Stringa Un crittogramma 3-D Secure.
eciIndicator Stringa Questa stringa non è sempre presente. Viene restituito solo per le transazioni con token dispositivo autenticati su Android (CRYPTOGRAM_3DS). Questo valore deve essere trasmesso nel flusso di elaborazione dei pagamenti.

Il seguente snippet JSON è un esempio del encryptedMessage completo per 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

Il circuito della carta potrebbe fornire eciIndicator per le transazioni con token dispositivo autenticati (CRYPTOGRAM_3DS).

Devi trasmettere il valore eciIndicator nella transazione di autorizzazione senza che venga modificato o codificato in modo permanente, altrimenti la transazione non va a buon fine. La tabella seguente mostra in dettaglio i valori di eciIndicator.

valore eciIndicator Circuito della carta Parte responsabile authMethod
""(empty) Mastercard Commerciante/Acquirer CRYPTOGRAM_3DS
02 Mastercard Emittente della carta CRYPTOGRAM_3DS
06 Mastercard Commerciante/Acquirer CRYPTOGRAM_3DS
05 Visa Emittente della carta CRYPTOGRAM_3DS
07 Visa Commerciante/Acquirer CRYPTOGRAM_3DS
""(empty) Altre reti Commerciante/Acquirer CRYPTOGRAM_3DS

Eventuali altri valori ECI per VISA e Mastercard non presenti in questa tabella non verranno restituiti.

Verifica della firma

Per verificare le firme, che includono la firma della chiave intermedia e del messaggio, sono necessari i seguenti elementi:

  • L'algoritmo utilizzato per creare la firma
  • La stringa di byte utilizzata per creare la firma
  • La chiave pubblica corrispondente a quella privata utilizzata per creare la firma
  • La firma stessa

Algoritmo di firma

Google utilizza l'algoritmo di firma digitale con curva ellittica (ECDSA) per firmare i messaggi con i seguenti parametri: ECDSA su NIST P-256 con SHA-256 come funzione hash, come definito in FIPS 186-4.

La firma

La firma è inclusa nel livello più esterno del messaggio. È codificata con base64 in formato byte ASN.1. Per ulteriori informazioni su ASN.1, consulta l'appendice A di IETF Tools. La firma è costituita dai numeri interi r e s ECDSA. Per maggiori informazioni, consulta la sezione Algoritmo di generazione della firma.

Di seguito è riportato un esempio del formato byte ASN.1 specificato, che è il formato standard prodotto dalle implementazioni ECDSA di Java Cryptography Extension (JCE).

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

Come costruire la stringa di byte per la firma della chiave di firma intermedia

Per convalidare la firma della chiave di firma intermedia nel token del metodo di pagamento di esempio, crea signedStringForIntermediateSigningKeySignature con la seguente formula:

signedStringForIntermediateSigningKeySignature =
length_of_sender_id || sender_id || length_of_protocol_version || protocol_version || length_of_signed_key || signed_key

La notazione "||" indica la concatenazione. Ogni componente (sender_id, protocolVersion, signedKey) deve essere codificato in UTF-8. Il signedKey deve essere la stringa di intermediateSigningKey.signedKey. La lunghezza in byte di ogni componente è di 4 byte in formato little-endian.

Esempio

Questo esempio utilizza il seguente token del metodo di pagamento di esempio:

{
  "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\"}"
}

sender_id è sempre Google e protocol_version è ECv2.

Se sender_id è Google, signedString viene visualizzato come mostrato nell'esempio seguente:

signedStringForIntermediateSigningKeySignature =
\x06\x00\x00\x00 || Google || | \x04\x00\x00\x00 || ECv2 || \xb5\x00\x00\x00 || {"keyExpiration":"1542323393147","keyValue":"MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAE/1+3HBVSbdv+j7NaArdgMyoSAM43yRydzqdg1TxodSzA96Dj4Mc1EiKroxxunavVIvdxGnJeFViTzFvzFRxyCw\u003d\u003d"}

Come verificare la firma in signedStringForIntermediateSigningKeySignature

L'algoritmo di verifica ECDSA standard viene utilizzato quando viene assemblata la stringa firmata per la firma della chiave di firma intermedia. Per il protocollo ECv2, devi scorrere tutte le firme in intermediateSigningKey.signatures e provare a convalidare ciascuna con le chiavi di firma Google non scadute in keys.json. Se almeno una convalida della firma funziona, considera la verifica completata. Utilizza intermediateSigningKey.signedKey.keyValue in un secondo momento per verificare signedStringForMessageSignature. Google ti consiglia vivamente di utilizzare una libreria crittografica esistente anziché il tuo codice di verifica.

Come costruire la stringa di byte per la firma del messaggio

Per convalidare la firma nel token del metodo di pagamento di esempio, crea signedStringForMessageSignature con la seguente formula:

signedStringForMessageSignature =
length_of_sender_id || sender_id || length_of_recipient_id || recipient_id || length_of_protocolVersion || protocolVersion || length_of_signedMessage || signedMessage

La notazione "||" indica la concatenazione. Ogni componente (sender_id, recipient_id, protocolVersion, signedMessage) deve essere codificato in UTF-8. La lunghezza in byte di ogni componente è di 4 byte in formato little-endian. Quando crei la stringa di byte, non analizzare o modificare signedMessage. Ad esempio, non sostituire \u003d con il carattere =.

Esempio

Il seguente esempio è un token di metodo di pagamento di esempio:

{
  "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\"}"
}

sender_id è sempre Google e recipient_id è merchant:merchantId. Il valore di merchantId corrisponde a quello trovato nella console di Google Pay e Wallet per i commercianti con accesso alla produzione.

Se sender_id è Google e recipient_id è merchant:12345, signedString viene visualizzato come nell'esempio seguente:

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"}

Come verificare la firma in signedStringForMessageSignature

Quando viene assemblata la stringa firmata, viene utilizzato l'algoritmo di verifica ECDSA standard. Il intermediateSigningKey.signedKey.keyValue verificato nel passaggio precedente viene utilizzato per verificare signedMessage. Google consiglia vivamente di utilizzare una libreria crittografica esistente anziché il tuo codice di verifica.

Specifica dello schema di crittografia

Google utilizza lo schema di crittografia integrata della curva ellittica (ECIES) per proteggere il token del metodo di pagamento restituito nella risposta dell'API Google Pay. Lo schema di crittografia utilizza i seguenti parametri:

Parametro Definizione
Metodo di incapsulamento chiave

ECIES-KEM, come definito nella norma ISO 18033-2.

  • Curva ellittica: NIST P-256 (nota anche in OpenSSL come prime256v1).
  • CheckMode, OldCofactorMode, SingleHashMode e CofactorMode sono 0.
  • Il formato del punto non è compresso.
Funzione di derivazione della chiave

Basato su HMAC con SHA-256 (HKDFwithSHA256).

  • Il sale non deve essere fornito.
  • Le informazioni devono essere codificate in ASCII da Google per la versione del protocollo ECv2.
  • Devono essere derivati 256 bit per la chiave AES256 e altri 256 bit per la chiave HMAC_SHA256.
Algoritmo di crittografia simmetrica

DEM2, come definito nello standard ISO 18033-2

Algoritmo di crittografia: AES-256-CTR con IV pari a zero e senza padding.

Algoritmo MAC HMAC_SHA256 con una chiave a 256 bit derivata dalla funzione di derivazione della chiave.

Formato della chiave pubblica di crittografia

La chiave pubblica di crittografia e ephemeralPublicKey restituite nei payload di Google sono formattate con la rappresentazione Base64 della chiave in formato punto non compresso. È composto dai seguenti due elementi:

  • Un numero magico che specifica il formato (0x04).
  • Due numeri interi grandi di 32 byte che rappresentano le coordinate X e Y nella curva ellittica.

Questo formato è descritto in modo più dettagliato in "Public Key Cryptography For The Financial Services Industry: The Elliptic Curve Digital Signature Algorithm (ECDSA)", ANSI X9.62, 1998.

Utilizzare OpenSSL per generare una chiave pubblica

Passaggio 1: genera una chiave privata

L'esempio seguente genera una chiave privata con curva ellittica adatta all'uso con NIST P-256 e la scrive in key.pem:

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

(Facoltativo) Visualizza le chiavi privata e pubblica

Utilizza il seguente comando per visualizzare sia la chiave privata che quella pubblica:

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

Il comando produce un output simile al seguente:

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

Passaggio 2: genera una chiave pubblica con codifica Base64

La chiave privata e quella pubblica generate nell'esempio del passaggio facoltativo precedente sono codificate in formato esadecimale. Per ottenere una chiave pubblica con codifica base64 in formato punto non compresso, utilizza questo 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

Il comando produce un file publicKey.txt il cui contenuto, la versione base64 della chiave in formato punto non compresso, è simile al seguente:

BOdoXP+9Aq473SnGwg3JU1aiNpsd9vH2ognq4PtDtlLGa3Kj8TPf+jaQNPyDSkh3JUhiS0KyrrlWhAgNZKHYF2Y=

Il contenuto del file non deve contenere spazi vuoti o ritorni a capo aggiuntivi. Per verificarlo, esegui questo comando in Linux o macOS:

od -bc publicKey.txt

Passaggio 3: genera una chiave privata con codifica base64 in formato PKCS #8

La libreria Tink prevede che la chiave privata sia codificata in base64 nel formato PKCS #8. Utilizza il seguente comando per generare la chiave privata in questo formato dalla chiave privata generata nel primo passaggio:

openssl pkcs8 -topk8 -inform PEM -outform DER -in key.pem -nocrypt | base64 | paste -sd "\0" -

Il comando produce un output simile al seguente:

MIGHAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBG0wawIBAQQgWV4oK8c/MZkCLk4qSCNjW0Zm6H0CBCtSYxkXkC9FBHehRANCAAQPldOnhO2/oXjdJD1dwlFPiNs6fcdoRgFu3/Z0iKj24SjTGyLRGAtYWLGXBZcDdPj3T2bJRHRVhE8Bc2AjkT7n

Come decriptare il token del metodo di pagamento

Per decriptare il token:

  1. Utilizza la chiave privata e il ephemeralPublicKey fornito per derivare una chiave condivisa lunga 512 bit che utilizza ECIES-KEM. Utilizza i seguenti parametri:
    • Curva ellittica: NIST P-256, nota anche in OpenSSL come prime256v1.
    • CheckMode, OldCofactorMode, SingleHashMode e CofactorMode sono 0.
    • Funzione di codifica: formato punto non compresso.
    • Funzione di derivazione della chiave: HKDFwithSHA256, come descritto in RFC 5869, con il seguente parametro:
      • Il sale non deve essere fornito. In base alla RFC, questo valore deve essere equivalente a un sale di 32 byte azzerati.
  2. Dividi la chiave generata in due chiavi lunghe 256 bit: symmetricEncryptionKey e macKey.
  3. Verifica che il campo tag sia un MAC valido per encryptedMessage.

    Per generare il MAC previsto, utilizza HMAC (RFC 5869) con la funzione hash SHA256 e il macKey ottenuto nel passaggio 2.

  4. Decripta encryptedMessage utilizzando la modalità AES-256-CTR e con i seguenti elementi:

    • Un IV pari a zero.
    • Non imbottito.
    • Il symmetricEncryptionKey derivato nel passaggio 2.

Gestione delle chiavi

Chiavi di crittografia del commerciante

I commercianti generano una chiave pubblica in base alle specifiche descritte in Specifiche dello schema di crittografia.

Chiavi di firma radice di Google

Google pubblica l'insieme di chiavi pubbliche di firma radice attualmente valide recuperabili da un URL pubblico. Le chiavi sono valide per tutto il tempo indicato dalle intestazioni della cache HTTP restituite dall'URL. Vengono memorizzate nella cache fino alla scadenza, che è determinata dal campo keyExpiration. Ti consigliamo di recuperare nuovamente le chiavi dall'URL pubblico quando una richiesta scade per ricevere l'elenco corrente di chiavi valide.

Eccezione per il protocollo ECv2: se non riesci a recuperare le chiavi da Google in fase di runtime, recupera keys.json dal nostro URL di produzione, salvalo nel tuo sistema e aggiornalo periodicamente manualmente. In circostanze normali, Google rilascia una nuova chiave di firma principale per ECv2 cinque anni prima della scadenza della chiave con la data di scadenza più lontana. In caso di compromissione delle chiavi, Google invia una notifica a tutti i commercianti tramite i dati di contatto forniti nel portale self-service per richiedere un ricaricamento più rapido di keys.json. Per non perdere la rotazione regolare, consigliamo ai commercianti che scelgono di salvare le chiavi Google nei contenuti di keys.json di aggiornarle annualmente nell'ambito della propria rotazione annuale delle chiavi.

Le chiavi fornite tramite l'URL pubblico vengono mappate nel seguente formato:

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

keyValue è una versione della chiave con codifica ASN.1 di tipo SubjectPublicKeyInfo definita nello standard X.509, codificata in base64, non sottoposta a wrapping o padding. In Java, la codifica ASN.1 a cui si fa riferimento è rappresentata dalla classe X509EncodedKeySpec. Può essere ottenuto con ECPublicKey.getEncoded().

Gli URL per gli ambienti di test e di produzione sono forniti dai seguenti link:

Rotazione chiave

Se decripti un token del metodo di pagamento direttamente sui tuoi server con l'integrazione diretta, devi ruotare le chiavi annualmente.

Completa i seguenti passaggi per ruotare le chiavi di crittografia:

  1. Utilizza OpenSSL per generare una nuova coppia di chiavi.
  2. Apri la console Google Pay & Wallet dopo aver eseguito l'accesso con l'Account Google utilizzato in precedenza per gestire la tua app con Google Play.
  3. Nella scheda API Google Pay, nel riquadro Integrazione diretta, fai clic su Gestisci accanto alla chiave pubblica esistente. Fai clic su Aggiungi un'altra chiave.
  4. Seleziona il campo di input di testo Chiave di crittografia pubblica e aggiungi la chiave pubblica appena generata con codifica Base64 in formato punto non compresso.
  5. Fai clic su Salva chiavi di crittografia.
  6. Per garantire una rotazione delle chiavi senza problemi, supporta la decriptografia delle chiavi private nuove e precedenti durante la transizione delle chiavi.

    Se utilizzi la libreria Tink per decriptare il token, utilizza il seguente codice Java per supportare più chiavi private:

    String decryptedMessage =
        new PaymentMethodTokenRecipient.Builder()
            .addRecipientPrivateKey(newPrivateKey)
            .addRecipientPrivateKey(oldPrivateKey);

    Assicurati che il codice per la decriptografia sia implementato in produzione e monitora le decrittografie riuscite.

  7. Modifica la chiave pubblica utilizzata nel codice.

    Sostituisci il valore dell'attributo publicKey nella proprietà 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. Esegui il deployment del codice del passaggio 4 in produzione. Una volta implementato il codice, le transazioni di crittografia e decrittografia utilizzano le nuove coppie di chiavi.
  9. Conferma che la vecchia chiave pubblica non viene più utilizzata per criptare le transazioni.

  10. Rimuovi la vecchia chiave privata.
  11. Apri Google Pay & Wallet Console dopo aver eseguito l'accesso con l'Account Google che hai utilizzato in precedenza per registrarti come sviluppatore con Google Pay.
  12. Nella scheda API Google Pay, nel riquadro Integrazione diretta, fai clic su Gestisci accanto alla chiave pubblica esistente. Fai clic su Elimina accanto alla vecchia chiave pubblica e fai clic su Salva chiavi di crittografia.

Google utilizza la chiave specificata nella proprietà publicKey all'interno dell'oggetto PaymentMethodTokenizationSpecification parameters, come mostrato nell'esempio seguente:

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

Utilizza la libreria Tink per gestire la risposta criptata

Per eseguire la verifica della firma e la decriptazione del messaggio, utilizza la libreria Tink paymentmethodtoken. Questa libreria è disponibile solo in Java. Per utilizzarlo, completa i seguenti passaggi:

  1. Nel file pom.xml, aggiungi l'app Tink paymentmethodtoken come dipendenza:

    <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. All'avvio del server, precarica le chiavi di firma Google per renderle disponibili in memoria. In questo modo, l'utente non visualizza alcuna latenza di rete mentre il processo di decrittografia recupera le chiavi.

    GooglePaymentsPublicKeysManager.INSTANCE_PRODUCTION.refreshInBackground();
  3. Decripta il messaggio con il seguente codice, che presuppone che paymentMethodToken sia memorizzato nella variabile encryptedMessage e sostituisci le sezioni in grassetto in base al tuo scenario.

    Per i test non di produzione, sostituisci INSTANCE_PRODUCTION con INSTANCE_TEST e, se l'integrazione è inattiva o non è configurata una chiave di crittografia, sostituisci [YOUR MERCHANT ID] con .

    • Attivo
    • Ha l'integrazione DIRECT abilitata
    • Ha una chiave di crittografia configurata

    non sostituire [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. Sostituisci PrivateKey1 con il valore della chiave privata appropriato associato al valore della chiave pubblica registrato con Google in Prepara le chiavi e registrati con Google. Puoi aggiungere più valori di altre chiavi private in un secondo momento, quando ti viene richiesto di ruotare le chiavi con Google. Le variabili possono essere una stringa PKCS8 codificata in base64 o un oggetto ECPrivateKey. Per saperne di più su come produrre una chiave privata PKCS8 codificata in base64, consulta Preparare le chiavi e registrarsi su Google.

  5. Se non riesci a chiamare un server Google ogni volta che decripti le chiavi, decripta con il codice seguente e sostituisci le sezioni in grassetto in base al tuo scenario.

    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 chiave attuale nell'ambiente di produzione è valida fino al 14/04/2038 in circostanze normali, ad eccezione dei compromessi della chiave. In caso di compromissione delle chiavi, Google invia una notifica a tutti i commercianti tramite i dati di contatto forniti nel portale self-service per richiedere un ricaricamento più rapido di keys.json.

    Lo snippet di codice gestisce i seguenti dettagli di sicurezza, in modo che tu possa concentrarti sul consumo del payload:

    • Chiavi di firma di Google recuperate e memorizzate nella cache in memoria
    • Verifica della firma
    • Decriptazione