Interfejs Google Pay API zwraca formy płatności w podpisanym i zaszyfrowanym ładunku PaymentMethodToken
. Zwracane formy płatności to albo karty składające się z numeru PAN, albo karty tokenizowane, które składają się z numeru PAN urządzenia i kryptogramów.
Ładunek zawiera pole o nazwie protocolVersion
, które informuje odbiorcę ładunku o używanych prymitywach kryptograficznych i oczekiwanym formacie.
Ten przewodnik zawiera informacje na temat generowania klucza publicznego, który jest potrzebny, aby zgłosić żądanie tokena formy płatności podpisanego i zaszyfrowanego przez Google. Znajdziesz tu również instrukcje, jak zweryfikować i odszyfrować token.
Ten przewodnik dotyczy tylko protocolVersion = ECv2
.
Dane karty płatniczej otrzymujesz bezpośrednio, dlatego dopilnuj, aby Twoja aplikacja była zgodna ze standardem PCI DSS, a Twoje serwery miały infrastrukturę wymaganą do bezpiecznego obsługiwania danych uwierzytelniających płatność użytkownika.
Oto co musi zrobić integrator, aby przetworzyć ładunek ECv2
PaymentMethodToken
z Google Pay API:
- Pobrać główne klucze podpisywania Google.
- Przy użyciu dowolnego z działających głównych kluczy podpisywania sprawdzić, czy podpis pośredniego klucza podpisywania jest prawidłowy.
- Sprawdzić, czy pośredni klucz podpisywania ładunku nie wygasł.
- Przy użyciu pośredniego klucza podpisywania sprawdzić, czy podpis ładunku jest prawidłowy.
- Po sprawdzeniu podpisu odszyfrować zawartość ładunku.
- Sprawdzić, czy wiadomość nie wygasła. Wymaga to sprawdzenia, czy obecny czas wypada przed czasem w polu
messageExpiration
w odszyfrowanej zawartości. - Użyć formy płatności w odszyfrowanej zawartości i obciążyć tę formę.
Przykładowy kod w naszej bibliotece Tink wykonuje kroki 1–6.
Struktura tokena formy płatności
Wiadomość zwrócona przez Google w odpowiedzi PaymentData
jest zakodowanym w UTF-8 zserializowanym obiektem JSON z kluczami określonymi w tej tabeli:
Nazwa | Typ | Opis |
---|---|---|
protocolVersion |
Ciąg znaków | Określa schemat szyfrowania/podpisywania, według którego została utworzona wiadomość. Umożliwia dostosowanie protokołu w czasie, jeśli jest to konieczne. |
signature |
Ciąg znaków | Potwierdza, że wiadomość pochodzi od Google. Jest zakodowany w base64 i utworzony za pomocą ECDSA przy użyciu pośredniego klucza podpisywania. |
intermediateSigningKey |
Obiekt | Obiekt JSON zawierający pośredni klucz podpisywania od Google. Zawiera signedKey z keyValue , keyExpiration i signatures . Jest zserializowany, aby uprościć proces weryfikacji podpisu pośredniego klucza podpisywania. |
signedMessage |
Ciąg znaków | Obiekt JSON zserializowany jako bezpieczny dla HTML ciąg znaków zawierający
encryptedMessage , ephemeralPublicKey , oraz tag . Jest zserializowany, aby uprościć proces weryfikacji podpisu. |
Przykład
Poniżej znajdziesz odpowiedź tokena formy płatności w formacie 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\"}" }
Pośredni klucz podpisywania
intermediateSigningKey
to zakodowany w UTF-8 zserializowany obiekt JSON, który zawiera następujące wartości:
Nazwa | Typ | Opis |
---|---|---|
signedKey |
Ciąg znaków | Wiadomość zakodowana w base64, zawierająca opis płatności w kluczu. |
signatures |
Ciąg znaków | Sprawdza, czy pośredni klucz podpisywania pochodzi od Google. Jest zakodowany w base64 i utworzony za pomocą ECDSA. |
Podpisany klucz
signedKey
to zakodowany w UTF-8 zserializowany obiekt JSON, który zawiera następujące wartości:
Nazwa | Typ | Opis |
---|---|---|
keyValue |
Ciąg znaków | Wersja klucza w base64 zakodowanego w ASN.1. SubjectPublicKeyInfo jest zdefiniowany w standardzie X.509. |
keyExpiration |
Ciąg znaków | Data i godzina, kiedy klucz pośredni traci ważność (milisekundy w czasie UTC od początku epoki). Integratory odrzucają wszystkie klucze, które wygasły. |
Podpisana wiadomość
signedMessage
to zakodowany w UTF-8 zserializowany obiekt JSON, który zawiera następujące wartości:
Nazwa | Typ | Opis |
---|---|---|
encryptedMessage |
Ciąg znaków | Zaszyfrowana wiadomość zakodowana w base64, zawierająca dane do płatności i dodatkowe pola bezpieczeństwa. |
ephemeralPublicKey |
Ciąg znaków | Tymczasowy klucz publiczny z kodowaniem base64 powiązany z kluczem prywatnym do szyfrowania wiadomości w nieskompresowanym formacie ze zdefiniowanym separatorem. Więcej informacji znajdziesz w sekcji Format publicznego klucza szyfrowania. |
tag |
Ciąg znaków | Algorytm MAC z encryptedMessage zakodowany w base64. |
Zaszyfrowana wiadomość
Odszyfrowana encryptedMessage
to zserializowany obiekt JSON zakodowany w UTF-8. Obiekt JSON ma dwa poziomy. Poziom zewnętrzny zawiera metadane i pola związane z zabezpieczeniami, a poziom wewnętrzny to kolejny obiekt JSON reprezentujący faktyczne dane uwierzytelniające płatność.
W tabelach i przykładach obiektów JSON znajdziesz więcej informacji na temat encryptedMessage
:
Nazwa | Typ | Opis |
---|---|---|
gatewayMerchantId |
Ciąg znaków | Unikalny identyfikator sprzedawcy, który firma obsługująca płatności może zrozumieć i wykorzystać do potwierdzenia, że wiadomość była przeznaczona dla sprzedawcy, który jej zażądał. Utworzony przez firmę obsługującą płatności i przekazany do Google przez sprzedawcę w |
messageExpiration |
Ciąg znaków | Data i godzina, kiedy wiadomość traci ważność (milisekundy w czasie UTC od początku epoki). Integrator powinien odrzucać wszystkie wiadomości, które straciły ważność. |
messageId |
Ciąg znaków | Unikalny identyfikator, po którym można rozpoznać wiadomość, jeśli trzeba będzie ją później anulować lub zlokalizować. |
paymentMethod |
Ciąg znaków | Typ danych uwierzytelniających płatność.
Obecnie obsługiwana jest tylko CARD .
|
paymentMethodDetails |
Obiekt | Dane uwierzytelniające płatność. Format tego obiektu jest określany na podstawie paymentMethod i został opisany w tabelach poniżej. |
Karta
Dane uwierzytelniające płatność używane przez CARD
składają się z tych właściwości:
Nazwa | Typ | Opis |
---|---|---|
pan |
Ciąg znaków | Podany numer obciążanego konta osobistego. Ten ciąg znaków zawiera tylko cyfry. |
expirationMonth |
Liczba | Miesiąc, w którym karta traci ważność (1 oznacza styczeń, 2 oznacza luty itd.). |
expirationYear |
Liczba | Rok, w którym karta traci ważność, wyrażony czterema cyframi (np. 2020). |
authMethod |
Ciąg znaków | Metoda uwierzytelniania transakcji kartą. |
assuranceDetails |
AssuranceDetailsSpecifications | Ten obiekt dostarcza informacje o weryfikacji przeprowadzonej na zwróconych danych uwierzytelniających płatności. |
PAN_ONLY
Ten fragment kodu JSON zawiera przykład pełnej wiadomości encryptedMessage
w przypadku CARD
paymentMethod
z 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
CARD
z uwierzytelnieniem przy użyciu kryptogramu 3-D Secure (CRYPTOGRAM_3DS
authMethod
). Zawiera te dodatkowe pola:
Nazwa | Typ | Opis |
---|---|---|
cryptogram |
Ciąg znaków | Kryptogram w standardzie 3-D Secure. |
eciIndicator |
Ciąg znaków | Wartość nie zawsze jest używana. Zwracana jest tylko w przypadku tokenów w sieci kart Visa. Ta wartość jest przekazywana w żądaniu autoryzacji płatności. |
Ten fragment kodu JSON zawiera przykład pełnej wiadomości encryptedMessage
w przypadku CARD
paymentMethod
z CRYPTOGRAM_3DS
authMethod
:
{ "paymentMethod": "CARD", "paymentMethodDetails": { "authMethod": "CRYPTOGRAM_3DS", "pan": "1111222233334444", "expirationMonth": 10, "expirationYear": 2020, "cryptogram": "AAAAAA...", "eciIndicator": "eci indicator" }, "messageId": "some-message-id", "messageExpiration": "1577862000000" }
Weryfikacja podpisu
Aby można było zweryfikować podpisy, które zawierają podpisy klucza pośredniego oraz wiadomości, potrzebny jest:
- algorytm użyty do utworzenia podpisu,
- ciąg bajtów wykorzystany do utworzenia podpisu,
- klucz publiczny odpowiadający kluczowi prywatnemu użytemu do utworzenia podpisu,
- podpis.
Algorytm podpisu
Google stosuje algorytm podpisu cyfrowego wykorzystujący krzywe eliptyczne Elliptic Curve Digital Signature Algorithm, ECDSA) do podpisywania wiadomości za pomocą następujących parametrów: ECDSA przez NIST P-256 z SHA-256 jako funkcją skrótu, jak określono w standardzie FIPS 186-4.
Podpis
Podpis znajduje się na najbardziej zewnętrznym poziomie wiadomości. Jest zakodowany w base64 w formacie bajtowym ASN.1. Więcej informacji o ASN.1 znajdziesz w Dodatku A narzędzi IETF. Podpis składa się z liczb całkowitych r oraz s algorytmu ECDSA. Więcej informacji znajdziesz w artykule na temat algorytmu generującego podpis.
Poniżej przedstawiamy przykład formatu bajtowego ASN.1, który jest standardowym formatem produkowanym przez implementacje ECDSA z wykorzystaniem rozszerzenia Java Cryptography Extension (JCE).
ECDSA-Sig-Value :: = SEQUENCE { r INTEGER, s INTEGER }
Jak utworzyć ciąg bajtów do podpisu pośredniego klucza podpisywania
Aby sprawdzić poprawność podpisu pośredniego klucza podpisywania w próbnym tokenie formy płatności, utwórz signedStringForIntermediateSigningKeySignature
na podstawie tej formuły:
signedStringForIntermediateSigningKeySignature = length_of_sender_id || sender_id || length_of_protocol_version || protocol_version || length_of_signed_key || signed_key
„||” oznacza konkatenację. Każdy komponent – sender_id
, protocolVersion
, signedKey
– musi być zakodowany w formacie UTF-8. signedKey
musi być ciągiem z intermediateSigningKey.signedKey
.
Każdy z elementów ma długość 4 bajtów w formacie little-endian.
Przykład
W tym przykładzie korzystamy z próbnego tokena formy płatności:
{ "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
to zawsze Google
, a protocol_version
to ECv2
.
Jeśli sender_id
to Google
, signedString
wygląda w następujący sposób:
signedStringForIntermediateSigningKeySignature = \x06\x00\x00\x00 || Google || | \x04\x00\x00\x00 || ECv2 || \xb5\x00\x00\x00 || {"keyExpiration":"1542323393147","keyValue":"MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAE/1+3HBVSbdv+j7NaArdgMyoSAM43yRydzqdg1TxodSzA96Dj4Mc1EiKroxxunavVIvdxGnJeFViTzFvzFRxyCw\u003d\u003d"}
Jak zweryfikować podpis signedStringForIntermediateSigningKeySignature
Podczas tworzenia podpisanego ciągu znaków dla podpisu pośredniego klucza podpisywania używany jest standardowy algorytm weryfikacji ECDSA. W przypadku protokołu ECv2 należy iterować wszystkie podpisy w intermediateSigningKey.signatures
i spróbować zweryfikować każdy z nich przy użyciu działających kluczy podpisywania Google w keys.json
. Jeśli powiedzie się przynajmniej jedna weryfikacja podpisu, weryfikację można uznać za zakończoną. Następnie użyj intermediateSigningKey.signedKey.keyValue
do zweryfikowania signedStringForMessageSignature
. Zdecydowanie zalecamy skorzystanie z istniejącej biblioteki kryptograficznej zamiast własnego kodu weryfikacyjnego.
Jak utworzyć ciąg bajtów do podpisu wiadomości
Aby sprawdzić poprawność podpisu w próbnym tokenie formy płatności, utwórz signedStringForMessageSignature
na podstawie tej formuły:
signedStringForMessageSignature = length_of_sender_id || sender_id || length_of_recipient_id || recipient_id || length_of_protocolVersion || protocolVersion || length_of_signedMessage || signedMessage
„||” oznacza konkatenację. Każdy komponent – sender_id
, recipient_id
, protocolVersion
, signedMessage
– musi być zakodowany w formacie UTF-8. Każdy z elementów ma długość 4 bajtów w formacie little-endian. Gdy tworzysz ciąg bajtów, nie analizuj ani nie modyfikuj elementu signedMessage
. Nie zastępuj na przykład elementu \u003d
znakiem =
.
Przykład
Oto próbny token formy płatności:
{ "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
to zawsze Google
, a recipient_id
to merchant:merchantId
. merchantId
odpowiada wartości w
Konsoli usług Google Pay i Portfela Google dla sprzedawców z dostępem produkcyjnym.
Jeśli sender_id
to Google
, a recipient_id
to merchant:12345
, signedString
wygląda jak w tym przykładzie:
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"}
Jak zweryfikować podpis signedStringForMessageSignature
Podczas tworzenia podpisanego ciągu znaków używany jest standardowy algorytm weryfikacji ECDSA. intermediateSigningKey.signedKey.keyValue
zweryfikowany w poprzednim kroku jest używany do zweryfikowania signedMessage
. Zdecydowanie zalecamy skorzystanie z istniejącej biblioteki kryptograficznej zamiast własnego kodu weryfikacyjnego.
Specyfikacja schematu szyfrowania
Google stosuje schemat szyfrowania zintegrowanego wykorzystujący krzywe eliptyczne (Elliptic Curve Integrated Encryption Scheme, ECIES), by zabezpieczać token formy płatności zwracany w odpowiedzi z Google Pay API. Schemat szyfrowania wykorzystuje poniższe parametry:
Parametr | Definicja |
---|---|
Metoda hermetyzacji klucza | ECIES-KEM według definicji z normy ISO 18033-2.
|
Funkcja derywacji klucza | Oparta na HMAC z SHA-256 (
|
Algorytm szyfrowania symetrycznego |
DEM2 według definicji z normy ISO 18033-2. Algorytm szyfrowania: AES-256-CTR o zerowym IV i bez dopełnienia. |
Algorytm MAC | HMAC_SHA256 z 256-bitowym kluczem pozyskanym z funkcji derywacji klucza. |
Format publicznego klucza szyfrowania
Publiczny klucz szyfrowania i ephemeralPublicKey
zwrócony w ładunkach Google są formatowane za pomocą klucza zakodowanego w base64 w nieskompresowanym formacie ze zdefiniowanym separatorem. Składa się z dwóch następujących elementów:
- Jedna liczba magiczna, która określa format (0x04).
- Dwie liczby całkowite o wielkości 32 bajtów reprezentujące współrzędne X i Y na krzywej eliptycznej.
Format jest opisany bardziej szczegółowo w normie „Public Key Cryptography For The Financial Services Industry: The Elliptic Curve Digital Signature Algorithm (ECDSA)” (ANSI X9.62) wydanej w 1998 r.
Korzystanie z OpenSSL do generowania klucza publicznego
Krok 1. Wygeneruj klucz prywatny
Ten przykład generuje klucz prywatny wykorzystujący krzywe eliptyczne, odpowiedni do zastosowania z NIST P-256, i zapisuje go w key.pem
:
openssl ecparam -name prime256v1 -genkey -noout -out key.pem
Opcjonalnie: wyświetl klucz prywatny i publiczny
Wyświetl klucz prywatny i publiczny za pomocą tego polecenia:
openssl ec -in key.pem -pubout -text -noout
Polecenie wyświetli dane wyjściowe podobne do tych poniżej:
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
Krok 2. Wygeneruj klucz publiczny zakodowany w standardzie base64
Przykładowe klucze prywatny i publiczny wygenerowane w poprzednim kroku opcjonalnym są zakodowane szesnastkowo. Aby uzyskać klucz publiczny zakodowany w standardzie Base64 w nieskompresowanym formacie ze zdefiniowanym separatorem, użyj tego polecenia:
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
To polecenie tworzy plik publicKey.txt
zawierający wersję klucza w standardzie base64 w nieskompresowanym formacie ze zdefiniowanym separatorem. Jego zawartość przypomina poniższą:
BOdoXP+9Aq473SnGwg3JU1aiNpsd9vH2ognq4PtDtlLGa3Kj8TPf+jaQNPyDSkh3JUhiS0KyrrlWhAgNZKHYF2Y=
Plik nie może zawierać żadnych dodatkowych spacji ani znaków przejścia do nowej linii. Aby to sprawdzić, uruchom to polecenie w systemie Linux lub MacOS:
od -bc publicKey.txt
Krok 3. Wygeneruj klucz prywatny zakodowany w standardzie base64 w formacie PKCS8
Biblioteka Tink wymaga, aby klucz prywatny był zakodowany w standardzie base64 w formacie PKCS8. Aby wygenerować klucz prywatny w tym formacie z klucza prywatnego wygenerowanego w pierwszym kroku, użyj tego polecenia:
openssl pkcs8 -topk8 -inform PEM -outform DER -in key.pem -nocrypt | base64 | paste -sd "\0" -
Polecenie wyświetli dane wyjściowe podobne do tych poniżej:
MIGHAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBG0wawIBAQQgWV4oK8c/MZkCLk4qSCNjW0Zm6H0CBCtSYxkXkC9FBHehRANCAAQPldOnhO2/oXjdJD1dwlFPiNs6fcdoRgFu3/Z0iKj24SjTGyLRGAtYWLGXBZcDdPj3T2bJRHRVhE8Bc2AjkT7n
Jak odszyfrować token formy płatności
Aby odszyfrować token, wykonaj te czynności:
- Użyj klucza prywatnego i podanego
ephemeralPublicKey
, aby uzyskać 512-bitowy klucz wspólny, który korzysta z ECIES-KEM. Użyj tych parametrów: - Krzywa eliptyczna: NIST P-256 znana w OpenSSL jako prime256v1.
CheckMode
,OldCofactorMode
,SingleHashMode
iCofactorMode
wynoszą0
.- Funkcja szyfrowania: nieskompresowany format ze zdefiniowanym separatorem.
- Funkcja derywacji klucza: HKDFwithSHA256, zgodnie z opisem w RFC 5869, z następującym parametrem:
- Nie wolno podawać ciągu zaburzającego. Zgodnie z RFC musi to odpowiadać ciągowi zaburzającemu składającemu się z 32 wyzerowanych bajtów.
- Podziel wygenerowany klucz na dwa 256-bitowe klucze:
symmetricEncryptionKey
imacKey
. Sprawdź, czy pole
tag
to prawidłowa wartość MAC dlaencryptedMessage
.Aby wygenerować oczekiwany MAC, użyj HMAC (RFC 5869) z funkcją skrótu SHA256 i
macKey
uzyskanym w kroku 2.Odszyfruj
encryptedMessage
za pomocą trybu AES-256-CTR oraz:- z zerowym IV,
- bez dopełniania,
- z
symmetricEncryptionKey
uzyskanym z derywacji w kroku 2.
Zarządzanie kluczami
Klucze szyfrowania dla sprzedawców
Sprzedawcy generują klucz publiczny zgodnie ze specyfikacją schematu szyfrowania.
Główne klucze podpisywania Google
Google publikuje zestaw aktualnie ważnych publicznych kluczy podpisywania, które można pobrać z publicznego adresu URL. Klucze są ważne tak długo, jak wskazują to nagłówki pamięci podręcznej HTTP zwracane przez adres URL. Klucze są przechowywane w pamięci podręcznej, dopóki nie stracą ważności. Termin utraty ważności jest określony w polu keyExpiration. Zalecamy, aby po utracie ważności przez klucze jeszcze raz pobrać je z publicznego adresu URL i w tej sposób otrzymać listę aktualnych kluczy.
Wyjątek dla protokołu ECv2: jeśli nie możesz pobierać kluczy od Google na bieżąco, pobierz plik keys.json
z naszego produkcyjnego adresu URL, zapisz go w systemie i okresowo odświeżaj ręcznie. W normalnych okolicznościach Google wydaje nowy główny klucz podpisywania dla ECv2 pięć lat przed wygaśnięciem klucza o najpóźniejszej dacie wygaśnięcia. W przypadku przejęcia kluczy Google wykorzystuje dane kontaktowe podane w portalu samoobsługowym, by powiadomić wszystkich sprzedawców o konieczności wcześniejszego przeładowania pliku keys.json
. Aby zapewnić regularną rotację kluczy, zalecamy, by sprzedawcy, którzy zdecydują się zapisać klucze Google w pliku keys.json
, odświeżali je przy okazji własnej corocznej rotacji kluczy.
Klucze dostępne pod publicznym adresem URL są zmapowane w poniższym formacie:
{ "keys": [ { "keyValue": "encoded public key", "protocolVersion": "ECv2" "keyExpiration":"2000000000000" }, { "keyValue": "encoded public key", "protocolVersion": "ECv2" "keyExpiration":"3000000000000" } ] }
keyValue
to wersja klucza w base64, bez zawijania wierszy lub dopełniania, zakodowanego w ASN.1 o typie SubjectPublicKeyInfo
zdefiniowanym w standardzie X.509. W Javie to kodowanie ASN.1 jest reprezentowane przez klasę X509EncodedKeySpec.
Można je uzyskać za pomocą ECPublicKey.getEncoded()
.
Oto adresy URL środowiska testowego i produkcyjnego:
- Testowanie:
https://payments.developers.google.com/paymentmethodtoken/test/keys.json
- Produkcja:
https://payments.developers.google.com/paymentmethodtoken/keys.json
Rotacja kluczy
Jeśli token formy płatności jest odszyfrowywany bezpośrednio na Twoich serwerach z bezpośrednią integracją, musisz raz w roku dokonać rotacji kluczy.
Aby dokonać rotacji kluczy szyfrowania, wykonaj te czynności:
- Wygeneruj nową parę kluczy przy użyciu OpenSSL.
- Otwórz Konsolę Google Pay i Portfela Google po zalogowaniu się na konto Google użyte przez Ciebie podczas rejestrowania się jako deweloper Google Pay.
- Na karcie Google Pay API w panelu Integracja bezpośrednia kliknij Zarządzaj obok istniejącego klucza publicznego. Kliknij Dodaj kolejny klucz.
- Wybierz pole do wprowadzania tekstu Publiczny klucz szyfrowania i dodaj nowo wygenerowany klucz publiczny zakodowany w standardzie base64 w nieskompresowanym formacie ze zdefiniowanym separatorem.
- Kliknij Zapisz klucze szyfrowania.
Jeśli chcesz, aby rotacja przebiegła bezproblemowo, w okresie przenoszenia kluczy podczas odszyfrowywania powinny być obsługiwane nowe i stare klucze prywatne.
Jeżeli do odszyfrowywania tokena używasz biblioteki Tink, skorzystaj z poniższego kodu w Javie, by umożliwić obsługę wielu kluczy prywatnych:
String decryptedMessage = new PaymentMethodTokenRecipient.Builder() .addRecipientPrivateKey(newPrivateKey) .addRecipientPrivateKey(oldPrivateKey);
Pamiętaj, aby wdrożyć kod do odszyfrowania w środowisku produkcyjnym oraz monitorować udane próby odszyfrowania.
Zmień klucz publiczny użyty w kodzie.
Zamień wartość atrybutu
publicKey
we właściwościPaymentMethodTokenizationSpecification
parameters
:const tokenizationSpecification = { "type": "DIRECT", "parameters": { "protocolVersion": "ECv2", "publicKey": "BOdoXP1aiNp.....kh3JUhiSZKHYF2Y=" } }
- Wdróż kod z kroku 4 w środowisku produkcyjnym. Gdy to zrobisz, w transakcjach szyfrowania i odszyfrowywania będą używane nowe pary kluczy.
Upewnij się, że transakcje nie są już szyfrowane przy użyciu nowego klucza publicznego.
- Usuń stary klucz prywatny.
- Otwórz Konsolę Google Pay i Portfela Google po zalogowaniu się na konto Google użyte przez Ciebie podczas rejestrowania się jako deweloper Google Pay.
- Na karcie Google Pay API w panelu Integracja bezpośrednia kliknij Zarządzaj obok istniejącego klucza publicznego. Kliknij Usuń obok starego klucza publicznego i wybierz Zapisz klucze szyfrowania.
Google używa klucza wskazanego we właściwości publicKey
w obiekcie PaymentMethodTokenizationSpecification
parameters
, jak w tym przykładzie:
{ "protocolVersion": "ECv2", "publicKey": "BOdoXP+9Aq473SnGwg3JU1..." }
Korzystanie z biblioteki Tink do zarządzania zaszyfrowaną odpowiedzią
Aby zweryfikować podpis i odszyfrować wiadomość, wykorzystaj bibliotekę kryptograficzną Tink. Aby przeprowadzić integrację z Tink i zweryfikować/odszyfrować dane, wykonaj te czynności:
W
pom.xml
dodaj aplikację Tinkpaymentmethodtoken
jako zależność:<dependencies> <!-- other dependencies ... --> <dependency> <groupId>com.google.crypto.tink</groupId> <artifactId>apps-paymentmethodtoken</artifactId> <version>1.2.0</version> <!-- or latest version --> </dependency> </dependencies>
Podczas włączania serwera pobierz z wyprzedzeniem klucze podpisywania Google, aby udostępnić je w pamięci. Zapobiega to opóźnieniom sieciowym u użytkowników, które byłyby spowodowane pobieraniem kluczy podczas procesu odszyfrowywania.
GooglePaymentsPublicKeysManager.INSTANCE_PRODUCTION.refreshInBackground();
Odszyfruj klucze przy użyciu kodu podanego poniżej, który zakłada, że
paymentMethodToken
jest przechowywany w zmiennejencryptedMessage
. Zastąp pogrubione sekcje zgodnie ze swoimi potrzebami.Jeśli testujesz środowisko, zamień
INSTANCE_PRODUCTION
naINSTANCE_TEST
, a [YOUR MERCHANT ID] na12345678901234567890
.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);
Zastąp PrivateKey1 i PrivateKey2 swoimi własnymi kluczami. Zmienne mogą mieć formę ciągu znaków w formacie PKCS8 zakodowanego w Base64 lub obiektu
ECPrivateKey
. Jeśli chcesz dowiedzieć więcej o tym, jak utworzyć klucz prywatny w formacie PKCS8 zakodowany w Base64, przeczytaj informacje na temat korzystania z OpenSSL do generowania pary kluczy.Jeśli nie możesz łączyć się z serwerem Google przy każdym odszyfrowywaniu kluczy, odszyfruj je przy użyciu kodu podanego poniżej. Zastąp pogrubione sekcje zgodnie ze swoimi potrzebami.
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);
Obecny klucz dostępny w środowisku produkcyjnym obowiązuje do 14.04.2038 r. w normalnych okolicznościach, chyba że dojdzie do przejęcia kluczy. W przypadku przejęcia kluczy Google wykorzystuje dane kontaktowe podane w portalu samoobsługowym, by powiadomić wszystkich sprzedawców o konieczności wcześniejszego przeładowania pliku
keys.json
.Możesz się skupić na przetwarzaniu ładunku, bo fragment kodu zajmuje się następującymi kwestiami dotyczącymi zabezpieczeń:
- pobieraniem kluczy podpisywania Google i zapisywaniem ich w pamięci podręcznej,
- weryfikowaniem podpisu,
- odszyfrowywaniem.