Verschlüsselung von Zahlungsdaten für Händler

Die Google Pay API gibt Zahlungsmethoden in einer signierten und verschlüsselten PaymentMethodToken-Nutzlast zurück. Die zurückgegebenen Zahlungsmethoden sind entweder Karten, die aus einer PAN bestehen, oder tokenisierte Karten, die aus einer Geräte-PAN und Kryptogrammen bestehen.

Die Nutzlast enthält ein Feld namens protocolVersion, das dem Empfänger der Nutzlast mitteilt, welche kryptografischen Primitive verwendet werden und welches Format erwartet wird.

In diesem Leitfaden wird beschrieben, wie du einen öffentlichen Schlüssel generierst, um ein von Google signiertes und verschlüsseltes Zahlungsmethoden-Token anzufordern. Außerdem werden die Schritte zum Bestätigen und Entschlüsseln des Tokens erläutert.

Dieser Leitfaden gilt nur für protocolVersion = ECv2.

Da du Zahlungskarteninformationen direkt erhältst, musst du dafür sorgen, dass deine App PCI-DSS-konform ist und deine Server die erforderliche Infrastruktur haben, um die Zahlungsdaten des Nutzers sicher zu verarbeiten, bevor du fortfährst.

Im Folgenden wird beschrieben, was ein Integrator tun muss, um die Nutzlast ECv2 PaymentMethodToken der Google Pay API zu nutzen:

  1. Rufen Sie die Google-Stammsignaturschlüssel ab.
  2. Prüfe, ob die Signatur des Zwischensignaturschlüssels durch einen der nicht abgelaufenen Stammsignaturschlüssel gültig ist.
  3. Prüfen Sie, ob der Zwischensignaturschlüssel der Nutzlast abgelaufen ist.
  4. Prüfen Sie, ob die Signatur der Nutzlast durch den Zwischensignaturschlüssel gültig ist.
  5. Entschlüsseln Sie den Inhalt der Nutzlast, nachdem Sie die Signatur überprüft haben.
  6. Prüfen Sie, ob die Nachricht abgelaufen ist. Dazu musst du prüfen, ob die aktuelle Zeit vor der Zeit im Feld messageExpiration im entschlüsselten Inhalt liegt.
  7. Verwenden Sie die Zahlungsmethode in den entschlüsselten Inhalten und belasten Sie sie.

Der Beispielcode in unserer Tink-Bibliothek führt die Schritte 1–6 aus.

Struktur des Zahlungsmethoden-Tokens

Die von Google in der PaymentData-Antwort zurückgegebene Nachricht ist ein UTF-8-codiertes, serialisiertes JSON-Objekt mit den in der folgenden Tabelle angegebenen Schlüsseln:

Name Typ Beschreibung
protocolVersion String Gibt das Verschlüsselungs- oder Signaturschema an, mit dem die Nachricht erstellt wird. So kann das Protokoll bei Bedarf im Laufe der Zeit weiterentwickelt werden.
signature String Bestätigt, dass die Nachricht von Google stammt. Er ist base64-codiert und wurde mit ECDSA vom Zwischensignaturschlüssel erstellt.
intermediateSigningKey Objekt Ein JSON-Objekt, das den Zwischensignaturschlüssel von Google enthält. Sie enthält die signedKey mit keyValue, keyExpiration und signatures. Sie wird serialisiert, um die Signaturprüfung des Zwischensignaturschlüssels zu vereinfachen.
signedMessage String Ein JSON-Objekt, das als HTML-sicherer String serialisiert ist und encryptedMessage, ephemeralPublicKey und tag enthält. Sie wird serialisiert, um die Signaturüberprüfung zu vereinfachen.

Beispiel

Im Folgenden finden Sie eine Antwort mit einem Zahlungsmethodentoken im JSON-Format:

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

Zwischensignaturschlüssel

intermediateSigningKey ist ein UTF-8-codiertes, serialisiertes JSON-Objekt, das die folgenden Werte enthält:

Name Typ Beschreibung
signedKey String Eine base64-codierte Nachricht, die die Zahlungsbeschreibung des Schlüssels enthält.
signatures String Überprüft, ob der Zwischensignaturschlüssel von Google stammt. Er ist base64-codiert und wurde mit ECDSA erstellt.

Signierter Schlüssel

