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 Primitiven verwendet werden und welches Format erwartet wird.

In diesem Leitfaden erfahren Sie, wie Sie einen öffentlichen Schlüssel generieren, um ein von Google signiertes und verschlüsseltes Zahlungsmethoden-Token anzufordern. Außerdem werden die Schritte beschrieben, die zum Verifizieren und Entschlüsseln des Tokens erforderlich sind.

Dieser Leitfaden gilt nur für protocolVersion = ECv2.

Da Sie Zahlungskarteninformationen direkt erhalten, muss Ihre App PCI DSS-konform sein und Ihre Server müssen über die erforderliche Infrastruktur verfügen, um die Zahlungsanmeldedaten des Nutzers sicher zu verarbeiten, bevor Sie fortfahren.

In den folgenden Schritten wird beschrieben, was ein Integrator tun muss, um die Nutzlast der Google Pay API ECv2 PaymentMethodToken zu nutzen:

  1. Rufen Sie die Google-Stammsignierschlüssel ab.
  2. Prüfen Sie, ob die Signatur des Zwischensignaturschlüssels mit einem der nicht abgelaufenen Root-Signaturschlü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 mit dem 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 müssen Sie prüfen, ob die aktuelle Zeit kleiner als das Feld messageExpiration im entschlüsselten Inhalt ist.
  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 bis 6 aus.

Tokenstruktur für Zahlungsmethoden

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, unter 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 wird 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. Es wird serialisiert, um die Überprüfung der Signatur des Zwischensignaturschlüssels zu vereinfachen.
signedMessage String Ein als HTML-sicherer String serialisiertes JSON-Objekt, das die 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 Zahlungsmethoden-Token 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\"}"
}

Zwischen-Signierschlüssel

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

Name Typ Beschreibung
signedKey String Eine base64-codierte Nachricht mit der Zahlungsbeschreibung des Schlüssels.
signatures String Prü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, der im ASN.1-Typ codiert ist. SubjectPublicKeyInfo ist im X.509-Standard definiert.
keyExpiration String Datum und Uhrzeit, zu der der Zwischenschlüssel abläuft, 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 dem privaten Schlüssel zugeordnet ist, um die Nachricht im unkomprimierten Punktformat zu verschlüsseln. Weitere Informationen finden Sie unter Format des öffentlichen Verschlüsselungsschlüssels.
tag String Ein base64-codierter MAC von encryptedMessage.

Nachricht verschlüsseln

Das entschlüsselte encryptedMessage ist ein UTF‑8-codiertes, serialisiertes JSON-Objekt. Das JSON 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 Zahlungsanmeldedaten. Derzeit wird nur CARD unterstützt.
paymentMethodDetails Objekt Der Zahlungsberechtigungsnachweis selbst. Das Format dieses Objekts wird durch paymentMethod bestimmt und in den folgenden Tabellen beschrieben.

Infokarte

Die folgenden Attribute bilden die Zahlungsanmeldedaten für die Zahlungsmethode CARD:

Name Typ Beschreibung
pan String Die belastete private 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 *TransactionInfo-Objekt für die Händler-ID an.
  • Der Händler fügt der Anfrage eine tokenUpdateUrl hinzu.
  • Der Nutzer wählt ein tokenisiertes Zahlungsmittel aus (z.B. eine 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. Wird nur für Transaktionen mit authentifizierten Gerätetoken unter Android zurückgegeben (CRYPTOGRAM_3DS). Dieser Wert muss im Zahlungsverarbeitungsablauf übergeben werden.

Das folgende JSON-Snippet ist ein Beispiel für die vollständige encryptedMessage für ein CARD paymentMethod mit einem 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 die eciIndicator für Transaktionen mit authentifizierten Geräte-Tokens (CRYPTOGRAM_3DS) bereit.

Sie müssen den Wert eciIndicator in der Autorisierungstransaktion übergeben, ohne ihn zu ändern oder fest zu codieren. Andernfalls schlägt die Transaktion fehl. In der folgenden Tabelle werden die Werte von eciIndicator aufgeführt.

eciIndicator-Wert Kartennetzwerk Haftungspflichtige 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

Alle anderen ECI-Werte für VISA und Mastercard, die nicht in dieser Tabelle enthalten sind, werden nicht zurückgegeben.

Signaturüberprüfung

Zum Überprüfen der Signaturen, die den Zwischenschlüssel und die Nachrichtensignaturen enthalten, sind die folgenden Elemente erforderlich:

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

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 in der äußersten Ebene der Nachricht enthalten. Es ist mit Base64 im ASN.1-Byteformat codiert. Weitere Informationen zu ASN.1 finden Sie im IETF Tools Appendix A. Die Signatur besteht aus den ECDSA-Ganzzahlen r und s. Weitere Informationen finden Sie unter Algorithmus zur Signaturgenerierung.

Das Folgende ist ein Beispiel für das angegebene ASN.1-Byteformat, das das Standardformat ist, das von JCE-ECDSA-Implementierungen (Java Cryptography Extension) erzeugt wird.

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

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

Um die Signatur des Zwischensignaturschlüssels im Beispiel-Zahlungsmethoden-Token zu validieren, erstellen Sie 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. Die 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 Beispiel-Zahlungsmethoden-Token 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\"}"
}

