Kryptografia danych płatności dla sprzedawców

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:

  1. Pobrać główne klucze podpisywania Google.
  2. Przy użyciu dowolnego z działających głównych kluczy podpisywania sprawdzić, czy podpis pośredniego klucza podpisywania jest prawidłowy.
  3. Sprawdzić, czy pośredni klucz podpisywania ładunku nie wygasł.
  4. Przy użyciu pośredniego klucza podpisywania sprawdzić, czy podpis ładunku jest prawidłowy.
  5. Po sprawdzeniu podpisu odszyfrować zawartość ładunku.
  6. Sprawdzić, czy wiadomość nie wygasła. Wymaga to sprawdzenia, czy obecny czas wypada przed czasem w polu messageExpiration w odszyfrowanej zawartości.
  7. Użyć formy płatności w odszyfrowanej zawartości i ją obciążyć.

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 stworzona 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 ciąg znaków zawierający encryptedMessage, ephemeralPublicKey i 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 PaymentMethodTokenizationSpecification przy użyciu Androida lub przeglądarki.

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 = styczeń, 2 = 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ą.

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
      },      "messageId": "some-message-id",
      "messageExpiration": "1577862000000"
    }
    

CRYPTOGRAM_3DS

CARD z uwierzytelnieniem przy użyciu kryptogramu 3-D Secure (CRYPTOGRAM_3DS authMethod). Zawiera 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) 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 specyfikacjiI ETF. 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, wtedy signedString wygląda jak w tym przykładzie:

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

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. Sprzedawcy z dostępem produkcyjnym mają dostęp do merchantId w profilu dewelopera w Google Pay.

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. Klucz intermediateSigningKey.signedKey.keyValue zweryfikowany w poprzednim kroku jest używany do zweryfikowania signedMessage. Zdecydowanie zalecamy skorzystanie z istniejącej biblioteki kryptograficznej zamiast implementowania 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.

  • Krzywa eliptyczna: NIST P-256 (w OpenSSL znana jako prime256v1).
  • CheckMode, OldCofactorMode, SingleHashMode i CofactorMode wynoszą 0.
  • Format ze zdefiniowanym separatorem jest nieskompresowany.
Funkcja derywacji klucza

Oparta na HMAC z SHA-256 (HKDFwithSHA256).

  • Nie wolno podawać ciągu zaburzającego.
  • Informacje muszą być zakodowane przez Google w ASCII dla protokołu w wersji ECv2.
  • Dla klucza AES256 należy wyprowadzić 256 bitów i kolejne 256 bitów dla klucza HMAC_SHA256.
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 wykorzystujący 256-bitowy klucz pozyskany 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. Format składa się z dwóch 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

Aby wyświetlić klucz prywatny i publiczny, użyj tego polecenia:

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

Polecenie powinno 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

Klucz prywatny i klucz publiczny wygenerowane w przykładzie powyżej 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" -
    

Sprawdź, czy plik klucza publicznego nie zawiera żadnych dodatkowych spacji ani znaków przejścia do nowej linii. Dodatkowe spacje mogą nie być widoczne w Vi, dlatego sprawdź plik w edytorze tekstu. Polecenie wyświetli dane wyjściowe podobne do tych poniżej, które są wersją klucza w standardzie Base64 w nieskompresowanym formacie ze zdefiniowanym separatorem:

    BOdoXP+9Aq473SnGwg3JU1aiNpsd9vH2ognq4PtDtlLGa3Kj8TPf+jaQNPyDSkh3JUhiS0KyrrlWhAgNZKHYF2Y=
    

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

Oto instrukcje odszyfrowywania tokena:

  1. 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 (w OpenSSL znana jako prime256v1).
    • CheckMode, OldCofactorMode, SingleHashMode i CofactorMode 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.
  2. Podziel wygenerowany klucz na dwa klucze 256-bitowe: symmetricEncryptionKey i macKey.
  3. Sprawdź, czy pole tag to prawidłowa wartość MAC dla encryptedMessage.

    Aby wygenerować oczekiwany MAC, użyj HMAC (RFC 5869) z funkcją skrótu SHA256 i kluczem macKey uzyskanym w kroku 2.

  4. Odszyfruj encryptedMessage za pomocą trybu AES-256-CTR:

    • 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 ten 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 swoim 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 keys.json. Aby zapewnić regularną rotację kluczy, zalecamy, by sprzedawcy, którzy zdecydują się zapisać klucze Google w zawartości 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:

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:

  1. Wygeneruj nową parę kluczy przy użyciu OpenSSL.
  2. Otwórz swój profil dewelopera Google Pay po zalogowaniu się na konto Google użyte przez Ciebie podczas rejestrowania się jako deweloper Google Pay. W sekcji „Publiczne klucze szyfrowania” wybierz przycisk Dodaj klucz szyfrowania. Następnie 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.
  3. Jeśli chcesz, aby rotacja przebiegła bezproblemowo, w okresie przenoszenia kluczy podczas odszyfrowywania powinien być obsługiwany zarówno nowy, jak i stary klucz prywatny.

    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.

  4. Zmień klucz publiczny użyty w kodzie.

    Zamień wartość atrybutu publicKey we właściwości PaymentMethodTokenizationSpecification parameters:

        const tokenizationSpecification = {
          "type": "DIRECT",
          "parameters": {
            "protocolVersion": "ECv2",
            "publicKey": "BOdoXP1aiNp.....kh3JUhiSZKHYF2Y="
          }
        }
  5. Wdróż kod z kroku 4 w środowisku produkcyjnym. Gdy to zrobisz, w transakcjach szyfrowania i odszyfrowywania powinny być używane nowe pary kluczy.
  6. Upewnij się, że transakcje nie są już szyfrowane przy użyciu starego klucza publicznego.

  7. Usuń stary klucz prywatny.
  8. Usuń stary klucz publiczny z sekcji „Publiczne klucze szyfrowania” w profilu dewelopera Google Pay i kliknij Zapisz.

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, postępuj zgodnie z instrukcją:

  • W pom.xml dodaj aplikację Tink paymentmethodtoken jako zależność:

        <dependencies>
          <!-- other dependencies ... -->
          <dependency>
            <groupId>com.google.crypto.tink</groupId>
            <artifactId>apps-paymentmethodtoken</artifactId>
            <version>1.2.0</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, gdy klucze są pobierane 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 zmiennej encryptedMessage. Zastąp pogrubione sekcje zgodnie ze swoimi potrzebami.

    Jeśli testujesz środowisko, zamień INSTANCE_PRODUCTION na INSTANCE_TEST, a Twój identyfikator sprzedawcy oznaczony jako [YOUR MERCHANT ID] na 12345678901234567890.

        String decryptedMessage =
            new PaymentMethodTokenRecipient.Builder()
            .fetchSenderVerifyingKeysWith(
                GooglePaymentsPublicKeysManager.INSTANCE_PRODUCTION)
            .recipientId("merchant:YOUR_MERCHANT_ID")
            // This guide applies only to protocolVersion = ECv2
            .protocolVersion("ECv2")
            // Multiple private keys can be added to support graceful
            // key rotations.
            .addRecipientPrivateKey(PrivateKey1)
            .addRecipientPrivateKey(PrivateKey2)
            .build()
            .unseal(encryptedMessage);
        

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