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 che sono 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 sicurezza le credenziali di pagamento dell'utente prima di procedere.

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

  1. Recupera le chiavi di firma radice di Google.
  2. Verifica che la firma della chiave di firma intermedia sia valida per una qualsiasi 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 in base alla chiave di firma intermedia.
  5. Decripta i contenuti del payload dopo aver verificato la firma.
  6. Verifica che il messaggio non sia scaduto. A questo scopo, devi verificare che l'ora corrente sia precedente al campo messageExpiration nei contenuti decriptati.
  7. Utilizza il metodo di pagamento nei contenuti decriptati e addebita l'importo.

Il codice campione 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 con codifica UTF-8 con le chiavi specificate nella tabella seguente:

Nome Tipo Descrizione
protocolVersion Stringa Identifica lo schema di crittografia o firma con cui viene creato il messaggio. Consente al protocollo di evolversi nel tempo, se necessario.
signature Stringa Verifica che il messaggio provenga da Google. È codificata in base64 e creata 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 serializzato per semplificare la procedura di verifica della firma della chiave di firma intermedia.
signedMessage Stringa Un oggetto JSON serializzato come stringa sicura per HTML che contiene 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 con codifica 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 temporanea con codifica Base64 associata alla chiave privata per criptare il messaggio in formato punto non compresso. Per saperne di più, consulta Formato della chiave pubblica di crittografia.
tag Stringa Un MAC con codifica base64 di encryptedMessage.

Messaggio criptato

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

Per maggiori dettagli su encryptedMessage, consulta le seguenti tabelle ed esempi di oggetti JSON:

Nome Tipo Descrizione
messageExpiration Stringa Data e ora di scadenza del messaggio in millisecondi UTC dall'epoca. 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 La credenziale di pagamento stessa. Il formato di questo oggetto è determinato da paymentMethod ed è descritto nelle tabelle seguenti.

Carta

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

Nome Tipo Descrizione
pan Stringa Il numero di conto bancario personale addebitato. Questa stringa contiene solo cifre.
expirationMonth Numero Il mese di scadenza della carta, dove 1 rappresenta gennaio, 2 rappresenta febbraio e così via.
expirationYear Numero L'anno di scadenza della carta, composto da quattro cifre, ad esempio 2020.
authMethod Stringa Il metodo di autenticazione della transazione con carta.
merchantTokenId Stringa

L'ID token commerciante della transazione con carta. Questo campo facoltativo viene restituito solo se sono soddisfatte tutte le seguenti condizioni:

  • Il commerciante specifica un oggetto MIT *TransactionInfo nella richiesta.
  • Il commerciante include un tokenUpdateUrl nella richiesta.
  • L'utente seleziona uno strumento tokenizzato (ad es. DPAN).

PAN_ONLY

Il seguente snippet JSON è un esempio della encryptedMessage completa 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

Un CARD autenticato con l'utilizzo di un crittogramma 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 del dispositivo autenticato su Android (CRYPTOGRAM_3DS). Questo valore deve essere trasmesso lungo il flusso di elaborazione dei pagamenti.

Il seguente snippet JSON è un esempio di encryptedMessage completo per un paymentMethod CARD con un authMethod CRYPTOGRAM_3DS:

{
  "paymentMethod": "CARD",
  "paymentMethodDetails": {
    "authMethod": "CRYPTOGRAM_3DS",
    "pan": "1111222233334444",
    "expirationMonth": 10,
    "expirationYear": 2025,
    "cryptogram": "AAAAAA...",
    "eciIndicator": "eci indicator",
    "merchantTokenId": "merchant-token-id",
    
  },
  
  "messageId": "some-message-id",
  "messageExpiration": "1759309000000"
}

eciIndicator

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

Devi trasmettere il valore eciIndicator nella transazione di autorizzazione senza che venga modificato o impostato come hardcoded, altrimenti la transazione non andrà a buon fine. La tabella seguente descrive i valori di eciIndicator.

Valore eciIndicator Circuito della carta Parte responsabile authMethod
""(empty) Mastercard Commerciante/Acquirente CRYPTOGRAM_3DS
02 Mastercard Emittente della carta CRYPTOGRAM_3DS
06 Mastercard Commerciante/Acquirente CRYPTOGRAM_3DS
05 Visa Emittente della carta CRYPTOGRAM_3DS
07 Visa Commerciante/Acquirente CRYPTOGRAM_3DS
""(empty) Altre reti Commerciante/Acquirente 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 le firme 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 alla chiave privata utilizzata per creare la firma
  • La firma stessa

L'algoritmo di firma

Google utilizza l'algoritmo ECDSA (Elliptic Curve Digital Signature Algorithm) 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 saperne di più su ASN.1, consulta l'Appendice A di IETF Tools. La firma è costituita dai numeri interi r e s ECDSA. Per ulteriori informazioni, consulta la sezione Algoritmo di generazione della firma.

Di seguito è riportato un esempio del formato di 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 creare 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. 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 su 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 eseguire l'iterazione di tutte le firme in intermediateSigningKey.signatures e provare a convalidare ciascuna con le chiavi di firma di 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 consiglia vivamente di utilizzare una libreria crittografica esistente anziché il tuo codice di verifica.

