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:
- Rufen Sie die Google-Stamm-Signierschlüssel ab.
- Prüfen Sie, ob die Signatur des Zwischensignaturschlüssels mit einem der nicht abgelaufenen Root-Signaturschlüssel gültig ist.
- Prüfen Sie, ob der Zwischensignaturschlüssel der Nutzlast abgelaufen ist.
- Prüfen Sie, ob die Signatur der Nutzlast mit dem Zwischensignaturschlüssel gültig ist.
- Entschlüsseln Sie den Inhalt der Nutzlast, nachdem Sie die Signatur überprüft haben.
- Prüfen Sie, ob die Nachricht abgelaufen ist. Dazu müssen Sie prüfen, ob die aktuelle Zeit kleiner als das Feld
messageExpirationim entschlüsselten Inhalt ist. - 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.
|
| Funktion zur Schlüsselableitung | HMAC-basiert mit SHA-256 (
|
| 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:
- 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,SingleHashModeundCofactorModesind0.- 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.
- Teilen Sie den generierten Schlüssel in zwei 256‑Bit-Schlüssel auf:
symmetricEncryptionKeyundmacKey. Prüfen Sie, ob das Feld
tageine gültige MAC fürencryptedMessageist.Verwenden Sie zum Generieren des erwarteten MAC HMAC (RFC 5869) mit der Hash-Funktion SHA256 und dem in Schritt 2 abgerufenen
macKey.Entschlüsseln Sie
encryptedMessagemit 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:
- Test:
https://payments.developers.google.com/paymentmethodtoken/test/keys.json - Produktion:
https://payments.developers.google.com/paymentmethodtoken/keys.json
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:
- Neues Schlüsselpaar mit OpenSSL generieren
- Öffnen Sie die Google Pay & Wallet Console, während Sie mit dem Google-Konto angemeldet sind, das Sie zuvor für die Registrierung als Entwickler für Google Pay verwendet haben.
- 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.
- 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.
- Klicken Sie auf Verschlüsselungsschlüssel speichern.
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.
Ändern Sie den in Ihrem Code verwendeten öffentlichen Schlüssel.
Ersetzen Sie den Wert des Attributs
publicKeyim AttributPaymentMethodTokenizationSpecificationparameters:const tokenizationSpecification = { "type": "DIRECT", "parameters": { "protocolVersion": "ECv2", "publicKey": "BOdoXP1aiNp.....kh3JUhiSZKHYF2Y=" } }
- 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.
Prüfen Sie, ob der alte öffentliche Schlüssel nicht mehr zum Verschlüsseln von Transaktionen verwendet wird.
- Entfernen Sie den alten privaten Schlüssel.
- Ö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.
- 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:
Fügen Sie in
pom.xmldie Tink-Apppaymentmethodtokenals 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>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();
Entschlüsseln Sie die Nachricht mit dem folgenden Code. Dabei wird davon ausgegangen, dass
paymentMethodTokenin der VariablenencryptedMessagegespeichert ist. Ersetzen Sie die fett formatierten Abschnitte entsprechend Ihrem Szenario.Ersetzen Sie für Nicht-Produktionstests
INSTANCE_PRODUCTIONdurchINSTANCE_TEST. Wenn Ihre Integration inaktiv ist oder kein Verschlüsselungsschlüssel konfiguriert ist, ersetzen Sie [YOUR MERCHANT ID] durch12345678901234567890.- 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);
Ersetzen Sie
PrivateKey1durch 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 einECPrivateKey-Objekt sein. Weitere Informationen zum Erstellen eines base64-codierten privaten PKCS8-Schlüssels finden Sie unter Schlüssel vorbereiten und bei Google registrieren.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.jsonzu 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