Criptografia datelor de plată pentru comercianți

API-ul Google Pay returnează metodele de plată într-o sarcină utilă PaymentMethodToken semnată și criptată. Metodele de plată returnate sunt fie carduri care conțin codul PAN, fie carduri tokenizate care conțin codul PAN al dispozitivului și criptograme.

Sarcina utilă conține un câmp numit protocolVersion care îi spune destinatarului sarcinii utile ce primitive criptografice sunt utilizate și formatul așteptat.

Acest ghid oferă informații despre cum să generați o cheie publică pentru a solicita un token de metodă de plată semnat și criptat de Google și detaliază pașii de urmat pentru a verifica și decripta token-ul.

Acest ghid se aplică numai pentru protocolVersion = ECv2 .

Deoarece primiți informațiile despre cardul de plată direct, asigurați-vă că aplicația dvs. este compatibilă cu PCI DSS și că serverele dvs. au infrastructura necesară pentru a gestiona în siguranță datele de autentificare ale utilizatorului înainte de a continua.

Următorii pași descriu ce trebuie să facă un integrator pentru a consuma sarcina utilă PaymentMethodToken ECv2 din API-ul Google Pay:

  1. Obțineți cheile de semnare root Google .
  2. Verificați dacă semnătura cheii de semnare intermediare este validă pentru oricare dintre cheile de semnare rădăcină neexpirate.
  3. Verificați dacă cheia de semnare intermediară a sarcinii utile nu a expirat.
  4. Verificați dacă semnătura sarcinii utile este validă prin cheia de semnare intermediară.
  5. Decriptați conținutul sarcinii utile după ce verificați semnătura.
  6. Verificați dacă mesajul nu a expirat. Aceasta necesită să verificați dacă ora curentă este mai mică decât câmpul messageExpiration din conținutul decriptat.
  7. Folosește metoda de plată din conținutul decriptat și încarcă-l.

Codul exemplu din biblioteca noastră Tink efectuează pașii 1–6.

Structura token-ului metodei de plată

Mesajul returnat de Google în răspunsul PaymentData este un obiect JSON serializat, codificat în UTF-8, cu cheile specificate în tabelul următor:

Nume Tip Descriere
protocolVersion Şir Identifică schema de criptare sau de semnare sub care este creat mesajul. Permite evoluția protocolului în timp, dacă este necesar.
signature Şir Verifică faptul că mesajul provine de la Google. Este codificat în base64 și creat cu ECDSA de cheia de semnare intermediară.
intermediateSigningKey Obiect Un obiect JSON care conține cheia de semnare intermediară de la Google. Acesta conține signedKey cu keyValue , keyExpiration și signatures . Este serializat pentru a simplifica procesul de verificare a semnăturii cheii de semnare intermediare.
signedMessage Şir Un obiect JSON serializat ca un șir HTML-safe care conține encryptedMessage , ephemeralPublicKey și tag . Este serializat pentru a simplifica procesul de verificare a semnăturii.

Exemplu

Următorul este un răspuns al token-ului metodei de plată în 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\"}"
}

Cheie de semnare intermediară

intermediateSigningKey este un obiect JSON serializat, codificat în UTF-8, care conține următoarele valori:

Nume Tip Descriere
signedKey Şir Un mesaj codificat în base64 care conține descrierea plății cheii.
signatures Şir Verifică faptul că cheia intermediară de semnare provine de la Google. Este codificată în base64 și creată cu ECDSA.

Cheie semnată

signedKey este un obiect JSON serializat, codificat în UTF-8, care conține următoarele valori:

Nume Tip Descriere
keyValue Şir O versiune base64 a cheii codificată în tipul ASN.1. SubjectPublicKeyInfo este definită în standardul X.509.
keyExpiration Şir Data și ora la care cheia intermediară expiră în milisecunde UTC de la epocă. Integratorii resping orice cheie expirată.

Mesaj semnat

signedMessage este un obiect JSON serializat, codificat în UTF-8, care conține următoarele valori:

Nume Tip Descriere
encryptedMessage Şir Un mesaj criptat, codificat în base64, care conține informații de plată și câteva câmpuri de securitate suplimentare.
ephemeralPublicKey Şir O cheie publică efemeră codificată în base64, asociată cu cheia privată pentru a cripta mesajul în format punctual necomprimat. Pentru mai multe informații, consultați Formatul cheii publice de criptare .
tag Şir Un MAC codificat în base64 al encryptedMessage .

Mesaj criptat

encryptedMessage decriptat este un obiect JSON serializat, codificat în UTF-8. JSON conține două niveluri. Nivelul exterior conține metadate și câmpuri incluse pentru securitate, în timp ce nivelul interior este un alt obiect JSON care reprezintă datele de plată efective.

Pentru mai multe detalii despre encryptedMessage , consultați următoarele tabele și exemple de obiecte JSON:

Nume Tip Descriere
messageExpiration Şir Data și ora la care mesajul expiră, în milisecunde UTC de la epocă. Integratorii ar trebui să respingă orice mesaj expirat.
messageId Şir Un ID unic care identifică mesajul în cazul în care acesta trebuie revocat sau localizat ulterior.
paymentMethod Şir Tipul acreditării de plată. În prezent, este acceptat doar CARD .
paymentMethodDetails Obiect Credențialele de plată în sine. Formatul acestui obiect este determinat de paymentMethod și este descris în tabelele următoare.

Card

Următoarele proprietăți constituie o acreditare de plată pentru metoda de plată CARD :

Nume Tip Descriere
pan Şir Numărul de cont personal debitat. Acest șir conține doar cifre.
expirationMonth Număr Luna de expirare a cardului, unde 1 reprezintă ianuarie, 2 reprezintă februarie și așa mai departe.
expirationYear Număr Anul de expirare format din patru cifre al cardului, cum ar fi 2020.
authMethod Şir Metoda de autentificare a tranzacției cu cardul.

PAN_ONLY

Următorul fragment JSON este un exemplu de mesaj encryptedMessage complet pentru o paymentMethod CARD cu o authMethod PAN_ONLY .