Come creare la stringa di byte per la firma del messaggio

Per convalidare la firma nel token del metodo di pagamento di esempio, crea il 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

Di seguito è riportato un esempio di token del metodo di pagamento:

{
  "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. merchantId corrisponde al valore trovato nella Console di Google Pay e Wallet per i commercianti con accesso in 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 su signedStringForMessageSignature

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

Specifica dello schema di crittografia

Google utilizza l'Elliptic Curve Integrated Encryption Scheme (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 in ISO 18033-2.

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

Basato su HMAC con SHA-256 (HKDFwithSHA256).

  • Il salt non deve essere fornito.
  • Le informazioni devono essere "Google", codificate in ASCII 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 crittografico simmetrico

DEM2, come definito in ISO 18033-2

Algoritmo crittografico: AES-256-CTR con IV zero e senza spaziatura interna.

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 il ephemeralPublicKey restituito nei payload di Google sono formattati 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 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.

Utilizza OpenSSL per generare una chiave pubblica

Passaggio 1: genera una chiave privata

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

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

(Facoltativo) Visualizza le chiavi private e pubbliche

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 il seguente 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=

I contenuti del file non devono 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 tua chiave privata e il ephemeralPublicKey fornito per ricavare una chiave condivisa di 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 salt non deve essere fornito. In base all'RFC, questo deve essere equivalente a un salt di 32 byte azzerati.
  2. Suddividi la chiave generata in due chiavi da 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 la macKey ottenuta nel passaggio 2.

  4. Decripta encryptedMessage utilizzando la modalità AES-256-CTR e quanto segue:

    • Un IV pari a zero.
    • Senza spaziatura interna.
    • Il valore symmetricEncryptionKey ricavato 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, che possono essere recuperate da un URL pubblico. Le chiavi sono valide per il periodo di tempo indicato dalle intestazioni della cache HTTP restituite dall'URL. Vengono memorizzate nella cache fino alla scadenza, determinata dal campo keyExpiration. Quando un recupero scade, ti consigliamo di recuperare nuovamente le chiavi dall'URL pubblico per ricevere l'elenco corrente delle chiavi valide.

Eccezione per il protocollo ECv2: se non riesci a recuperare le chiavi da Google in fase di runtime, recupera la chiave keys.json dal nostro URL di produzione, salvala nel tuo sistema e aggiornala periodicamente manualmente. In circostanze normali, Google emette una nuova chiave di firma root per ECv2 cinque anni prima della scadenza della chiave con la data di scadenza più lunga. 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 assicurarti di 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 base64, non sottoposta a wrapping o padding, della chiave codificata nel tipo ASN.1 SubjectPublicKeyInfo definito nello standard X.509. 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 delle chiavi

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

Per ruotare le chiavi di crittografia, completa i seguenti passaggi:

  1. Utilizza OpenSSL per generare una nuova coppia di chiavi.
  2. Apri la Google Pay & Wallet Console dopo aver eseguito l'accesso con l'Account Google che hai utilizzato in precedenza per registrarti come sviluppatore per Google Pay.
  3. Nella scheda API Google Pay, nel riquadro Integrazione diretta, fai clic su Gestisci accanto alla tua chiave pubblica esistente. Fai clic su Aggiungi un'altra chiave.
  4. Seleziona il campo di immissione 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 decriptazione 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 decriptazione sia stato implementato in produzione e monitora le decriptazioni riuscite.

  7. Modifica la chiave pubblica utilizzata nel codice.

    Sostituisci il valore dell'attributo publicKey nella proprietà PaymentMethodTokenizationSpecification parameters:

    const tokenizationSpecification = {
      "type": "DIRECT",
      "parameters": {
        "protocolVersion": "ECv2",
        "publicKey": "BOdoXP1aiNp.....kh3JUhiSZKHYF2Y="
      }
    }
  8. Esegui il deployment del codice del passaggio 4 in produzione. Una volta eseguito il deployment del codice, le transazioni di crittografia e decriptazione 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 chiave pubblica precedente e poi 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 decrittografia dei messaggi, utilizza la libreria Tink paymentmethodtoken. Questa libreria è disponibile solo in Java. Per utilizzarla, segui questi 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, esegui il precaricamento delle chiavi di firma di 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 archiviato 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 non è attiva o non ha una chiave di crittografia configurata, sostituisci [YOUR MERCHANT ID] con 12345678901234567890.

    • Attivo
    • Integrazione DIRETTA 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 che si associa al valore della chiave pubblica registrata con Google in Prepara le chiavi e registrati con Google. In un secondo momento, puoi aggiungere più valori di chiavi private quando ti viene richiesto di ruotare le chiavi con Google. Le variabili possono essere una stringa PKCS8 con codifica in base64 o un oggetto ECPrivateKey. Per saperne di più su come generare una chiave privata PKCS8 con codifica base64, consulta Prepara le chiavi ed esegui la registrazione con Google.

  5. Se non riesci a chiamare un server di Google ogni volta che decripti le chiavi, decriptale con il seguente codice 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 delle compromissioni 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 sull'utilizzo del payload:

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