signedKey ist ein UTF-8-codiertes, serialisiertes JSON-Objekt, das die folgenden Werte enthält:

Name Typ Beschreibung
keyValue String Eine base64-Version des Schlüssels, codiert im ASN.1-Typ. SubjectPublicKeyInfo ist im X.509-Standard definiert.
keyExpiration String Datum und Uhrzeit des Ablaufs des Zwischenschlüssels in UTC-Millisekunden seit der Epoche. Integratoren lehnen alle abgelaufenen Schlüssel ab.

Signierte Nachricht

signedMessage ist ein UTF-8-codiertes, serialisiertes JSON-Objekt, das die folgenden Werte enthält:

Name Typ Beschreibung
encryptedMessage String Eine base64-codierte verschlüsselte Nachricht, die Zahlungsinformationen und einige zusätzliche Sicherheitsfelder enthält.
ephemeralPublicKey String Ein base64-codierter sitzungsspezifischer öffentlicher Schlüssel, der mit dem privaten Schlüssel verknüpft ist, um die Nachricht im unkomprimierten Punktformat zu verschlüsseln. Weitere Informationen finden Sie unter Format des öffentlichen Schlüssels für die Verschlüsselung.
tag String Ein base64-codierter MAC von encryptedMessage.

Nachricht verschlüsseln

Der entschlüsselte encryptedMessage ist ein UTF-8-codiertes, serialisiertes JSON-Objekt. Das JSON-Objekt enthält zwei Ebenen. Die äußere Ebene enthält Metadaten und Felder, die aus Sicherheitsgründen enthalten sind. Die innere Ebene ist ein weiteres JSON-Objekt, das die eigentlichen Zahlungsanmeldedaten darstellt.

Weitere Informationen zu encryptedMessage finden Sie in den folgenden Tabellen und JSON-Objektbeispielen:

Name Typ Beschreibung
messageExpiration String Datum und Uhrzeit, zu der die Nachricht abläuft, in UTC-Millisekunden seit der Epoche. Integratoren sollten alle abgelaufenen Nachrichten ablehnen.
messageId String Eine eindeutige ID, mit der die Nachricht identifiziert wird, falls sie später widerrufen oder gefunden werden muss.
paymentMethod String Der Typ der Zahlungsberechtigung. Derzeit wird nur CARD unterstützt.
paymentMethodDetails Objekt Der Zahlungsberechtigungsnachweis selbst. Das Format dieses Objekts wird durch den paymentMethod bestimmt und in den folgenden Tabellen beschrieben.

Infokarte

Die folgenden Attribute bilden einen Zahlungsberechtigungsnachweis für die Zahlungsmethode CARD:

Name Typ Beschreibung
pan String Die belastete persönliche Kontonummer. Dieser String enthält nur Ziffern.
expirationMonth Zahl Der Ablaufmonat der Karte, wobei 1 für Januar, 2 für Februar usw. steht.
expirationYear Zahl Das vierstellige Ablaufjahr der Karte, z. B. 2020.
authMethod String Die Authentifizierungsmethode der Kartentransaktion.
merchantTokenId String

Die Händler-Token-ID der Kartentransaktion. Dieses optionale Feld wird nur zurückgegeben, wenn alle folgenden Bedingungen erfüllt sind:

  • Der Händler gibt in der Anfrage ein MIT-*TransactionInfo-Objekt an.
  • Der Händler fügt der Anfrage eine tokenUpdateUrl hinzu.
  • Der Nutzer wählt ein tokenisiertes Zahlungsmittel aus (z.B. DPAN).

PAN_ONLY

Das folgende JSON-Snippet ist ein Beispiel für die vollständige encryptedMessage für eine CARD paymentMethod mit einer 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

Eine CARD, die mit einem 3-D Secure-Kryptogramm authentifiziert wurde, CRYPTOGRAM_3DS authMethod. Sie enthält die folgenden zusätzlichen Felder:

Name Typ Beschreibung
cryptogram String Ein 3-D Secure-Kryptogramm.
eciIndicator String Dieser String ist nicht immer vorhanden. Sie wird nur für Transaktionen mit authentifizierten Geräte-Tokens auf Android-Geräten zurückgegeben (CRYPTOGRAM_3DS). Dieser Wert muss im Zahlungsabwicklungsablauf weitergegeben werden.