{
  "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 autentificat cu ajutorul unei criptograme 3-D Secure, CRYPTOGRAM_3DS authMethod . Acesta include următoarele câmpuri suplimentare:

Nume Tip Descriere
cryptogram Şir O criptogramă 3-D Secure.
eciIndicator Şir Acest șir nu este întotdeauna prezent. Returnează doar pentru tranzacțiile cu token-uri de dispozitiv autentificate pe Android (CRYPTOGRAM_3DS). Această valoare trebuie transmisă în fluxul de procesare a plăților.

Următorul fragment JSON este un exemplu de mesaj encryptedMessage complet pentru o paymentMethod CARD cu o authMethod CRYPTOGRAM_3DS :

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

eciIndicator

Rețeaua de carduri ar putea furniza eciIndicator pentru tranzacții cu token-uri de dispozitive autentificate (CRYPTOGRAM_3DS).

Trebuie să transmiteți valoarea eciIndicator în tranzacția de autorizare fără ca aceasta să fie modificată sau codificată; altfel, tranzacția eșuează. Tabelul următor detaliază valorile lui eciIndicator .

Valoarea indicatorului eci Rețea de carduri Partea răspunzătoare Metodă de autorizare
""(empty) MasterCard Comerciant/Achizitor CRYPTOGRAM_3DS
02 MasterCard Emitentul cardului CRYPTOGRAM_3DS
06 MasterCard Comerciant/Achizitor CRYPTOGRAM_3DS
05 Visa Emitentul cardului CRYPTOGRAM_3DS
07 Visa Comerciant/Achizitor CRYPTOGRAM_3DS
""(empty) Alte rețele Comerciant/Achizitor CRYPTOGRAM_3DS

Orice alte valori ECI pentru VISA și Mastercard care nu sunt prezente în acest tabel nu vor fi returnate.

Verificarea semnăturii

Pentru a verifica semnăturile, care includ semnăturile cheie intermediare și semnăturile mesajelor, sunt necesare următoarele elemente:

  • Algoritmul folosit pentru crearea semnăturii
  • Șirul de octeți folosit pentru a crea semnătura
  • Cheia publică care corespunde celei private utilizate pentru crearea semnăturii
  • Semnătura în sine

Algoritmul semnăturii

Google utilizează algoritmul de semnătură digitală cu curbă eliptică ( ECDSA ) pentru a semna mesajele cu următorii parametri: ECDSA peste NIST P-256 cu SHA-256 ca funcție hash, așa cum este definită în FIPS 186-4.

Semnătura

Semnătura este inclusă în nivelul cel mai exterior al mesajului. Este codificată cu base64 în format ASN.1 octeți. Pentru mai multe informații despre ASN.1, consultați Instrumentele IETF Anexa A. Semnătura este formată din numerele întregi ECDSA r și s . Pentru mai multe informații, consultați Algoritmul de generare a semnăturilor .

Următorul este un exemplu al formatului ASN.1 octet specificat, care este formatul standard produs de implementările ECDSA ale extensiei Java Cryptography Extension (JCE).

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

Cum se construiește șirul de octeți pentru semnătura cheie intermediară

Pentru a valida semnătura cheie intermediară în tokenul metodei de plată eșantion, construiți signedStringForIntermediateSigningKeySignature cu următoarea formulă:

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

Notația „||” înseamnă concatenare. Fiecare componentă sender_id , protocolVersion , signedKey — trebuie să fie codificată în UTF-8. signedKey trebuie să fie șirul de caractere intermediateSigningKey.signedKey . Lungimea în octeți a fiecărei componente este de 4 octeți în format little-endian.

Exemplu

Acest exemplu folosește următorul token pentru metoda de plată:

{
  "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 este întotdeauna Google , iar protocol_version este ECv2 .

Dacă sender_id este Google , signedString apare așa cum se arată în exemplul următor:

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

Cum se verifică semnătura pe signedStringForIntermediateSigningKeySignature

Algoritmul standard de verificare ECDSA este utilizat atunci când este asamblat șirul semnat pentru semnătura cheie intermediară. Pentru protocolul ECv2, trebuie să parcurgeți toate semnăturile din intermediateSigningKey.signatures și să încercați să validați fiecare semnătură cu cheile de semnare Google neexpirate din keys.json . Dacă cel puțin o validare a semnăturii funcționează, considerați verificarea finalizată. Utilizați intermediateSigningKey.signedKey.keyValue ulterior pentru a verifica signedStringForMessageSignature . Google vă recomandă insistent să utilizați o bibliotecă criptografică existentă, în loc de propriul cod de verificare.

Cum se construiește șirul de octeți pentru semnătura mesajului

Pentru a valida semnătura în tokenul metodei de plată eșantion, construiți signedStringForMessageSignature cu următoarea formulă:

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

Notația „||” înseamnă concatenare. Fiecare componentă sender_id , recipient_id , protocolVersion , signedMessage — trebuie să fie codificată în UTF-8. Lungimea în octeți a fiecărei componente este de 4 octeți în format little-endian. Când construiți șirul de octeți, nu analizați și nu modificați signedMessage . De exemplu, nu înlocuiți \u003d cu caracterul = .

Exemplu

Următorul exemplu este un token pentru o metodă de plată:

{
  "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 este întotdeauna Google , iar recipient_id este merchant: merchantId . merchantId corespunde valorii găsite în Consola Google Pay și Wallet pentru comercianții cu acces la nivel de producție.

Dacă sender_id este Google și recipient_id este merchant:12345 , signedString apare așa cum este în exemplul următor:

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

Cum se verifică semnătura pe signedStringForMessageSignature

Algoritmul standard de verificare ECDSA este utilizat atunci când este asamblat șirul semnat. Valoarea intermediateSigningKey.signedKey.keyValue verificată în pasul anterior este utilizată pentru a verifica signedMessage . Google vă recomandă insistent să utilizați o bibliotecă criptografică existentă în locul propriului cod de verificare.

Specificația schemei de criptare

Google folosește schema de criptare integrată cu curbă eliptică ( ECIES ) pentru a securiza token-ul metodei de plată returnat în răspunsul API-ului Google Pay. Schema de criptare folosește următorii parametri:

Parametru Definiţie
Metoda de încapsulare a cheilor

ECIES-KEM, așa cum este definit în ISO 18033-2 .

  • Curbă eliptică: NIST P-256 (cunoscută și în OpenSSL ca prime256v1).
  • CheckMode , OldCofactorMode , SingleHashMode și CofactorMode sunt 0.
  • Formatul de punct este necomprimat.
Funcția de derivare a cheii

Bazat pe HMAC cu SHA-256 ( HKDFwithSHA256 ).

  • Nu trebuie furnizată sare.
  • Informațiile trebuie să fie codificate de Google în ASCII pentru versiunea de protocol ECv2 .
  • 256 de biți trebuie derivați pentru cheia AES256 și alți 256 de biți trebuie derivați pentru cheia HMAC_SHA256 .
Algoritm de criptare simetrică

DEM2, așa cum este definit în ISO 18033-2

Algoritm de criptare: AES-256-CTR cu IV zero și fără umplutură.

Algoritmul MAC HMAC_SHA256 cu o cheie de 256 de biți, derivată din funcția de derivare a cheii.

Formatul cheii publice de criptare

Cheia publică de criptare și cheia publică ephemeralPublicKey returnată în sarcinile utile Google sunt formatate cu reprezentarea base64 a cheii în format de puncte necomprimat. Acestea constau din următoarele două elemente:

  • Un număr magic care specifică formatul (0x04).
  • Două numere întregi mari de 32 de octeți care reprezintă coordonatele X și Y în curba eliptică.

Acest format este descris mai detaliat în „Public Key Cryptography For The Financial Services Industry: The Elliptic Curve Digital Signature Algorithm (ECDSA)”, ANSI X9.62, 1998.

Folosește OpenSSL pentru a genera o cheie publică

Pasul 1: Generați o cheie privată

Următorul exemplu generează o cheie privată de tip curbă eliptică potrivită pentru utilizarea cu NIST P-256 și o scrie în key.pem :

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

Opțional: Vizualizați cheile private și publice

Folosește următoarea comandă pentru a vizualiza atât cheia privată, cât și cea publică:

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

Comanda produce o ieșire similară cu următoarea:

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

Pasul 2: Generați o cheie publică codificată în base64

Cheia privată și publică generate în exemplul anterior de pas opțional sunt codificate în hexazecimal. Pentru a obține o cheie publică codificată în base64 în format punctual necomprimat, utilizați următoarea comandă:

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

Comanda produce un fișier publicKey.txt al cărui conținut, versiunea base64 a cheii în format de punct necomprimat, seamănă cu următorul:

BOdoXP+9Aq473SnGwg3JU1aiNpsd9vH2ognq4PtDtlLGa3Kj8TPf+jaQNPyDSkh3JUhiS0KyrrlWhAgNZKHYF2Y=

Conținutul fișierului nu trebuie să aibă spații goale suplimentare sau retururi de car. Pentru a verifica acest lucru, executați următoarea comandă în Linux sau MacOS:

od -bc publicKey.txt

Pasul 3: Generați o cheie privată codificată în base64 în format PKCS #8

Biblioteca Tink se așteaptă ca cheia privată să fie codificată în base64 în format PKCS #8. Folosește următoarea comandă pentru a genera cheia privată în acest format din cheia privată generată în primul pas:

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

Comanda produce o ieșire similară cu următoarea:

MIGHAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBG0wawIBAQQgWV4oK8c/MZkCLk4qSCNjW0Zm6H0CBCtSYxkXkC9FBHehRANCAAQPldOnhO2/oXjdJD1dwlFPiNs6fcdoRgFu3/Z0iKj24SjTGyLRGAtYWLGXBZcDdPj3T2bJRHRVhE8Bc2AjkT7n

Cum se decriptează token-ul metodei de plată

Urmați acești pași pentru a decripta token-ul:

  1. Folosește cheia privată și ephemeralPublicKey -ul dat pentru a obține o cheie partajată de 512 biți care utilizează ECIES-KEM . Folosește următorii parametri:
    • Curbă eliptică: NIST P-256, cunoscută și în OpenSSL ca prime256v1.
    • CheckMode , OldCofactorMode , SingleHashMode și CofactorMode sunt 0 .
    • Funcție de codificare: Format de puncte necomprimat.
    • Funcția de derivare a cheii: HKDFwithSHA256, așa cum este descrisă în RFC 5869 , cu următorul parametru:
      • Nu trebuie furnizată o valoare salt. Conform RFC, aceasta trebuie să fie echivalentă cu o valoare salt de 32 de octeți aduși la zero.
  2. Împărțiți cheia generată în două chei de 256 de biți: symmetricEncryptionKey și macKey .
  3. Verificați dacă câmpul tag este un MAC valid pentru encryptedMessage .

    Pentru a genera MAC-ul așteptat, utilizați HMAC ( RFC 5869 ) cu funcția hash SHA256 și macKey obținută în Pasul 2.

  4. Decriptați encryptedMessage folosind modul AES-256-CTR și cu următoarele:

    • Un IV zero.
    • Necăptușit.
    • Cheia symmetricEncryptionKey derivată în Pasul 2.

Gestionarea cheilor

Chei de criptare pentru comercianți

Comercianții generează o cheie publică conform specificațiilor descrise în Specificația schemei de criptare .

Chei de semnare Google root

Google publică setul de chei publice de semnare rădăcină valide în prezent, care pot fi preluate de la o adresă URL publică. Cheile sunt valide atât timp cât indică anteturile HTTP cache returnate de adresa URL. Acestea sunt stocate în cache până la expirare, fapt determinat de câmpul keyExpiration . Recomandăm ca, atunci când o preluare expiră, să preluați din nou cheile de la adresa URL publică pentru a primi lista curentă de chei valide.

Excepție pentru protocolul ECv2: Dacă nu puteți prelua cheile de la Google în timpul execuției, preluați fișierul keys.json de la adresa URL de producție, salvați-l în sistem și actualizați periodic manual. În circumstanțe normale, Google emite o nouă cheie de semnare rădăcină pentru ECv2 cu cinci ani înainte de expirarea cheii cu cea mai lungă dată de expirare. În cazul compromiterii cheilor, Google notifică toți comercianții prin intermediul informațiilor de contact furnizate în portalul self-service pentru a solicita o reîncărcare mai rapidă a keys.json . Pentru a vă asigura că nu ratați rotația regulată, recomandăm comercianților care aleg să salveze cheile Google în conținutul fișierului keys.json să actualizeze anual fișierul, ca parte a propriei rotații anuale a cheilor.

Cheile furnizate prin URL-ul public sunt mapate în următorul format:

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

keyValue este o versiune base64, fără wrap sau padding, a cheii codificate în tipul ASN.1 SubjectPublicKeyInfo definit în standardul X.509. În Java, codificarea ASN.1 la care se face referire este reprezentată de clasa X509EncodedKeySpec . Aceasta poate fi obținută cu ECPublicKey.getEncoded() .

Adresele URL atât pentru mediile de testare, cât și pentru cele de producție sunt furnizate de următoarele linkuri:

Rotația cheii

Dacă decriptați un token al metodei de plată direct pe serverele dvs. cu integrare directă, atunci trebuie să rotiți cheile anual.

Parcurgeți următorii pași pentru a roti cheile de criptare:

  1. Folosește OpenSSL pentru a genera o nouă pereche de chei .
  2. Deschideți consola Google Pay și Wallet în timp ce sunteți conectat(ă) cu Contul Google folosit anterior pentru a gestiona aplicația cu Google Play.
  3. În fila Google Pay API , sub panoul Integrare directă , faceți clic pe Gestionare lângă cheia publică existentă. Faceți clic pe Adăugați o altă cheie .
  4. Selectați câmpul de introducere a textului Cheie publică de criptare și adăugați cheia publică nou generată, codificată în base64, în format de puncte necomprimat.
  5. Faceți clic pe Salvați cheile de criptare .
  6. Pentru a asigura o rotație fără probleme a cheilor, acceptați decriptarea atât a cheilor private noi, cât și a celor vechi în timp ce transferați cheile.

    Dacă utilizați biblioteca Tink pentru a decripta token-ul, utilizați următorul cod Java pentru a accepta mai multe chei private:

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

    Asigurați-vă că codul pentru decriptare este implementat în producție și că monitorizați decriptările reușite.

  7. Schimbați cheia publică utilizată în codul dvs.

    Înlocuiți valoarea atributului publicKey din proprietatea parameters PaymentMethodTokenizationSpecification :

    /**
     * @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. Implementați codul de la pasul 4 în producție. Odată ce codul este implementat, tranzacțiile de criptare și decriptare utilizează noile perechi de chei.
  9. Confirmați că vechea cheie publică nu mai este utilizată pentru criptarea tranzacțiilor.

  10. Eliminați vechea cheie privată.
  11. Deschideți consola Google Pay și Wallet în timp ce sunteți conectat(ă) cu Contul Google pe care l-ați folosit anterior pentru a vă înregistra ca dezvoltator cu Google Pay.
  12. În fila Google Pay API , sub panoul Integrare directă , faceți clic pe Gestionare lângă cheia publică existentă. Faceți clic pe Ștergere lângă vechea cheie publică și apoi pe Salvare chei de criptare .

Google folosește cheia specificată în proprietatea publicKey din cadrul obiectului parameters PaymentMethodTokenizationSpecification , așa cum se arată în exemplul următor:

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

Folosește biblioteca Tink pentru a gestiona răspunsul criptat

Pentru a efectua verificarea semnăturii și decriptarea mesajelor, utilizați biblioteca Tink paymentmethodtoken . Această bibliotecă este disponibilă numai în Java. Pentru a o utiliza, urmați pașii următori:

  1. În fișierul pom.xml , adăugați aplicația Tink paymentmethodtoken ca dependență:

    <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. La pornirea serverului, preîncărcați cheile de semnare Google pentru a le face disponibile în memorie. Acest lucru împiedică utilizatorul să vadă orice latență a rețelei în timp ce procesul de decriptare preia cheile.

    GooglePaymentsPublicKeysManager.INSTANCE_PRODUCTION.refreshInBackground();
  3. Decriptați mesajul cu următorul cod, care presupune că paymentMethodToken este stocat în variabila encryptedMessage și înlocuiți secțiunile cu caractere aldine în conformitate cu scenariul dvs.

    Pentru testele care nu sunt în producție, înlocuiți INSTANCE_PRODUCTION cu INSTANCE_TEST , iar dacă integrarea este inactivă sau nu are configurată o cheie de criptare, înlocuiți [YOUR MERCHANT ID] cu .

    • Activ
    • Are integrarea DIRECT activată
    • Are o cheie de criptare configurată

    nu înlocuiți [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. Înlocuiți PrivateKey1 cu valoarea cheii private corespunzătoare care se asociază cu valoarea cheii publice înregistrate la Google din Pregătiți-vă cheile și înregistrați-vă la Google . Puteți adăuga mai multe alte valori ale cheii private ulterior, când este necesar să rotiți cheile cu Google . Variabilele pot fi fie un șir PKCS8 codificat în base64, fie un obiect ECPrivateKey . Pentru mai multe informații despre cum să produceți o cheie privată PKCS8 codificată în base64, consultați Pregătiți-vă cheile și înregistrați-vă la Google .

  5. Dacă nu puteți apela un server Google de fiecare dată când decriptați chei, decriptați cu următorul cod și înlocuiți secțiunile cu caractere aldine în funcție de scenariul dvs.

    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);

    Cheia curentă din mediul de producție este valabilă până la 14.04.2038 în circumstanțe normale, cu excepția compromiterii cheilor. În cazul compromiterii cheilor, Google notifică toți comercianții prin intermediul informațiilor de contact furnizate în portalul self-service pentru a solicita o reîncărcare mai rapidă a keys.json .

    Fragmentul de cod gestionează următoarele detalii de securitate, astfel încât să vă puteți concentra pe consumul de date utile:

    • Cheile de semnare Google au fost preluate și stocate în memoria cache
    • Verificarea semnăturii
    • Decriptare