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 dieser Anleitung erfahren Sie, wie Sie einen öffentlichen Schlüssel generieren, um ein von Google signiertes und verschlüsseltes Zahlungsmethoden-Token anzufordern, und wie Sie das Token überprüfen und entschlüsseln.

Dieser Leitfaden gilt nur für protocolVersion = ECv2.

Da Sie Zahlungsinformationen direkt erhalten, muss Ihre App PCI DSS-konform sein. Außerdem müssen Ihre Server die erforderliche Infrastruktur haben, 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-Stamm-Signierschlü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. Sie 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. Sie wird serialisiert, um die Überprüfung der Signatur des Zwischensignaturschlüssels zu vereinfachen.
signedMessage String Ein JSON-Objekt, das als HTML-sicherer String serialisiert wurde und 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 in 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\"}"
}

Zwischen-Signierschlü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 Prüft, ob der Zwischensignaturschlüssel von Google stammt. Er ist Base64-codiert und wird 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, als 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.

Verschlüsselte Nachricht

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.

Karte

Die folgenden Eigenschaften 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.

PAN_ONLY

Das folgende JSON-Snippet ist ein Beispiel für die vollständige encryptedMessage für eine CARD paymentMethod mit einem 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ätetokens unter Android zurückgegeben (CRYPTOGRAM_3DS). Dieser Wert muss im Zahlungsverarbeitungsablauf weitergegeben werden.

Das folgende JSON-Snippet ist ein Beispiel für das 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"
    
  },
  
  "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 eciIndicator-Wert 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 Verantwortliche 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 Werbenetzwerke 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

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

Um die Signatur des Zwischensignierschlüssels im Beispiel-Zahlungsmethoden-Token zu validieren, 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. Der 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 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-Verifizierungsalgorithmus 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 die 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 in der Google Pay API-Antwort zurückgegebene Zahlungsmethoden-Token zu schützen. Für das Verschlüsselungsschema werden die folgenden Parameter verwendet:

Parameter Definition
Methode zur Schlüsselkapselung

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.
Funktion zur Schlüsselableitung

HMAC-basiert mit SHA-256 (HKDFwithSHA256).

  • Salz darf nicht bereitgestellt werden.
  • Informationen müssen für die Protokollversion ECv2 in ASCII-Codierung von Google vorliegen.
  • 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 Auffüllung.

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

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 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 Elliptic Curve-Schlüssel 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

Die Befehlsausgabe 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 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 publicKey.txt-Datei 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 unter Linux oder MacOS den folgenden Befehl 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" -

Die Befehlsausgabe 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 bekannt.
    • CheckMode, OldCofactorMode, SingleHashMode und CofactorMode sind 0.
    • Codierungsfunktion: Unkomprimiertes Punktformat.
    • Schlüsselableitungsfunktion: HKDFwithSHA256, wie in RFC 5869 beschrieben, mit dem folgenden Parameter:
      • Salz darf nicht bereitgestellt 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 zum Generieren des erwarteten MAC HMAC (RFC 5869) mit der Hash-Funktion SHA256 und dem in Schritt 2 abgerufenen macKey.

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

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

Schlüsselverwaltung

Händlerverschlü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 aktuell 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 HTTP-Cache-Header, die von der URL zurückgegeben werden, 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 die keys.json von unserer Produktions-URL ab, speichern Sie sie in Ihrem System und aktualisieren Sie sie 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-Signaturschlüssel für ECv2 aus. Bei 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 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-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. Neues Schlüsselpaar mit OpenSSL generieren
  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, 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 überwachen.

  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 Produktionsumgebung bereit. Sobald der Code bereitgestellt wurde, werden für Verschlüsselungs- und Entschlüsselungsvorgänge die neuen Schlüsselpaare verwendet.
  9. Prüfen Sie, ob 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 und melden Sie sich mit dem Google-Konto an, 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 in der Eigenschaft 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 für Zahlungsmethodentokens, um die Signatur zu prü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 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 Serverstart werden die Google-Signaturschlüssel vorab abgerufen, damit der Schlüssel im Arbeitsspeicher verfügbar ist. So wird verhindert, dass Nutzer eine Netzwerk-Latenz bemerken, während beim Entschlüsselungsvorgang 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 fett formatierten Abschnitte entsprechend Ihrem Szenario.

    Ersetzen Sie für Nicht-Produktionstests INSTANCE_PRODUCTION durch INSTANCE_TEST. Wenn Ihre Integration inaktiv ist oder kein Verschlüsselungsschlüssel konfiguriert ist, ersetzen Sie [YOUR MERCHANT ID] durch .

    • Aktiv
    • Direkte 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 dem registrierten öffentlichen Schlüsselwert bei Google aus Schlüssel vorbereiten und bei Google registrieren zugeordnet ist. Sie können später weitere Werte für private 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 privaten PKCS8-Schlü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 Neuladen von keys.json zu veranlassen.

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

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