Das folgende JSON-Snippet ist ein Beispiel für die vollständige encryptedMessage für eine CARD-paymentMethod mit einer CRYPTOGRAM_3DS-authMethod:

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

Das Kartennetzwerk stellt möglicherweise das eciIndicator für authentifizierte Gerätetoken-Transaktionen (CRYPTOGRAM_3DS) bereit.

Du musst den Wert eciIndicator bei der Autorisierungstransaktion übergeben, ohne ihn zu ändern oder hartzucodieren. Andernfalls schlägt die Transaktion fehl. In der folgenden Tabelle werden die Werte von eciIndicator beschrieben.

eciIndicator-Wert Kartennetzwerk Haftende Partei authMethod
""(empty) Mastercard Händler/Acquirer CRYPTOGRAM_3DS
02 Mastercard Kartenaussteller CRYPTOGRAM_3DS
06 Mastercard Händler/Acquirer CRYPTOGRAM_3DS
05 Visa Kartenaussteller CRYPTOGRAM_3DS
07 Visa Händler/Acquirer CRYPTOGRAM_3DS
""(empty) Andere Netzwerke Händler/Acquirer CRYPTOGRAM_3DS

Andere ECI-Werte für VISA und Mastercard, die nicht in dieser Tabelle aufgeführt sind, werden nicht zurückgegeben.

Signaturüberprüfung

Zum Prüfen der Signaturen, einschließlich des Zwischenschlüssels und der Nachrichtensignaturen, sind die folgenden Elemente erforderlich:

  • Der Algorithmus, der zum Erstellen der Signatur verwendet wird
  • Der Byte-String, der zum Erstellen der Signatur verwendet wird
  • Der öffentliche Schlüssel, der dem privaten Schlüssel entspricht, der zum Erstellen der Signatur verwendet wurde
  • Die Signatur selbst

Der Signaturalgorithmus

Google verwendet den Elliptic Curve Digital Signature Algorithm (ECDSA), um die Nachrichten mit den folgenden Parametern zu signieren: ECDSA über NIST P-256 mit SHA-256 als Hash-Funktion, wie in FIPS 186-4 definiert.

Die Signatur

Die Signatur ist auf der äußersten Ebene der Nachricht enthalten. Er ist mit Base64 im ASN.1-Byteformat codiert. Weitere Informationen zu ASN.1 finden Sie im Anhang A der IETF-Tools. Die Signatur besteht aus den ECDSA-Ganzzahlen r und s. Weitere Informationen finden Sie unter Algorithmus zur Signaturgenerierung.

Das folgende Beispiel zeigt das angegebene ASN.1-Byteformat, das das Standardformat ist, das von ECDSA-Implementierungen der Java Cryptography Extension (JCE) erzeugt wird.

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

Byte-String für die Signatur des Zwischensignaturschlüssels erstellen

Wenn Sie die Signatur des Zwischensignaturschlüssels im Beispieltoken für die Zahlungsmethode validieren möchten, erstellen Sie die signedStringForIntermediateSigningKeySignature mit der folgenden Formel:

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

Die Notation „||“ bedeutet „verketten“. Jede Komponente (sender_id, protocolVersion, signedKey) muss UTF-8-codiert sein. signedKey muss der String von intermediateSigningKey.signedKey sein. Die Bytelänge jeder Komponente beträgt 4 Byte im Little-Endian-Format.

Beispiel

In diesem Beispiel wird das folgende Beispieltoken für die Zahlungsmethode verwendet:

{
  "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 ist immer Google und protocol_version ist ECv2.

Wenn sender_id den Wert Google hat, wird signedString wie im folgenden Beispiel angezeigt:

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

Signatur für signedStringForIntermediateSigningKeySignature überprüfen

Der standardmäßige ECDSA-Prüfalgorithmus wird verwendet, wenn der signierte String für die Signatur des Zwischensignaturschlüssels zusammengestellt wird. Für das ECv2-Protokoll müssen Sie alle Signaturen in intermediateSigningKey.signatures durchlaufen und versuchen, jede mit den nicht abgelaufenen Google-Signaturschlüsseln in keys.json zu validieren. Wenn mindestens eine Signaturvalidierung funktioniert, gilt die Überprüfung als abgeschlossen. Verwenden Sie später intermediateSigningKey.signedKey.keyValue, um signedStringForMessageSignature zu prüfen. Google empfiehlt dringend, eine vorhandene kryptografische Bibliothek anstelle Ihres eigenen Bestätigungscodes zu verwenden.

Byte-String für die Nachrichtensignatur erstellen

Wenn Sie die Signatur im Beispiel-Zahlungsmethodentoken validieren möchten, erstellen Sie den signedStringForMessageSignature mit der folgenden Formel:

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

Die Notation „||“ bedeutet „verketten“. Jede Komponente (sender_id, recipient_id, protocolVersion, signedMessage) muss UTF-8-codiert sein. Die Bytelänge jeder Komponente beträgt 4 Byte im Little-Endian-Format. Wenn Sie den Byte-String erstellen, dürfen Sie signedMessage nicht parsen oder ändern. Ersetzen Sie beispielsweise \u003d nicht durch das Zeichen =.

Beispiel

Das folgende Beispiel zeigt ein Beispieltoken für eine Zahlungsmethode:

{
  "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 ist immer Google und recipient_id ist merchant:merchantId. Die merchantId entspricht dem Wert, der in der Google Pay & Wallet Console für Händler mit Produktionszugriff angegeben ist.

Wenn der sender_id Google und der recipient_id merchant:12345 ist, wird der signedString wie im folgenden Beispiel angezeigt:

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

Signatur in „signedStringForMessageSignature“ überprüfen

Der standardmäßige ECDSA-Prüfalgorithmus wird verwendet, wenn der signierte String zusammengestellt wird. Die im vorherigen Schritt verifizierte intermediateSigningKey.signedKey.keyValue wird verwendet, um die signedMessage zu verifizieren. Google empfiehlt dringend, eine vorhandene kryptografische Bibliothek anstelle Ihres eigenen Bestätigungscodes zu verwenden.

Spezifikation des Verschlüsselungsschemas

Google verwendet das Elliptic Curve Integrated Encryption Scheme (ECIES), um das in der Google Pay API-Antwort zurückgegebene Zahlungsmethodentoken zu sichern. Das Verschlüsselungsschema verwendet die folgenden Parameter:

Parameter Definition
Schlüsselkapselungsmethode

ECIES-KEM, wie in ISO 18033-2 definiert.

  • Elliptische Kurve: NIST P-256 (in OpenSSL auch als prime256v1 bezeichnet).
  • CheckMode, OldCofactorMode, SingleHashMode und CofactorMode sind 0.
  • Das Punktformat ist unkomprimiert.
Schlüsselableitungsfunktion

HMAC-basiert mit SHA-256 (HKDFwithSHA256).

  • Salt darf nicht angegeben werden.
  • Die Informationen müssen "Google" sein und für die Protokollversion ECv2 in ASCII codiert werden.
  • Für den Schlüssel AES256 müssen 256 Bit und für den Schlüssel HMAC_SHA256 weitere 256 Bit abgeleitet werden.
Symmetrischer Verschlüsselungsalgorithmus

DEM2, wie in ISO 18033-2 definiert

Verschlüsselungsalgorithmus: AES-256-CTR mit Null-IV und ohne Padding.

MAC-Algorithmus HMAC_SHA256 mit einem 256-Bit-Schlüssel, der von der Schlüsselableitungsfunktion abgeleitet wurde.

Format des öffentlichen Verschlüsselungsschlüssels

Der öffentliche Verschlüsselungsschlüssel und der in Google-Nutzlasten zurückgegebene ephemeralPublicKey sind mit der Base64-Darstellung des Schlüssels im unkomprimierten Punktformat formatiert. Sie besteht aus den folgenden beiden Elementen:

  • Eine magische Zahl, die das Format angibt (0x04).
  • Zwei 32 Byte große Ganzzahlen, die die X- und Y-Koordinaten in der elliptischen Kurve darstellen.

Dieses Format wird ausführlicher in „Public Key Cryptography For The Financial Services Industry: The Elliptic Curve Digital Signature Algorithm (ECDSA)“, ANSI X9.62, 1998 beschrieben.

Öffentlichen Schlüssel mit OpenSSL generieren

Schritt 1: Privaten Schlüssel generieren

Im folgenden Beispiel wird ein privater Schlüssel mit elliptischer Kurve generiert, der für die Verwendung mit NIST P-256 geeignet ist, und in key.pem geschrieben:

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

Optional: Private und öffentliche Schlüssel ansehen

Verwenden Sie den folgenden Befehl, um sowohl den privaten als auch den öffentlichen Schlüssel aufzurufen:

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

Die Ausgabe des Befehls sieht in etwa so aus:

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

Schritt 2: Base64-codierten öffentlichen Schlüssel generieren

Der private und der öffentliche Schlüssel, die im vorherigen optionalen Schritt generiert wurden, sind hexadezimal codiert. Verwenden Sie den folgenden Befehl, um einen base64-codierten öffentlichen Schlüssel im unkomprimierten Punktformat abzurufen:

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

Der Befehl erzeugt eine publicKey.txt-Datei, deren Inhalt, die Base64-Version des Schlüssels im unkomprimierten Punktformat, in etwa so aussieht:

BOdoXP+9Aq473SnGwg3JU1aiNpsd9vH2ognq4PtDtlLGa3Kj8TPf+jaQNPyDSkh3JUhiS0KyrrlWhAgNZKHYF2Y=

Der Dateiinhalt darf keine zusätzlichen Leerzeichen oder Zeilenumbrüche enthalten. Führen Sie dazu unter Linux oder macOS den folgenden Befehl aus:

od -bc publicKey.txt

Schritt 3: Base64-codierten privaten Schlüssel im PKCS #8-Format generieren

Die Tink-Bibliothek erwartet, dass Ihr privater Schlüssel im PKCS #8-Format base64-codiert ist. Verwenden Sie den folgenden Befehl, um den privaten Schlüssel in diesem Format aus dem im ersten Schritt generierten privaten Schlüssel zu generieren:

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

Die Ausgabe des Befehls sieht in etwa so aus:

MIGHAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBG0wawIBAQQgWV4oK8c/MZkCLk4qSCNjW0Zm6H0CBCtSYxkXkC9FBHehRANCAAQPldOnhO2/oXjdJD1dwlFPiNs6fcdoRgFu3/Z0iKj24SjTGyLRGAtYWLGXBZcDdPj3T2bJRHRVhE8Bc2AjkT7n

Zahlungsmethoden-Token entschlüsseln

So entschlüsseln Sie das Token:

  1. Verwenden Sie Ihren privaten Schlüssel und den angegebenen ephemeralPublicKey, um einen 512 Bit langen freigegebenen Schlüssel abzuleiten, der ECIES-KEM verwendet. Verwenden Sie die folgenden Parameter:
    • Elliptische Kurve: NIST P-256, in OpenSSL auch als prime256v1 bezeichnet.
    • CheckMode, OldCofactorMode, SingleHashMode und CofactorMode sind 0.
    • Codierungsfunktion: Unkomprimiertes Punktformat.
    • Schlüsselableitungsfunktion: HKDFwithSHA256, wie in RFC 5869 beschrieben, mit dem folgenden Parameter:
      • Salt darf nicht angegeben werden. Gemäß RFC muss dies einem Salt von 32 Null-Byte entsprechen.
  2. Teilen Sie den generierten Schlüssel in zwei 256-Bit-Schlüssel auf: symmetricEncryptionKey und macKey.
  3. Prüfen Sie, ob das Feld tag ein gültiger MAC für encryptedMessage ist.

    Verwenden Sie zum Generieren des erwarteten MAC HMAC (RFC 5869) mit der Hash-Funktion SHA256 und dem in Schritt 2 erhaltenen macKey.

  4. Entschlüsseln Sie encryptedMessage mit dem AES-256-CTR-Modus und Folgendem:

    • Ein IV von null.
    • Nicht gepolstert.
    • Der in Schritt 2 abgeleitete symmetricEncryptionKey.

Schlüsselverwaltung

Verschlüsselungsschlüssel des Händlers

Händler generieren einen öffentlichen Schlüssel gemäß den Spezifikationen in der Spezifikation des Verschlüsselungsschemas.

Google-Root-Signaturschlüssel

Google veröffentlicht die derzeit gültigen öffentlichen Root-Signaturschlüssel, die über eine öffentliche URL abgerufen werden können. Die Schlüssel sind so lange gültig, wie es die von der URL zurückgegebenen HTTP-Cache-Header angeben. Sie werden im Cache gespeichert, bis sie ablaufen. Dies wird durch das Feld keyExpiration bestimmt. Wir empfehlen, die Schlüssel nach Ablauf des Abrufs noch einmal von der öffentlichen URL abzurufen, um die aktuelle Liste der gültigen Schlüssel zu erhalten.

Ausnahme für das ECv2-Protokoll: Wenn du die Schlüssel nicht zur Laufzeit von Google abrufen kannst, rufe die keys.json von unserer Produktions-URL ab, speichere sie in deinem System und aktualisiere sie regelmäßig manuell. Unter normalen Umständen stellt Google fünf Jahre vor Ablauf des Schlüssels mit dem längsten Ablaufdatum einen neuen Root-Signaturschlüssel für ECv2 aus. Wenn Schlüssel kompromittiert werden, benachrichtigt Google alle Händler über die im Self-Service-Portal angegebenen Kontaktdaten, um ein schnelleres Neuladen von keys.json anzufordern. Damit Sie die regelmäßige Rotation nicht verpassen, empfehlen wir Händlern, die Google-Schlüssel im Inhalt von keys.json speichern, diese im Rahmen ihrer eigenen jährlichen Schlüsselrotation jährlich zu aktualisieren.

Die über die öffentliche URL bereitgestellten Schlüssel werden im folgenden Format zugeordnet:

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

Der keyValue ist eine Base64-codierte, nicht verpackte oder aufgefüllte Version des Schlüssels, der im ASN.1-Typ SubjectPublicKeyInfo codiert ist, der im X.509-Standard definiert ist. In Java wird die referenzierte ASN.1-Codierung durch die X509EncodedKeySpec-Klasse dargestellt. Sie kann mit ECPublicKey.getEncoded() abgerufen werden.

URLs für Test- und Produktionsumgebungen finden Sie unter den folgenden Links:

Schlüsselrotation

Wenn du ein Zahlungsmethoden-Token mit direkter Integration direkt auf deinen Servern entschlüsselst, musst du die Schlüssel jährlich rotieren.

Führen Sie die folgenden Schritte aus, um Verschlüsselungsschlüssel zu rotieren:

  1. Mit OpenSSL ein neues Schlüsselpaar generieren
  2. Öffne die Google Pay & Wallet Console und melde dich mit dem Google-Konto die Sie zuvor zum Verwalten Ihrer App bei Google Play verwendet haben.
  3. Klicken Sie auf dem Tab Google Pay API im Bereich Direkte Integration neben Ihrem vorhandenen öffentlichen Schlüssel auf Verwalten. Klicken Sie auf Weiteren Schlüssel hinzufügen.
  4. Wählen Sie das Texteingabefeld Öffentlicher Verschlüsselungsschlüssel aus und fügen Sie Ihren neu generierten öffentlichen Schlüssel im Base64-codierten, unkomprimierten Punktformat hinzu.
  5. Klicken Sie auf Verschlüsselungsschlüssel speichern.
  6. Damit die Schlüsselrotation reibungslos funktioniert, sollten Sie während der Umstellung sowohl die Entschlüsselung mit dem neuen als auch mit dem alten privaten Schlüssel unterstützen.

    Wenn Sie die Tink-Bibliothek zum Entschlüsseln des Tokens verwenden, können Sie mit dem folgenden Java-Code mehrere private Schlüssel unterstützen:

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

    Achten Sie darauf, dass der Code für die Entschlüsselung in der Produktion bereitgestellt wird und dass Sie erfolgreiche Entschlüsselungen im Blick behalten.

  7. Ändern Sie den in Ihrem Code verwendeten öffentlichen Schlüssel.

    Ersetzen Sie den Wert des Attributs publicKey im Attribut PaymentMethodTokenizationSpecification parameters:

    /**
     * @param publicKey public key retrieved from your server
     */
    private static JSONObject getTokenizationSpecification(String publicKey) {
      JSONObject tokenizationSpecification = new JSONObject();
      tokenizationSpecification.put("type", "DIRECT");
      tokenizationSpecification.put(
        "parameters",
        new JSONObject()
            .put("protocolVersion", "ECv2")
            .put("publicKey", publicKey));
      return tokenizationSpecification;
    }
  8. Stellen Sie den Code aus Schritt 4 in der Produktion bereit. Sobald der Code bereitgestellt ist, werden für Verschlüsselungs- und Entschlüsselungstransaktionen die neuen Schlüsselpaare verwendet.
  9. Prüfe, ob der alte öffentliche Schlüssel nicht mehr zum Verschlüsseln von Transaktionen verwendet wird.

  10. Entfernen Sie den alten privaten Schlüssel.
  11. Öffne die Google Pay & Wallet Console und melde dich mit dem Google-Konto an, mit dem du dich zuvor als Entwickler bei Google Pay registriert hast.
  12. Klicken Sie auf dem Tab Google Pay API im Bereich Direkte Integration neben Ihrem vorhandenen öffentlichen Schlüssel auf Verwalten. Klicken Sie neben dem alten öffentlichen Schlüssel auf Löschen und dann auf Verschlüsselungsschlüssel speichern.

Google verwendet den Schlüssel, der im Attribut publicKey im Objekt PaymentMethodTokenizationSpecification parameters angegeben ist, wie im folgenden Beispiel gezeigt:

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

Verwenden Sie die Tink-Bibliothek, um die verschlüsselte Antwort zu verwalten.

Verwenden Sie die Tink-Bibliothek „paymentmethodtoken“, um die Signatur zu überprüfen und die Nachricht zu entschlüsseln. Diese Bibliothek ist nur in Java verfügbar. Gehen Sie dazu so vor:

  1. Fügen Sie in Ihrer pom.xml die Tink-paymentmethodtoken-Anwendung als Abhängigkeit hinzu:

    <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. Rufen Sie beim Serverstart die Google-Signaturschlüssel vorab ab, damit der Schlüssel im Arbeitsspeicher verfügbar ist. Dadurch wird verhindert, dass Nutzer die Netzwerklatenz sehen, während die Schlüssel für den Entschlüsselungsprozess abgerufen werden.

    GooglePaymentsPublicKeysManager.INSTANCE_PRODUCTION.refreshInBackground();
  3. Entschlüsseln Sie die Nachricht mit dem folgenden Code, wobei davon ausgegangen wird, dass paymentMethodToken in der Variablen encryptedMessage gespeichert ist. Ersetzen Sie die fett formatierten Abschnitte entsprechend Ihrem Szenario.

    Ersetzen Sie für Tests außerhalb der Produktion INSTANCE_PRODUCTION durch INSTANCE_TEST. Wenn Ihre Integration inaktiv ist oder kein Verschlüsselungsschlüssel konfiguriert ist, ersetzen Sie [YOUR MERCHANT ID] durch .

    • Aktiv
    • Hat die DIRECT-Integration aktiviert
    • Verschlüsselungsschlüssel ist konfiguriert

    Ersetzen Sie [YOUR MERCHANT ID] nicht.

    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. Ersetzen Sie PrivateKey1 durch den entsprechenden privaten Schlüsselwert, der dem registrierten öffentlichen Schlüsselwert bei Google aus Schlüssel vorbereiten und bei Google registrieren zugeordnet ist. Sie können später mehrere andere private Schlüsselwerte hinzufügen, wenn Sie Schlüssel mit Google rotieren müssen. Die Variablen können entweder ein Base64-codierter PKCS8-String oder ein ECPrivateKey-Objekt sein. Weitere Informationen zum Erstellen eines Base64-codierten privaten PKCS8-Schlüssels finden Sie unter Schlüssel vorbereiten und bei Google registrieren.

  5. Wenn Sie nicht jedes Mal, wenn Sie Schlüssel entschlüsseln, einen Google-Server aufrufen können, entschlüsseln Sie mit dem folgenden Code und ersetzen Sie die fett formatierten Abschnitte entsprechend Ihrem Szenario.

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

    Der aktuelle Schlüssel in der Produktionsumgebung ist unter normalen Umständen bis zum 14.04.2038 gültig, sofern er nicht kompromittiert wird. Wenn Schlüssel kompromittiert werden, benachrichtigt Google alle Händler über die im Self-Service-Portal angegebenen Kontaktdaten, um ein schnelleres Neuladen von keys.json anzufordern.

    Das Code-Snippet verarbeitet die folgenden Sicherheitsdetails, damit Sie sich auf die Nutzung der Nutzlast konzentrieren können:

    • Google-Signaturschlüssel abgerufen und im Arbeitsspeicher im Cache gespeichert
    • Signaturüberprüfung
    • Entschlüsselung