Die Google Pay API gibt Zahlungsmethoden in einer signierten und verschlüsselten PaymentMethodToken-Nutzlast zurück. Die zurückgegebenen Zahlungsmethoden sind entweder Karten, die aus einer PAN bestehen, oder tokenisierte Karten, die aus einer Geräte-PAN und Kryptogrammen bestehen.
Die Nutzlast enthält ein Feld namens protocolVersion, das dem Empfänger der Nutzlast mitteilt, welche kryptografischen Primitive verwendet werden und welches Format erwartet wird.
In diesem Leitfaden wird beschrieben, wie du einen öffentlichen Schlüssel generierst, um ein von Google signiertes und verschlüsseltes Zahlungsmethoden-Token anzufordern. Außerdem werden die Schritte zum Bestätigen und Entschlüsseln des Tokens erläutert.
Dieser Leitfaden gilt nur für protocolVersion = ECv2.
Da du Zahlungskarteninformationen direkt erhältst, musst du dafür sorgen, dass deine App PCI-DSS-konform ist und deine Server die erforderliche Infrastruktur haben, um die Zahlungsdaten des Nutzers sicher zu verarbeiten, bevor du fortfährst.
Im Folgenden wird beschrieben, was ein Integrator tun muss, um die Nutzlast ECv2 PaymentMethodToken der Google Pay API zu nutzen:
- Rufen Sie die Google-Stammsignaturschlüssel ab.
- Prüfe, ob die Signatur des Zwischensignaturschlüssels durch einen der nicht abgelaufenen Stammsignaturschlüssel gültig ist.
- Prüfen Sie, ob der Zwischensignaturschlüssel der Nutzlast abgelaufen ist.
- Prüfen Sie, ob die Signatur der Nutzlast durch den 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 musst du prüfen, ob die aktuelle Zeit vor der Zeit im Feld
messageExpirationim entschlüsselten Inhalt liegt. - Verwenden Sie die Zahlungsmethode in den entschlüsselten Inhalten und belasten Sie sie.
Der Beispielcode in unserer Tink-Bibliothek führt die Schritte 1–6 aus.
Struktur des Zahlungsmethoden-Tokens
Die von Google in der PaymentData-Antwort zurückgegebene Nachricht ist ein UTF-8-codiertes, serialisiertes JSON-Objekt mit den in der folgenden Tabelle angegebenen Schlüsseln:
| Name | Typ | Beschreibung |
|---|---|---|
protocolVersion |
String | Gibt das Verschlüsselungs- oder Signaturschema an, mit dem die Nachricht erstellt wird. So kann das Protokoll bei Bedarf im Laufe der Zeit weiterentwickelt werden. |
signature |
String | Bestätigt, dass die Nachricht von Google stammt. Er ist base64-codiert und wurde mit ECDSA vom Zwischensignaturschlüssel erstellt. |
intermediateSigningKey |
Objekt | Ein JSON-Objekt, das den Zwischensignaturschlüssel von Google enthält. Sie enthält die signedKey mit keyValue, keyExpiration und signatures. Sie wird serialisiert, um die Signaturprüfung des Zwischensignaturschlüssels zu vereinfachen. |
signedMessage |
String | Ein JSON-Objekt, das als HTML-sicherer String serialisiert ist und encryptedMessage, ephemeralPublicKey und tag enthält. Sie wird serialisiert, um die Signaturüberprüfung zu vereinfachen. |
Beispiel
Im Folgenden finden Sie eine Antwort mit einem Zahlungsmethodentoken im JSON-Format:
{ "protocolVersion":"ECv2", "signature":"MEQCIH6Q4OwQ0jAceFEkGF0JID6sJNXxOEi4r+mA7biRxqBQAiAondqoUpU/bdsrAOpZIsrHQS9nwiiNwOrr24RyPeHA0Q\u003d\u003d", "intermediateSigningKey":{ "signedKey": "{\"keyExpiration\":\"1542323393147\",\"keyValue\":\"MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAE/1+3HBVSbdv+j7NaArdgMyoSAM43yRydzqdg1TxodSzA96Dj4Mc1EiKroxxunavVIvdxGnJeFViTzFvzFRxyCw\\u003d\\u003d\"}", "signatures": ["MEYCIQCO2EIi48s8VTH+ilMEpoXLFfkxAwHjfPSCVED/QDSHmQIhALLJmrUlNAY8hDQRV/y1iKZGsWpeNmIP+z+tCQHQxP0v"] }, "signedMessage":"{\"tag\":\"jpGz1F1Bcoi/fCNxI9n7Qrsw7i7KHrGtTf3NrRclt+U\\u003d\",\"ephemeralPublicKey\":\"BJatyFvFPPD21l8/uLP46Ta1hsKHndf8Z+tAgk+DEPQgYTkhHy19cF3h/bXs0tWTmZtnNm+vlVrKbRU9K8+7cZs\\u003d\",\"encryptedMessage\":\"mKOoXwi8OavZ\"}" }
Zwischensignaturschlüssel
intermediateSigningKey ist ein UTF-8-codiertes, serialisiertes JSON-Objekt, das die folgenden Werte enthält:
| Name | Typ | Beschreibung |
|---|---|---|
signedKey |
String | Eine base64-codierte Nachricht, die die Zahlungsbeschreibung des Schlüssels enthält. |
signatures |
String | Überprüft, ob der Zwischensignaturschlüssel von Google stammt. Er ist base64-codiert und wurde mit ECDSA erstellt. |
Signierter Schlüssel
signedKey ist ein UTF-8-codiertes, serialisiertes JSON-Objekt, das die folgenden Werte enthält:
| Name | Typ | Beschreibung |
|---|---|---|
keyValue |
String | Eine base64-Version des Schlüssels, codiert im ASN.1-Typ. SubjectPublicKeyInfo ist im X.509-Standard definiert. |
keyExpiration |
String | Datum und Uhrzeit des Ablaufs des Zwischenschlüssels in UTC-Millisekunden seit der Epoche. Integratoren lehnen alle abgelaufenen Schlüssel ab. |
Signierte Nachricht
signedMessage ist ein UTF-8-codiertes, serialisiertes JSON-Objekt, das die folgenden Werte enthält:
| Name | Typ | Beschreibung |
|---|---|---|
encryptedMessage |
String | Eine base64-codierte verschlüsselte Nachricht, die Zahlungsinformationen und einige zusätzliche Sicherheitsfelder enthält. |
ephemeralPublicKey |
String | Ein base64-codierter sitzungsspezifischer öffentlicher Schlüssel, der mit dem privaten Schlüssel verknüpft ist, um die Nachricht im unkomprimierten Punktformat zu verschlüsseln. Weitere Informationen finden Sie unter Format des öffentlichen Schlüssels für die Verschlüsselung. |
tag |
String | Ein base64-codierter MAC von encryptedMessage. |
Nachricht verschlüsseln
Der entschlüsselte encryptedMessage ist ein UTF-8-codiertes, serialisiertes JSON-Objekt. Das JSON-Objekt enthält zwei Ebenen. Die äußere Ebene enthält Metadaten und Felder, die aus Sicherheitsgründen enthalten sind. Die innere Ebene ist ein weiteres JSON-Objekt, das die eigentlichen Zahlungsanmeldedaten darstellt.
Weitere Informationen zu encryptedMessage finden Sie in den folgenden Tabellen und JSON-Objektbeispielen:
| Name | Typ | Beschreibung |
|---|---|---|
messageExpiration |
String | Datum und Uhrzeit, zu der die Nachricht abläuft, in UTC-Millisekunden seit der Epoche. Integratoren sollten alle abgelaufenen Nachrichten ablehnen. |
messageId |
String | Eine eindeutige ID, mit der die Nachricht identifiziert wird, falls sie später widerrufen oder gefunden werden muss. |
paymentMethod |
String | Der Typ der Zahlungsberechtigung.
Derzeit wird nur CARD unterstützt.
|
paymentMethodDetails |
Objekt | Der Zahlungsberechtigungsnachweis selbst. Das Format dieses Objekts wird durch den paymentMethod bestimmt und in den folgenden Tabellen beschrieben. |
Infokarte
Die folgenden Attribute bilden einen Zahlungsberechtigungsnachweis für die Zahlungsmethode CARD:
| Name | Typ | Beschreibung |
|---|---|---|
pan |
String | Die belastete persönliche Kontonummer. Dieser String enthält nur Ziffern. |
expirationMonth |
Zahl | Der Ablaufmonat der Karte, wobei 1 für Januar, 2 für Februar usw. steht. |
expirationYear |
Zahl | Das vierstellige Ablaufjahr der Karte, z. B. 2020. |
authMethod |
String | Die Authentifizierungsmethode der Kartentransaktion. |
merchantTokenId |
String | Die Händler-Token-ID der Kartentransaktion. Dieses optionale Feld wird nur zurückgegeben, wenn alle folgenden Bedingungen erfüllt sind:
|
PAN_ONLY
Das folgende JSON-Snippet ist ein Beispiel für die vollständige encryptedMessage für eine CARD paymentMethod
mit einer PAN_ONLY authMethod.
{ "paymentMethod": "CARD", "paymentMethodDetails": { "authMethod": "PAN_ONLY", "pan": "1111222233334444", "expirationMonth": 10, "expirationYear": 2025 }, "gatewayMerchantId": "some-merchant-id", "messageId": "some-message-id", "messageExpiration": "1759309000000" }
CRYPTOGRAM_3DS
Eine CARD, die mit einem 3-D Secure-Kryptogramm authentifiziert wurde, CRYPTOGRAM_3DS authMethod. Sie enthält die folgenden zusätzlichen Felder:
| Name | Typ | Beschreibung |
|---|---|---|
cryptogram |
String | Ein 3-D Secure-Kryptogramm. |
eciIndicator |
String | Dieser String ist nicht immer vorhanden. Sie wird nur für Transaktionen mit authentifizierten Geräte-Tokens auf Android-Geräten zurückgegeben (CRYPTOGRAM_3DS). Dieser Wert muss im Zahlungsabwicklungsablauf weitergegeben werden. |
Das folgende JSON-Snippet ist ein Beispiel für die vollständige encryptedMessage für eine CARD-paymentMethod mit einer CRYPTOGRAM_3DS-authMethod:
{
"paymentMethod": "CARD",
"paymentMethodDetails": {
"authMethod": "CRYPTOGRAM_3DS",
"pan": "1111222233334444",
"expirationMonth": 10,
"expirationYear": 2025,
"cryptogram": "AAAAAA...",
"eciIndicator": "eci indicator",
"merchantTokenId": "merchant-token-id",
},
"messageId": "some-message-id",
"messageExpiration": "1759309000000"
}eciIndicator
Das Kartennetzwerk stellt möglicherweise das eciIndicator für authentifizierte Gerätetoken-Transaktionen (CRYPTOGRAM_3DS) bereit.
Du musst den Wert eciIndicator bei der Autorisierungstransaktion übergeben, ohne ihn zu ändern oder hartzucodieren. Andernfalls schlägt die Transaktion fehl. In der folgenden Tabelle werden die Werte von eciIndicator beschrieben.
| eciIndicator-Wert | Kartennetzwerk | Haftende Partei | authMethod |
|---|---|---|---|
""(empty) |
Mastercard | Händler/Acquirer | CRYPTOGRAM_3DS |
02 |
Mastercard | Kartenaussteller | CRYPTOGRAM_3DS |
06 |
Mastercard | Händler/Acquirer | CRYPTOGRAM_3DS |
05 |
Visa | Kartenaussteller | CRYPTOGRAM_3DS |
07 |
Visa | Händler/Acquirer | CRYPTOGRAM_3DS |
""(empty) |
Andere Netzwerke | Händler/Acquirer | CRYPTOGRAM_3DS |
Andere ECI-Werte für VISA und Mastercard, die nicht in dieser Tabelle aufgeführt sind, werden nicht zurückgegeben.
Signaturüberprüfung
Zum Prüfen der Signaturen, einschließlich des Zwischenschlüssels und der Nachrichtensignaturen, sind die folgenden Elemente erforderlich:
- Der Algorithmus, der zum Erstellen der Signatur verwendet wird
- Der Byte-String, der zum Erstellen der Signatur verwendet wird
- Der öffentliche Schlüssel, der dem privaten Schlüssel entspricht, der zum Erstellen der Signatur verwendet wurde
- Die Signatur selbst
Der Signaturalgorithmus
Google verwendet den Elliptic Curve Digital Signature Algorithm (ECDSA), um die Nachrichten mit den folgenden Parametern zu signieren: ECDSA über NIST P-256 mit SHA-256 als Hash-Funktion, wie in FIPS 186-4 definiert.
Die Signatur
Die Signatur ist auf der äußersten Ebene der Nachricht enthalten. Er ist mit Base64 im ASN.1-Byteformat codiert. Weitere Informationen zu ASN.1 finden Sie im Anhang A der IETF-Tools. Die Signatur besteht aus den ECDSA-Ganzzahlen r und s. Weitere Informationen finden Sie unter Algorithmus zur Signaturgenerierung.
Das folgende Beispiel zeigt das angegebene ASN.1-Byteformat, das das Standardformat ist, das von ECDSA-Implementierungen der Java Cryptography Extension (JCE) erzeugt wird.
ECDSA-Sig-Value :: = SEQUENCE {
r INTEGER,
s INTEGER
}Byte-String für die Signatur des Zwischensignaturschlüssels erstellen
Wenn Sie die Signatur des Zwischensignaturschlüssels im Beispieltoken für die Zahlungsmethode validieren möchten, erstellen Sie die signedStringForIntermediateSigningKeySignature mit der folgenden Formel:
signedStringForIntermediateSigningKeySignature = length_of_sender_id || sender_id || length_of_protocol_version || protocol_version || length_of_signed_key || signed_key
Die Notation „||“ bedeutet „verketten“. Jede Komponente (sender_id, protocolVersion, signedKey) muss UTF-8-codiert sein. signedKey muss der String von intermediateSigningKey.signedKey sein.
Die Bytelänge jeder Komponente beträgt 4 Byte im Little-Endian-Format.
Beispiel
In diesem Beispiel wird das folgende Beispieltoken für die Zahlungsmethode verwendet:
{
"protocolVersion":"ECv2",
"signature":"MEQCIH6Q4OwQ0jAceFEkGF0JID6sJNXxOEi4r+mA7biRxqBQAiAondqoUpU/bdsrAOpZIsrHQS9nwiiNwOrr24RyPeHA0Q\u003d\u003d",
"intermediateSigningKey":{
"signedKey": "{\"keyExpiration\":\"1542323393147\",\"keyValue\":\"MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAE/1+3HBVSbdv+j7NaArdgMyoSAM43yRydzqdg1TxodSzA96Dj4Mc1EiKroxxunavVIvdxGnJeFViTzFvzFRxyCw\\u003d\\u003d\"}",
"signatures": ["MEYCIQCO2EIi48s8VTH+ilMEpoXLFfkxAwHjfPSCVED/QDSHmQIhALLJmrUlNAY8hDQRV/y1iKZGsWpeNmIP+z+tCQHQxP0v"]
},
"signedMessage":"{\"tag\":\"jpGz1F1Bcoi/fCNxI9n7Qrsw7i7KHrGtTf3NrRclt+U\\u003d\",\"ephemeralPublicKey\":\"BJatyFvFPPD21l8/uLP46Ta1hsKHndf8Z+tAgk+DEPQgYTkhHy19cF3h/bXs0tWTmZtnNm+vlVrKbRU9K8+7cZs\\u003d\",\"encryptedMessage\":\"mKOoXwi8OavZ\"}"
}sender_id ist immer Google und protocol_version ist ECv2.
Wenn sender_id den Wert Google hat, wird signedString wie im folgenden Beispiel angezeigt:
signedStringForIntermediateSigningKeySignature =
\x06\x00\x00\x00 || Google || | \x04\x00\x00\x00 || ECv2 || \xb5\x00\x00\x00 || {"keyExpiration":"1542323393147","keyValue":"MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAE/1+3HBVSbdv+j7NaArdgMyoSAM43yRydzqdg1TxodSzA96Dj4Mc1EiKroxxunavVIvdxGnJeFViTzFvzFRxyCw\u003d\u003d"}Signatur für signedStringForIntermediateSigningKeySignature überprüfen
Der standardmäßige ECDSA-Prüfalgorithmus wird verwendet, wenn der signierte String für die Signatur des Zwischensignaturschlüssels zusammengestellt wird. Für das ECv2-Protokoll müssen Sie alle Signaturen in intermediateSigningKey.signatures durchlaufen und versuchen, jede mit den nicht abgelaufenen Google-Signaturschlüsseln in keys.json zu validieren. Wenn mindestens eine Signaturvalidierung funktioniert, gilt die Überprüfung als abgeschlossen. Verwenden Sie später intermediateSigningKey.signedKey.keyValue, um signedStringForMessageSignature zu prüfen. Google empfiehlt dringend, eine vorhandene kryptografische Bibliothek anstelle Ihres eigenen Bestätigungscodes zu verwenden.
Byte-String für die Nachrichtensignatur erstellen
Wenn Sie die Signatur im Beispiel-Zahlungsmethodentoken validieren möchten, erstellen Sie den signedStringForMessageSignature mit der folgenden Formel:
signedStringForMessageSignature = length_of_sender_id || sender_id || length_of_recipient_id || recipient_id || length_of_protocolVersion || protocolVersion || length_of_signedMessage || signedMessage
Die Notation „||“ bedeutet „verketten“. Jede Komponente (sender_id, recipient_id, protocolVersion, signedMessage) muss UTF-8-codiert sein. Die Bytelänge jeder Komponente beträgt 4 Byte im Little-Endian-Format. Wenn Sie den Byte-String erstellen, dürfen Sie signedMessage nicht parsen oder ändern. Ersetzen Sie beispielsweise \u003d nicht durch das Zeichen =.
Beispiel
Das folgende Beispiel zeigt ein Beispieltoken für eine Zahlungsmethode:
{
"protocolVersion":"ECv2",
"signature":"MEQCIH6Q4OwQ0jAceFEkGF0JID6sJNXxOEi4r+mA7biRxqBQAiAondqoUpU/bdsrAOpZIsrHQS9nwiiNwOrr24RyPeHA0Q\u003d\u003d",
"intermediateSigningKey":{
"signedKey": "{\"keyExpiration\":\"1542323393147\",\"keyValue\":\"MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAE/1+3HBVSbdv+j7NaArdgMyoSAM43yRydzqdg1TxodSzA96Dj4Mc1EiKroxxunavVIvdxGnJeFViTzFvzFRxyCw\\u003d\\u003d\"}",
"signatures": ["MEYCIQCO2EIi48s8VTH+ilMEpoXLFfkxAwHjfPSCVED/QDSHmQIhALLJmrUlNAY8hDQRV/y1iKZGsWpeNmIP+z+tCQHQxP0v"]
},
"signedMessage":"{\"tag\":\"jpGz1F1Bcoi/fCNxI9n7Qrsw7i7KHrGtTf3NrRclt+U\\u003d\",\"ephemeralPublicKey\":\"BJatyFvFPPD21l8/uLP46Ta1hsKHndf8Z+tAgk+DEPQgYTkhHy19cF3h/bXs0tWTmZtnNm+vlVrKbRU9K8+7cZs\\u003d\",\"encryptedMessage\":\"mKOoXwi8OavZ\"}"
}sender_id ist immer Google und recipient_id ist merchant:merchantId. Die merchantId entspricht dem Wert, der in der Google Pay & Wallet Console für Händler mit Produktionszugriff angegeben ist.
Wenn der sender_id Google und der recipient_id merchant:12345 ist, wird der signedString wie im folgenden Beispiel angezeigt:
signedStringForMessageSignature =
\x06\x00\x00\x00 || Google || \x0e\x00\x00\x00 || merchant:12345 || | \x04\x00\x00\x00 || ECv2 || \xd2\x00\x00\x00 || {"tag":"jpGz1F1Bcoi/fCNxI9n7Qrsw7i7KHrGtTf3NrRclt+U\u003d","ephemeralPublicKey":"BJatyFvFPPD21l8/uLP46Ta1hsKHndf8Z+tAgk+DEPQgYTkhHy19cF3h/bXs0tWTmZtnNm+vlVrKbRU9K8+7cZs\u003d","encryptedMessage":"mKOoXwi8OavZ"}Signatur in „signedStringForMessageSignature“ überprüfen
Der standardmäßige ECDSA-Prüfalgorithmus wird verwendet, wenn der signierte String zusammengestellt wird. Die im vorherigen Schritt verifizierte intermediateSigningKey.signedKey.keyValue wird verwendet, um die signedMessage zu verifizieren. Google empfiehlt dringend, eine vorhandene kryptografische Bibliothek anstelle Ihres eigenen Bestätigungscodes zu verwenden.
Spezifikation des Verschlüsselungsschemas
Google verwendet das Elliptic Curve Integrated Encryption Scheme (ECIES), um das in der Google Pay API-Antwort zurückgegebene Zahlungsmethodentoken zu sichern. Das Verschlüsselungsschema verwendet die folgenden Parameter:
| Parameter | Definition |
|---|---|
| Schlüsselkapselungsmethode | ECIES-KEM, wie in ISO 18033-2 definiert.
|
| Schlüsselableitungsfunktion | HMAC-basiert mit SHA-256 (
|
| Symmetrischer Verschlüsselungsalgorithmus |
DEM2, wie in ISO 18033-2 definiert Verschlüsselungsalgorithmus: AES-256-CTR mit Null-IV und ohne Padding. |
| MAC-Algorithmus | HMAC_SHA256 mit einem 256-Bit-Schlüssel, der von der Schlüsselableitungsfunktion abgeleitet wurde. |
Format des öffentlichen Verschlüsselungsschlüssels
Der öffentliche Verschlüsselungsschlüssel und der in Google-Nutzlasten zurückgegebene ephemeralPublicKey sind mit der Base64-Darstellung des Schlüssels im unkomprimierten Punktformat formatiert. Sie besteht aus den folgenden beiden Elementen:
- Eine magische Zahl, die das Format angibt (0x04).
- Zwei 32 Byte große Ganzzahlen, die die X- und Y-Koordinaten in der elliptischen Kurve darstellen.
Dieses Format wird ausführlicher in „Public Key Cryptography For The Financial Services Industry: The Elliptic Curve Digital Signature Algorithm (ECDSA)“, ANSI X9.62, 1998 beschrieben.
Öffentlichen Schlüssel mit OpenSSL generieren
Schritt 1: Privaten Schlüssel generieren
Im folgenden Beispiel wird ein privater Schlüssel mit elliptischer Kurve generiert, der für die Verwendung mit NIST P-256 geeignet ist, und in key.pem geschrieben:
openssl ecparam -name prime256v1 -genkey -noout -out key.pem
Optional: Private und öffentliche Schlüssel ansehen
Verwenden Sie den folgenden Befehl, um sowohl den privaten als auch den öffentlichen Schlüssel aufzurufen:
openssl ec -in key.pem -pubout -text -noout
Die Ausgabe des Befehls sieht in etwa so aus:
read EC key
Private-Key: (256 bit)
priv:
08:f4:ae:16:be:22:48:86:90:a6:b8:e3:72:11:cf:
c8:3b:b6:35:71:5e:d2:f0:c1:a1:3a:4f:91:86:8a:
f5:d7
pub:
04:e7:68:5c:ff:bd:02:ae:3b:dd:29:c6:c2:0d:c9:
53:56:a2:36:9b:1d:f6:f1:f6:a2:09:ea:e0:fb:43:
b6:52:c6:6b:72:a3:f1:33:df:fa:36:90:34:fc:83:
4a:48:77:25:48:62:4b:42:b2:ae:b9:56:84:08:0d:
64:a1:d8:17:66
ASN1 OID: prime256v1
Schritt 2: Base64-codierten öffentlichen Schlüssel generieren
Der private und der öffentliche Schlüssel, die im vorherigen optionalen Schritt generiert wurden, sind hexadezimal codiert. Verwenden Sie den folgenden Befehl, um einen base64-codierten öffentlichen Schlüssel im unkomprimierten Punktformat abzurufen:
openssl ec -in key.pem -pubout -text -noout 2> /dev/null | grep "pub:" -A5 | sed 1d | xxd -r -p | base64 | paste -sd "\0" - | tr -d '\n\r ' > publicKey.txt
Der Befehl erzeugt eine publicKey.txt-Datei, deren Inhalt, die Base64-Version des Schlüssels im unkomprimierten Punktformat, in etwa so aussieht:
BOdoXP+9Aq473SnGwg3JU1aiNpsd9vH2ognq4PtDtlLGa3Kj8TPf+jaQNPyDSkh3JUhiS0KyrrlWhAgNZKHYF2Y=
Der Dateiinhalt darf keine zusätzlichen Leerzeichen oder Zeilenumbrüche enthalten. Führen Sie dazu unter Linux oder macOS den folgenden Befehl aus:
od -bc publicKey.txt
Schritt 3: Base64-codierten privaten Schlüssel im PKCS #8-Format generieren
Die Tink-Bibliothek erwartet, dass Ihr privater Schlüssel im PKCS #8-Format base64-codiert ist. Verwenden Sie den folgenden Befehl, um den privaten Schlüssel in diesem Format aus dem im ersten Schritt generierten privaten Schlüssel zu generieren:
openssl pkcs8 -topk8 -inform PEM -outform DER -in key.pem -nocrypt | base64 | paste -sd "\0" -
Die Ausgabe des Befehls sieht in etwa so aus:
MIGHAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBG0wawIBAQQgWV4oK8c/MZkCLk4qSCNjW0Zm6H0CBCtSYxkXkC9FBHehRANCAAQPldOnhO2/oXjdJD1dwlFPiNs6fcdoRgFu3/Z0iKj24SjTGyLRGAtYWLGXBZcDdPj3T2bJRHRVhE8Bc2AjkT7n
Zahlungsmethoden-Token entschlüsseln
So entschlüsseln Sie das Token:
- Verwenden Sie Ihren privaten Schlüssel und den angegebenen
ephemeralPublicKey, um einen 512 Bit langen freigegebenen Schlüssel abzuleiten, der ECIES-KEM verwendet. Verwenden Sie die folgenden Parameter: - Elliptische Kurve: NIST P-256, in OpenSSL auch als prime256v1 bezeichnet.
CheckMode,OldCofactorMode,SingleHashModeundCofactorModesind0.- Codierungsfunktion: Unkomprimiertes Punktformat.
- Schlüsselableitungsfunktion: HKDFwithSHA256, wie in RFC 5869 beschrieben, mit dem folgenden Parameter:
- Salt darf nicht angegeben werden. Gemäß RFC muss dies einem Salt von 32 Null-Byte entsprechen.
- Teilen Sie den generierten Schlüssel in zwei 256-Bit-Schlüssel auf:
symmetricEncryptionKeyundmacKey. Prüfen Sie, ob das Feld
tagein gültiger MAC fürencryptedMessageist.Verwenden Sie zum Generieren des erwarteten MAC HMAC (RFC 5869) mit der Hash-Funktion SHA256 und dem in Schritt 2 erhaltenen
macKey.Entschlüsseln Sie
encryptedMessagemit dem AES-256-CTR-Modus und Folgendem:- Ein IV von null.
- Nicht gepolstert.
- Der in Schritt 2 abgeleitete
symmetricEncryptionKey.
Schlüsselverwaltung
Verschlüsselungsschlüssel des Händlers
Händler generieren einen öffentlichen Schlüssel gemäß den Spezifikationen in der Spezifikation des Verschlüsselungsschemas.
Google-Root-Signaturschlüssel
Google veröffentlicht die derzeit gültigen öffentlichen Root-Signaturschlüssel, die über eine öffentliche URL abgerufen werden können. Die Schlüssel sind so lange gültig, wie es die von der URL zurückgegebenen HTTP-Cache-Header angeben. Sie werden im Cache gespeichert, bis sie ablaufen. Dies wird durch das Feld keyExpiration bestimmt. Wir empfehlen, die Schlüssel nach Ablauf des Abrufs noch einmal von der öffentlichen URL abzurufen, um die aktuelle Liste der gültigen Schlüssel zu erhalten.
Ausnahme für das ECv2-Protokoll: Wenn du die Schlüssel nicht zur Laufzeit von Google abrufen kannst, rufe die keys.json von unserer Produktions-URL ab, speichere sie in deinem System und aktualisiere sie regelmäßig manuell. Unter normalen Umständen stellt Google fünf Jahre vor Ablauf des Schlüssels mit dem längsten Ablaufdatum einen neuen Root-Signaturschlüssel für ECv2 aus. Wenn Schlüssel kompromittiert werden, benachrichtigt Google alle Händler über die im Self-Service-Portal angegebenen Kontaktdaten, um ein schnelleres Neuladen von keys.json anzufordern. Damit Sie die regelmäßige Rotation nicht verpassen, empfehlen wir Händlern, die Google-Schlüssel im Inhalt von keys.json speichern, diese im Rahmen ihrer eigenen jährlichen Schlüsselrotation jährlich zu aktualisieren.
Die über die öffentliche URL bereitgestellten Schlüssel werden im folgenden Format zugeordnet:
{ "keys": [ { "keyValue": "encoded public key", "protocolVersion": "ECv2" "keyExpiration":"2000000000000" }, { "keyValue": "encoded public key", "protocolVersion": "ECv2" "keyExpiration":"3000000000000" } ] }
Der keyValue ist eine Base64-codierte, nicht verpackte oder aufgefüllte Version des Schlüssels, der im ASN.1-Typ SubjectPublicKeyInfo codiert ist, der im X.509-Standard definiert ist. In Java wird die referenzierte ASN.1-Codierung durch die X509EncodedKeySpec-Klasse dargestellt.
Sie kann mit ECPublicKey.getEncoded() abgerufen werden.
URLs für Test- und Produktionsumgebungen finden Sie unter den folgenden Links:
- Test:
https://payments.developers.google.com/paymentmethodtoken/test/keys.json - Produktion:
https://payments.developers.google.com/paymentmethodtoken/keys.json
Schlüsselrotation
Wenn du ein Zahlungsmethoden-Token mit direkter Integration direkt auf deinen Servern entschlüsselst, musst du die Schlüssel jährlich rotieren.
Führen Sie die folgenden Schritte aus, um Verschlüsselungsschlüssel zu rotieren:
- Mit OpenSSL ein neues Schlüsselpaar generieren
- Öffne die Google Pay & Wallet Console und melde dich mit dem Google-Konto die Sie zuvor zum Verwalten Ihrer App bei Google Play verwendet haben.
- 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 im Base64-codierten, unkomprimierten Punktformat hinzu.
- Klicken Sie auf Verschlüsselungsschlüssel speichern.
Damit die Schlüsselrotation reibungslos funktioniert, sollten Sie während der Umstellung sowohl die Entschlüsselung mit dem neuen als auch mit dem alten privaten Schlüssel unterstützen.
Wenn Sie die Tink-Bibliothek zum Entschlüsseln des Tokens verwenden, können Sie mit dem folgenden Java-Code mehrere private Schlüssel unterstützen:
String decryptedMessage = new PaymentMethodTokenRecipient.Builder() .addRecipientPrivateKey(newPrivateKey) .addRecipientPrivateKey(oldPrivateKey);
Achten Sie darauf, dass der Code für die Entschlüsselung in der Produktion bereitgestellt wird und dass Sie erfolgreiche Entschlüsselungen im Blick behalten.
Ändern Sie den in Ihrem Code verwendeten öffentlichen Schlüssel.
Ersetzen Sie den Wert des Attributs
publicKeyim AttributPaymentMethodTokenizationSpecificationparameters:/** * @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; }
- Stellen Sie den Code aus Schritt 4 in der Produktion bereit. Sobald der Code bereitgestellt ist, werden für Verschlüsselungs- und Entschlüsselungstransaktionen die neuen Schlüsselpaare verwendet.
Prüfe, ob der alte öffentliche Schlüssel nicht mehr zum Verschlüsseln von Transaktionen verwendet wird.
- Entfernen Sie den alten privaten Schlüssel.
- Öffne die Google Pay & Wallet Console und melde dich mit dem Google-Konto an, mit dem du dich zuvor als Entwickler bei Google Pay registriert hast.
- Klicken Sie auf dem Tab Google Pay API im Bereich Direkte Integration neben Ihrem vorhandenen öffentlichen Schlüssel auf Verwalten. Klicken Sie neben dem alten öffentlichen Schlüssel auf Löschen und dann auf Verschlüsselungsschlüssel speichern.
Google verwendet den Schlüssel, der im Attribut publicKey im Objekt PaymentMethodTokenizationSpecification parameters angegeben ist, wie im folgenden Beispiel gezeigt:
{
"protocolVersion": "ECv2",
"publicKey": "BOdoXP+9Aq473SnGwg3JU1..."
}Verwenden Sie die Tink-Bibliothek, um die verschlüsselte Antwort zu verwalten.
Verwenden Sie die Tink-Bibliothek „paymentmethodtoken“, um die Signatur zu überprüfen und die Nachricht zu entschlüsseln. Diese Bibliothek ist nur in Java verfügbar. Gehen Sie dazu so vor:
Fügen Sie in Ihrer
pom.xmldie Tink-paymentmethodtoken-Anwendung als Abhängigkeit hinzu:<dependencies> <!-- other dependencies ... --> <dependency> <groupId>com.google.crypto.tink</groupId> <artifactId>apps-paymentmethodtoken</artifactId> <version>1.9.1</version> <!-- or latest version --> </dependency> </dependencies>Rufen Sie beim Serverstart die Google-Signaturschlüssel vorab ab, damit der Schlüssel im Arbeitsspeicher verfügbar ist. Dadurch wird verhindert, dass Nutzer die Netzwerklatenz sehen, während die Schlüssel für den Entschlüsselungsprozess abgerufen werden.
GooglePaymentsPublicKeysManager.INSTANCE_PRODUCTION.refreshInBackground();
Entschlüsseln Sie die Nachricht mit dem folgenden Code, wobei davon ausgegangen wird, dass
paymentMethodTokenin der VariablenencryptedMessagegespeichert ist. Ersetzen Sie die fett formatierten Abschnitte entsprechend Ihrem Szenario.Ersetzen Sie für Tests außerhalb der Produktion
INSTANCE_PRODUCTIONdurchINSTANCE_TEST. Wenn Ihre Integration inaktiv ist oder kein Verschlüsselungsschlüssel konfiguriert ist, ersetzen Sie [YOUR MERCHANT ID] durch.- Aktiv
- Hat die DIRECT-Integration aktiviert
- Verschlüsselungsschlüssel ist konfiguriert
Ersetzen Sie [YOUR MERCHANT ID] nicht.
String decryptedMessage = new PaymentMethodTokenRecipient.Builder() .fetchSenderVerifyingKeysWith( GooglePaymentsPublicKeysManager.INSTANCE_PRODUCTION) .recipientId("merchant:[YOUR MERCHANT ID]") // This guide applies only to protocolVersion = ECv2 .protocolVersion("ECv2") // Multiple private keys can be added to support graceful // key rotations. .addRecipientPrivateKey(PrivateKey1) .addRecipientPrivateKey(PrivateKey2) .build() .unseal(encryptedMessage);
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 mehrere andere private Schlüsselwerte hinzufügen, wenn Sie Schlüssel mit Google rotieren müssen. Die Variablen können entweder ein Base64-codierter PKCS8-String oder 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, wenn Sie Schlüssel entschlüsseln, einen Google-Server aufrufen können, entschlüsseln Sie mit dem folgenden Code und ersetzen Sie die fett formatierten Abschnitte entsprechend Ihrem Szenario.
String decryptedMessage = new PaymentMethodTokenRecipient.Builder() .addSenderVerifyingKey("ECv2 key fetched from test or production url") .recipientId("merchant:[YOUR MERCHANT ID]") // This guide applies only to protocolVersion = ECv2 .protocolVersion("ECv2") // Multiple private keys can be added to support graceful // key rotations. .addRecipientPrivateKey(PrivateKey1) .addRecipientPrivateKey(PrivateKey2) .build() .unseal(encryptedMessage);
Der aktuelle Schlüssel in der Produktionsumgebung ist unter normalen Umständen bis zum 14.04.2038 gültig, sofern er nicht kompromittiert wird. Wenn Schlüssel kompromittiert werden, benachrichtigt Google alle Händler über die im Self-Service-Portal angegebenen Kontaktdaten, um ein schnelleres Neuladen von
keys.jsonanzufordern.Das Code-Snippet verarbeitet die folgenden Sicherheitsdetails, damit Sie sich auf die Nutzung der Nutzlast konzentrieren können:
- Google-Signaturschlüssel abgerufen und im Arbeitsspeicher im Cache gespeichert
- Signaturüberprüfung
- Entschlüsselung