Der sender_id ist immer Google und der protocol_version ist ECv2.

Wenn sender_id gleich Google ist, 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-Bestätigungsalgorithmus wird verwendet, wenn der signierte String für die Signatur des Zwischensignaturschlüssels zusammengestellt wird. Beim ECv2-Protokoll müssen Sie alle Signaturen in intermediateSigningKey.signatures durchlaufen und jede mit den nicht abgelaufenen Google-Signaturschlüsseln in keys.json validieren. Wenn mindestens eine Signaturvalidierung funktioniert, gilt die Überprüfung als abgeschlossen. Verwenden Sie intermediateSigningKey.signedKey.keyValue später, um die signedStringForMessageSignature zu bestätigen. Google empfiehlt dringend, eine vorhandene kryptografische Bibliothek anstelle eines eigenen Bestätigungscodes zu verwenden.

Byte-String für die Nachrichtensignatur erstellen

Um die Signatur im Beispiel-Zahlungsmethoden-Token zu validieren, erstellen Sie die 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. Beim Erstellen des Byte-Strings darf signedMessage nicht geparst oder geändert werden. Ersetzen Sie beispielsweise \u003d nicht durch das Zeichen =.

Beispiel

Das folgende Beispiel zeigt ein Beispiel für ein Zahlungsmethoden-Token:

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

Der sender_id ist immer Google und der recipient_id ist merchant:merchantId. Die merchantId entspricht dem Wert, der in der Google Pay & Wallet Console für Händler mit Produktionszugriff zu finden ist.

Wenn sender_id Google und recipient_id merchant:12345 ist, wird 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 für signedStringForMessageSignature überprüfen

Der standardmäßige ECDSA-Bestätigungsalgorithmus wird verwendet, wenn der signierte String zusammengestellt wird. Die im vorherigen Schritt bestätigte intermediateSigningKey.signedKey.keyValue wird verwendet, um die signedMessage zu bestätigen. Google empfiehlt dringend, eine vorhandene kryptografische Bibliothek anstelle eines eigenen Bestätigungscodes zu verwenden.

Spezifikation des Verschlüsselungsschemas

Google verwendet das Elliptic Curve Integrated Encryption Scheme (ECIES), um das Zahlungsmethoden-Token zu schützen, das in der Google Pay API-Antwort zurückgegeben wird. Das Verschlüsselungsschema verwendet die folgenden Parameter:

Parameter Definition
Methode zur Schlüsselkapselung

ECIES-KEM gemäß ISO 18033-2.

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

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

DEM2 gemäß ISO 18033-2

Verschlüsselungsalgorithmus: AES-256-CTR mit IV = 0 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 die in Google-Nutzlasten zurückgegebenen ephemeralPublicKey werden mit der Base64-Darstellung des Schlüssels im unkomprimierten Punktformat formatiert. Sie besteht aus den folgenden zwei Elementen:

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

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

Öffentlichen Schlüssel mit OpenSSL generieren

Schritt 1: Privaten Schlüssel generieren

Im folgenden Beispiel wird ein privater Schlüssel für elliptische Kurven 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

Mit dem folgenden Befehl können Sie sowohl den privaten als auch den öffentlichen Schlüssel aufrufen:

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

Der Befehl erzeugt eine Ausgabe, die in etwa so aussieht:

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 werden, sind hexadezimal codiert. Verwenden Sie den folgenden Befehl, um einen base64-codierten öffentlichen Schlüssel im unkomprimierten Punktformat zu erhalten:

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

Mit dem Befehl wird eine Datei publicKey.txt erstellt, deren Inhalt, die Base64-Version des Schlüssels im unkomprimierten Punktformat, dem folgenden ähnelt:

BOdoXP+9Aq473SnGwg3JU1aiNpsd9vH2ognq4PtDtlLGa3Kj8TPf+jaQNPyDSkh3JUhiS0KyrrlWhAgNZKHYF2Y=

Der Dateiinhalt darf keine zusätzlichen Leerzeichen oder Zeilenumbrüche enthalten. Führen Sie den folgenden Befehl unter Linux oder MacOS aus, um dies zu überprüfen:

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

Der Befehl erzeugt eine Ausgabe, die in etwa so aussieht:

MIGHAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBG0wawIBAQQgWV4oK8c/MZkCLk4qSCNjW0Zm6H0CBCtSYxkXkC9FBHehRANCAAQPldOnhO2/oXjdJD1dwlFPiNs6fcdoRgFu3/Z0iKj24SjTGyLRGAtYWLGXBZcDdPj3T2bJRHRVhE8Bc2AjkT7n

Zahlungsmethoden-Token entschlüsseln

So entschlüsseln Sie das Token:

  1. Verwenden Sie Ihren privaten Schlüssel und die angegebene ephemeralPublicKey, um einen 512 Bit langen geteilten Schlüssel abzuleiten, der ECIES-KEM verwendet. Verwenden Sie die folgenden Parameter:
    • Elliptische Kurve: NIST P-256, in OpenSSL auch als prime256v1 bekannt.
    • CheckMode, OldCofactorMode, SingleHashMode und CofactorMode sind 0.
    • Codierungsfunktion: Unkomprimiertes Punktformat.
    • Schlüsselableitungsfunktion: HKDFwithSHA256, wie in RFC 5869 beschrieben, mit dem folgenden Parameter:
      • Es darf kein Salt angegeben werden. Gemäß RFC muss dies einem Salt von 32 Nullbytes 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 eine gültige MAC für encryptedMessage ist.

    Verwenden Sie HMAC (RFC 5869) mit der Hash-Funktion SHA256 und dem in Schritt 2 abgerufenen macKey, um den erwarteten MAC zu generieren.

  4. Entschlüsseln Sie encryptedMessage mit dem AES‑256-CTR-Modus und den folgenden Parametern:

    • Eine IV von null.
    • Nicht aufgefüllt.
    • Die in Schritt 2 abgeleitete symmetricEncryptionKey.

Schlüsselverwaltung

Händler-Verschlüsselungsschlüssel

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

Google-Root-Signierschlü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 die von der URL zurückgegebenen HTTP-Cache-Header angeben. Sie werden im Cache gespeichert, bis sie ablaufen. Das Ablaufdatum wird durch das Feld keyExpiration bestimmt. Wir empfehlen, die Schlüssel nach Ablauf eines Abrufs noch einmal über die öffentliche URL abzurufen, um die aktuelle Liste der gültigen Schlüssel zu erhalten.

Ausnahme für das ECv2-Protokoll: Wenn Sie die Schlüssel zur Laufzeit nicht von Google abrufen können, rufen Sie den keys.json von unserer Produktions-URL ab, speichern Sie ihn in Ihrem System und aktualisieren Sie ihn regelmäßig manuell. Unter normalen Umständen gibt Google fünf Jahre vor Ablauf des Schlüssels mit dem längsten Ablaufdatum einen neuen Root-Signierschlüssel für ECv2 aus. Im Falle von Schlüsselkompromittierungen benachrichtigt Google alle Händler über die im Self-Service-Portal angegebenen Kontaktdaten, um eine schnellere Neuaufladung von keys.json zu veranlassen. Damit Sie die regelmäßige Rotation nicht verpassen, empfehlen wir Händlern, die Google-Schlüssel im Inhalt von keys.json speichern, diese jährlich im Rahmen ihrer eigenen jährlichen Schlüsselrotation 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"
    }
  ]
}

keyValue ist eine Base64-Version des Schlüssels, die nicht umbrochen oder aufgefüllt ist und 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 Sie ein Zahlungsmethoden-Token mit direkter Integration direkt auf Ihren Servern entschlüsseln, müssen Sie die Schlüssel jährlich rotieren.

So rotieren Sie Verschlüsselungsschlüssel:

  1. OpenSSL zum Generieren eines neuen Schlüsselpaars verwenden
  2. Öffnen Sie die Google Pay & Wallet Console, während Sie mit dem Google-Konto wurde zuvor zum Verwalten Ihrer App bei Google Play verwendet.
  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 base64-codiert im unkomprimierten Punktformat hinzu.
  5. Klicken Sie auf Verschlüsselungsschlüssel speichern.
  6. Um eine nahtlose Schlüsselrotation zu gewährleisten, müssen Sie während der Umstellung der Schlüssel die Entschlüsselung mit dem neuen und dem alten privaten Schlüssel unterstützen.

    Wenn Sie die Tink-Bibliothek zum Entschlüsseln des Tokens verwenden, verwenden Sie den folgenden Java-Code, um mehrere private Schlüssel zu 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 überwachen.

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

    Ersetzen Sie den Wert des Attributs publicKey in der Property 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 Produktionsumgebung bereit. Sobald der Code bereitgestellt wurde, werden für Ver- und Entschlüsselungsvorgänge die neuen Schlüsselpaare verwendet.
  9. Bestätigen Sie, dass der alte öffentliche Schlüssel nicht mehr zum Verschlüsseln von Transaktionen verwendet wird.

  10. Entfernen Sie den alten privaten Schlüssel.
  11. Öffnen Sie die Google Pay & Wallet Console, während Sie mit dem Google-Konto angemeldet sind, mit dem Sie sich zuvor als Entwickler bei Google Pay registriert haben.
  12. Klicken Sie auf dem Tab Google Pay API im Bereich Direkte Integration neben Ihrem vorhandenen öffentlichen Schlüssel auf Verwalten. Klicken Sie neben Ihrem 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..."
}

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

Verwenden Sie die Tink-Bibliothek für paymentmethodtoken, um die Signatur zu überprüfen und die Nachricht zu entschlüsseln. Diese Bibliothek ist nur in Java verfügbar. Führen Sie dazu die folgenden Schritte aus:

  1. Fügen Sie in Ihrer pom.xml die Tink-App paymentmethodtoken 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. Beim Start des Servers werden die Google-Signaturschlüssel vorab abgerufen, damit der Schlüssel im Arbeitsspeicher verfügbar ist. So wird verhindert, dass Nutzer Netzwerk-Latenzzeiten sehen, während beim Entschlüsselungsprozess die Schlüssel abgerufen werden.

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

    Ersetzen Sie für Tests, die nicht in der Produktion ausgeführt werden, INSTANCE_PRODUCTION durch INSTANCE_TEST. Wenn Ihre Integration inaktiv ist oder kein Verschlüsselungsschlüssel konfiguriert ist, ersetzen Sie [YOUR MERCHANT ID] durch .

    • Aktiv
    • DIRECT-Integration aktiviert
    • Verschlüsselungsschlüssel 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 mit dem registrierten öffentlichen Schlüsselwert verknüpft ist, den Sie in Schlüssel vorbereiten und bei Google registrieren angegeben haben. Sie können später mehrere andere Werte für den privaten Schlüssel 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 PKCS8-Privatschlüssels finden Sie unter Schlüssel vorbereiten und bei Google registrieren.

  5. Wenn Sie nicht jedes Mal einen Google-Server aufrufen können, wenn Sie Schlüssel entschlüsseln, verwenden Sie den folgenden Code und ersetzen Sie die fettgedruckten 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 der Schlüssel nicht kompromittiert wird. Bei Schlüsselkompromittierungen benachrichtigt Google alle Händler über die im Self-Service-Portal angegebenen Kontaktdaten, um ein schnelleres Aufladen von keys.json zu veranlassen.

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

    • Von Google abgerufene und im Arbeitsspeicher zwischengespeicherte Signierschlüssel
    • Signaturüberprüfung
    • Entschlüsselung