Google Pay API 會利用已簽署的加密 PaymentMethodToken
酬載傳回付款方式。傳回的付款方式是由 PAN 組成的卡片,或是由裝置 PAN 和密文組成的權杖化卡片。
酬載包含 protocolVersion
欄位,可讓酬載的接收者瞭解使用中的密碼編譯基元是哪些,以及這些基元的預期格式。
本指南將說明如何產生公開金鑰,藉此要求 Google 簽署及加密的付款方式權杖;另也提供驗證及解密該權杖的詳細步驟。
本指南僅適用於 protocolVersion = ECv2
。
由於您會直接收到付款卡資訊,因此在繼續操作之前,請先確認您的應用程式符合 PCI DSS 規範,伺服器也具備所需的基礎架構,可以安全地處理使用者的付款憑證。
以下簡單說明整合商使用 Google Pay API ECv2
PaymentMethodToken
酬載時必須採取的步驟:
- 擷取 Google 根簽署金鑰。
- 透過任何未過期的根簽署金鑰,驗證中繼簽署金鑰的簽章是否有效。
- 驗證酬載的中繼簽署金鑰尚未過期。
- 透過中繼簽署金鑰,驗證酬載的簽章是否有效。
- 先驗證簽章,再將酬載的內容解密。
- 驗證訊息是否尚未過期。這個步驟需要您查看目前的時間是否早於解密內容中的
messageExpiration
欄位。 - 使用解密內容中的付款方式收費。
Tink 程式庫中的程式碼範例可用來執行步驟 1 到 6。
付款方式權杖的結構
Google 在 PaymentData
回應中傳回的訊息是採用 UTF-8 編碼的序列化 JSON 物件,其中包含下表中指定的金鑰:
名稱 | 類型 | 說明 |
---|---|---|
protocolVersion |
字串 | 指出建立訊息時使用的加密或簽署配置。如有需要,此項目能讓通訊協定隨時間更新。 |
signature |
字串 | 驗證訊息確實來自 Google。這個字串採用 Base64 編碼,而且是透過中繼簽署金鑰以 ECDSA 建立而成。 |
intermediateSigningKey |
物件 | 內含 Google 中繼簽署金鑰的 JSON 物件,其中包含擁有 keyValue 、keyExpiration 和 signatures 的 signedKey 。為簡化中繼簽署金鑰的簽章驗證程序,這個物件已經過序列化處理。 |
signedMessage |
字串 | 已序列化為 HTML 安全字串的 JSON 物件,當中包含 encryptedMessage 、ephemeralPublicKey 和 tag 。為簡化簽章的驗證程序,這個物件已經過序列化處理。 |
示例
以下是採 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\"}" }
中繼簽署金鑰
intermediateSigningKey
是採用 UTF-8 編碼的序列化 JSON 物件,其包含下列值:
名稱 | 類型 | 說明 |
---|---|---|
signedKey |
字串 | 採用 Base64 編碼的訊息,當中包含金鑰的付款說明。 |
signatures |
字串 | 驗證中繼簽署金鑰確實來自 Google。這個字串採用 Base64 編碼,而且是以 ECDSA 建立而成。 |
已簽署的金鑰
signedKey
是採用 UTF-8 編碼的序列化 JSON 物件,其包含下列值:
名稱 | 類型 | 說明 |
---|---|---|
keyValue |
字串 | 採用 ASN.1 類型編碼的 Base64 版本金鑰,SubjectPublicKeyInfo 如 X.509 標準所定義。 |
keyExpiration |
字串 | 中繼金鑰的效期截止日期與時間,以毫秒為單位自 Epoch 紀元時間起算 (採用世界標準時間)。整合商會拒絕所有已過期的金鑰。 |
已簽署的訊息
signedMessage
是採用 UTF-8 編碼的序列化 JSON 物件,其包含下列值:
名稱 | 類型 | 說明 |
---|---|---|
encryptedMessage |
字串 | 採用 Base64 編碼的加密訊息,當中包含付款資訊和一些額外的安全性欄位。 |
ephemeralPublicKey |
字串 | 與私密金鑰相關聯並採用 Base64 編碼的暫時公開金鑰,可用來加密未壓縮點格式的訊息。詳情請參閱加密公開金鑰格式一節。 |
tag |
字串 | 採用 Base64 編碼的 encryptedMessage MAC。 |
已加密的訊息
解密後的 encryptedMessage
是採 UTF-8 編碼的序列化 JSON 物件。JSON 共有兩層,外層包含基於安全考量而納入的中繼資料和欄位,內層則是另一個代表實際付款憑證的 JSON 物件。
如要進一步瞭解 encryptedMessage
,請參閱下方表格和 JSON 物件示例:
名稱 | 類型 | 說明 |
---|---|---|
gatewayMerchantId |
字串 | 處理方可解讀的不重複商家 ID,用於驗證訊息是否屬於提出要求的商家。由處理方建立,商家會透過 Android 或網頁版的 |
messageExpiration |
字串 | 訊息的效期截止日期與時間,以毫秒為單位自 Epoch 紀元時間起算 (採用世界標準時間)。整合商應拒絕任何已過期的訊息。 |
messageId |
字串 | 可用於識別訊息的唯一識別碼,日後需要撤銷或找出訊息時即可使用。 |
paymentMethod |
字串 | 付款憑證的類型,目前僅支援 CARD 。
|
paymentMethodDetails |
物件 | 付款憑證本身。這個物件的格式會由 paymentMethod 決定,詳細說明請見下表。 |
卡片
CARD
付款方式的付款憑證是由下列屬性組成:
名稱 | 類型 | 說明 |
---|---|---|
pan |
字串 | 可供扣款的個人帳號,只能由數字組成。 |
expirationMonth |
數字 | 卡片的到期月分,其中 1 代表 1 月,2 代表 2 月,依此類推。 |
expirationYear |
數字 | 卡片的到期年分,共四位數,例如 2020。 |
authMethod |
字串 | 卡片交易的驗證方法。 |
assuranceDetails |
AssuranceDetailsSpecifications | 這個物件會提供相關資訊,說明系統對傳回的付款憑證執行的驗證作業。 |
PAN_ONLY
下列 JSON 程式碼片段為 CARD
paymentMethod
適用的完整 encryptedMessage
示例,而且這個付款方式具備 PAN_ONLY
這種 authMethod
。
"paymentMethod": "CARD", "paymentMethodDetails": { "authMethod": "PAN_ONLY", "pan": "1111222233334444", "expirationMonth": 10, "expirationYear": 2020 }, "gatewayMerchantId": "some-merchant-id", "messageId": "some-message-id", "messageExpiration": "1577862000000" }
CRYPTOGRAM_3DS
已利用 3-D Secure 密文「CRYPTOGRAM_3DS
authMethod
」驗證的 CARD
,當中包含下列額外欄位:
名稱 | 類型 | 說明 |
---|---|---|
cryptogram |
字串 | 3-D Secure 密文。 |
eciIndicator |
字串 | 不一定存在。系統只會針對 Visa 發卡機構的權杖傳回這個字串。這個值會透過付款授權要求傳送。 |
以下 JSON 程式碼片段是具有 CRYPTOGRAM_3DS
authMethod
的 CARD
paymentMethod
所適用的完整 encryptedMessage
示例:
{ "paymentMethod": "CARD", "paymentMethodDetails": { "authMethod": "CRYPTOGRAM_3DS", "pan": "1111222233334444", "expirationMonth": 10, "expirationYear": 2020, "cryptogram": "AAAAAA...", "eciIndicator": "eci indicator" }, "messageId": "some-message-id", "messageExpiration": "1577862000000" }
驗證簽章
驗證簽章 (包含中繼金鑰和訊息簽章) 所需的項目如下:
- 用來建立簽章的演算法
- 用來建立簽章的位元組字串
- 與用來建立簽章的私密金鑰相對應的公開金鑰
- 簽章本身
簽章演算法
Google 是採用橢圓曲線數位簽章演算法 (ECDSA) 來簽署訊息,所用的參數如下:透過 NIST P-256 執行 ECDSA,以 SHA-256 做為雜湊函式 (如 FIPS 186-4 所定義)。
簽章
簽章位於訊息的最外層,並採用 Base64 編碼和 ASN.1 位元組格式。如要進一步瞭解 ASN.1,請參閱 IETF 工具附錄 A 。簽章由 ECDSA 整數 r 和 s 組成,詳情請參閱簽章產生演算法說明。
以下是前述 ASN.1 位元組格式的示例 (此為透過實作 Java Cryptography Extension (JCE) ECDSA 所產生的標準格式)。
ECDSA-Sig-Value :: = SEQUENCE { r INTEGER, s INTEGER }
如何建立中繼簽署金鑰簽章的位元組字串
如要驗證付款方式權杖範例中的中繼簽署金鑰簽章,請使用下列公式建立 signedStringForIntermediateSigningKeySignature
:
signedStringForIntermediateSigningKeySignature = length_of_sender_id || sender_id || length_of_protocol_version || protocol_version || length_of_signed_key || signed_key
「||」符號代表串連。每個元件 (sender_id
、protocolVersion
、signedKey
) 都必須採用 UTF-8 編碼。signedKey
必須是 intermediateSigningKey.signedKey
的字串,且位元組長度均為 4 個位元組 (採用位元組由小到大格式)。
示例
本示例使用的付款方式權杖範例如下:
{ "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
一律為 Google
,而 protocol_version
則為 ECv2
。
如果 sender_id
是 Google
,則 signedString
會如下例所示:
signedStringForIntermediateSigningKeySignature = \x06\x00\x00\x00 || Google || | \x04\x00\x00\x00 || ECv2 || \xb5\x00\x00\x00 || {"keyExpiration":"1542323393147","keyValue":"MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAE/1+3HBVSbdv+j7NaArdgMyoSAM43yRydzqdg1TxodSzA96Dj4Mc1EiKroxxunavVIvdxGnJeFViTzFvzFRxyCw\u003d\u003d"}
如何根據 signedStringForIntermediateSigningKeySignature 驗證簽章
系統會使用標準的 ECDSA 驗證演算法,組合中繼簽署金鑰簽章的已簽署字串。針對 ECv2 通訊協定,您必須個別處理 intermediateSigningKey.signatures
中的所有簽章,並嘗試利用 keys.json
中未到期的 Google 簽署金鑰驗證每個簽章。如果至少有一個簽章可通過驗證,您就能認定驗證作業已順利完成。請稍後使用 intermediateSigningKey.signedKey.keyValue
驗證 signedStringForMessageSignature
。Google 強烈建議您使用既有的密碼編譯程式庫,而非自己的驗證作業程式碼。
如何建立訊息簽章的位元組字串
如要驗證範例付款方式權杖中的簽章,請使用下列公式建構 signedStringForMessageSignature
:
signedStringForMessageSignature = length_of_sender_id || sender_id || length_of_recipient_id || recipient_id || length_of_protocolVersion || protocolVersion || length_of_signedMessage || signedMessage
「||」符號代表串連。每個元件 (sender_id
、recipient_id
、protocolVersion
、signedMessage
) 都必須採用 UTF-8 編碼,且位元組長度均為 4 個位元組 (採用位元組由小到大格式)。建構位元組字串時,請勿剖析或修改 signedMessage
。例如,請勿將 \u003d
替換成 =
字元。
示例
以下是付款方式權杖舉例:
{ "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
一律為 Google
,而 recipient_id
則為 merchant:merchantId
。merchantId
與 Google Pay 商家主控台中針對具備實際工作環境存取權商家所找到的值相符。
如果 sender_id
是 Google
且 recipient_id
是 merchant:12345
,則 signedString
會以下例中的方式顯示:
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"}
如何根據 signedStringForMessageSignature 驗證簽章
系統會使用標準的 ECDSA 驗證演算法,組合已簽署字串。在上一個步驟中驗證的 intermediateSigningKey.signedKey.keyValue
會用來驗證 signedMessage
。Google 強烈建議您使用既有的密碼編譯程式庫,而非自己的驗證作業程式碼。
加密配置規格
Google 使用橢圓曲線整合式加密配置 (ECIES) 來確保 Google Pay API 回應中傳回的付款方式權杖安全無虞。這種加密配置所使用的參數如下:
參數 | 定義 |
---|---|
金鑰封裝方法 | ECIES-KEM (如 ISO 18033-2 所定義)。
|
金鑰衍生函式 | 採 HMAC 和 SHA-256 (
|
對稱加密演算法 |
DEM2 (如 ISO 18033-2 所定義) 加密演算法:初始向量為零且未填充的 AES-256-CTR。 |
MAC 演算法 | HMAC_SHA256 (具備金鑰衍生函式產生的 256 位元金鑰)。 |
加密公開金鑰格式
加密公開金鑰和 Google 酬載傳回的 ephemeralPublicKey
都是採用未壓縮點格式的 Base64 編碼金鑰,當中包含下列兩項元素:
- 一個用來指定格式的魔術數字 (0x04)。
- 兩個較大的 32 位元組整數,代表橢圓曲線中的 X 和 Y 座標。
如要進一步瞭解這種格式,請參閱《Public Key Cryptography For The Financial Services Industry: The Elliptic Curve Digital Signature Algorithm (ECDSA)》(適用於金融服務產業的公開金鑰密碼編譯:橢圓曲線數位簽章演算法 (ECDSA)),ANSI X9.62,1998。
使用 OpenSSL 產生公開金鑰
步驟 1:產生私密金鑰
下列示例會產生適合與 NIST P-256 搭配使用的橢圓曲線私密金鑰,並將金鑰寫入 key.pem
:
openssl ecparam -name prime256v1 -genkey -noout -out key.pem
選擇性操作:查看私密與公開金鑰
您可以使用下列指令來查看私密與公開金鑰:
openssl ec -in key.pem -pubout -text -noout
這個指令會輸出與以下類似的內容:
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
步驟 2:產生採用 Base64 編碼的公開金鑰
上述選擇性步驟示例中產生的私密與公開金鑰會採用十六進位編碼。如要取得採 Base64 編碼和未壓縮點格式的公開金鑰,請使用下列指令:
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
這個指令會輸出內容如下的 publicKey.txt
檔案,即採用未壓縮點格式的 Base64 版本金鑰:
BOdoXP+9Aq473SnGwg3JU1aiNpsd9vH2ognq4PtDtlLGa3Kj8TPf+jaQNPyDSkh3JUhiS0KyrrlWhAgNZKHYF2Y=
檔案內容不得含有多餘的空格或回車字元。如要確認內容未違反這項規定,請在 Linux 或 MacOS 中執行下列指令:
od -bc publicKey.txt
步驟 3:產生採用 PKCS #8 格式的 Base64 編碼私密金鑰
Tink 程式庫會預期您的私密金鑰是採用 Base64 編碼與 PKCS #8 格式。請使用下列指令,以您在第一個步驟中產生的私密金鑰產生這種格式的私密金鑰:
openssl pkcs8 -topk8 -inform PEM -outform DER -in key.pem -nocrypt | base64 | paste -sd "\0" -
這個指令會輸出與以下類似的內容:
MIGHAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBG0wawIBAQQgWV4oK8c/MZkCLk4qSCNjW0Zm6H0CBCtSYxkXkC9FBHehRANCAAQPldOnhO2/oXjdJD1dwlFPiNs6fcdoRgFu3/Z0iKj24SjTGyLRGAtYWLGXBZcDdPj3T2bJRHRVhE8Bc2AjkT7n
如何解密付款方式權杖
請按照下列步驟解密權杖:
- 使用您的私密金鑰和取得的
ephemeralPublicKey
,衍生出採用 ECIES-KEM 的 512 位元共用金鑰。請使用以下參數: - 橢圓曲線:NIST P-256 (在 OpenSSL 中又稱為「prime256v1」)。
CheckMode
、OldCofactorMode
、SingleHashMode
和CofactorMode
為0
。- 編碼函式:未壓縮點格式。
- 金鑰衍生函式:HKDFwithSHA256 (如 RFC 5869 中所述),其中包含下列參數:
- 請勿提供鹽。根據 RFC,這必須等同於 32 個零位元組的鹽。
- 將產生的金鑰分成兩組長度為 256 位元的金鑰:
symmetricEncryptionKey
和macKey
。 驗證
tag
欄位是encryptedMessage
的有效 MAC。如要產生預期的 MAC,請使用具備雜湊函式 SHA256 的 HMAC (RFC 5869) 和步驟 2 中取得的
macKey
。使用 AES-256-CTR 模式並按照以下條件將
encryptedMessage
解密:- 初始向量為零。
- 未填補。
- 步驟 2 中產生的
symmetricEncryptionKey
。
管理金鑰
商家加密金鑰
商家會根據加密配置規格一節中所述的規格產生公開金鑰。
Google 根簽署金鑰
Google 會發布目前有效的根簽署公開金鑰組,您可以透過公開網址擷取這組金鑰。網址傳回的 HTTP 快取標頭會註明金鑰的有效期限。系統會將這些金鑰加入快取,直到金鑰到期為止,而到期日是由「keyExpiration」欄位決定。如果擷取作業已過期,建議您重新透過公開網址擷取金鑰,以便取得最新的有效金鑰清單。
ECv2 通訊協定的例外狀況:如果您無法在執行階段中透過 Google 擷取金鑰,請改用我們的實際工作環境網址來擷取 keys.json
,將其儲存至您的系統,然後定期手動重新整理。在正常情況下,Google 會在有效期限最長的金鑰到期五年前,發出新的 ECv2 根簽署金鑰。如果金鑰遭到盜用,Google 會透過自助入口網站中的聯絡資訊通知所有商家,以便盡早要求重新載入 keys.json
。如果貴商家選擇將 Google 金鑰儲存在 keys.json
內容中,為確保您記得定期輪替金鑰,我們會建議您每年重新整理一次,藉此完成每年的金鑰輪替作業。
您透過公開網址取得的金鑰會按照以下格式顯示:
{ "keys": [ { "keyValue": "encoded public key", "protocolVersion": "ECv2" "keyExpiration":"2000000000000" }, { "keyValue": "encoded public key", "protocolVersion": "ECv2" "keyExpiration":"3000000000000" } ] }
keyValue
是不換行或填充的 Base64 版本金鑰,並採用 ASN.1 類型 SubjectPublicKeyInfo
編碼 (如 X.509 標準所定義)。在 Java 中,前述的 ASN.1 編碼是以 X509EncodedKeySpec 類別表示,您可以使用 ECPublicKey.getEncoded()
取得。
測試版和正式版環境的網址如下:
- 測試版:
https://payments.developers.google.com/paymentmethodtoken/test/keys.json
- 正式版:
https://payments.developers.google.com/paymentmethodtoken/keys.json
金鑰輪替
如果您在具備直接整合功能的伺服器上,直接將付款方式權杖解密,則必須每年輪替一次金鑰。
如要輪替加密金鑰,請完成下列步驟:
- 透過 OpenSSL 產生新的金鑰組。
- 在登入 Google 帳戶的狀態下開啟 Google Pay 商家主控台。使用的帳戶是先前透過 Google Play 管理您的應用程式的 Google 帳戶。
- 在「Google Pay API」分頁的「直接整合」窗格下方,按一下現有公開金鑰旁的「管理」。按一下「新增其他金鑰」。
- 選取「公開加密金鑰」文字輸入欄位,然後新增您剛剛產生的未壓縮點格式 Base64 編碼公開金鑰。
- 按一下「儲存加密金鑰」。
為順利執行金鑰的輪替作業,請在替換金鑰時確保新舊私密金鑰均適用於解密作業。
如果您使用 Tink 程式庫解密權杖,請使用下列 Java 程式碼來支援使用多個私密金鑰:
String decryptedMessage = new PaymentMethodTokenRecipient.Builder() .addRecipientPrivateKey(newPrivateKey) .addRecipientPrivateKey(oldPrivateKey);
請確認解密作業的程式碼已部署至實際工作環境,解密作業也能順利完成。
變更程式碼中使用的公開金鑰。
替換
PaymentMethodTokenizationSpecification
parameters
屬性中的publicKey
屬性值:/** * @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; }
- 將步驟 4 中的程式碼部署至實際工作環境。程式碼部署完畢之後,加密和解密交易就會使用新的金鑰組。
確認系統已不再使用舊的公開金鑰來加密任何交易。
- 移除舊版私密金鑰。
- 登入您先前用來申請成為 Google Pay 開發人員的 Google 帳戶,然後開啟 Google Pay 商家主控台。
- 在「Google Pay API」分頁的「直接整合」窗格下,按一下現有公開金鑰旁的「管理」。按一下舊公開金鑰旁的「刪除」,然後按一下「儲存加密金鑰」。
Google 會使用 PaymentMethodTokenizationSpecification
parameters
物件中 publicKey
屬性指定的金鑰,如以下範例所示:
{ "protocolVersion": "ECv2", "publicKey": "BOdoXP+9Aq473SnGwg3JU1..." }
使用 Tink 程式庫管理加密回應
如要驗證簽名及解密訊息,請使用 Tink 加密編譯程式庫。如要與 Tink 整合並執行驗證和解密作業,請完成下列步驟:
在您的
pom.xml
中,新增 Tinkpaymentmethodtoken
應用程式做為依附元件:<dependencies> <!-- other dependencies ... --> <dependency> <groupId>com.google.crypto.tink</groupId> <artifactId>apps-paymentmethodtoken</artifactId> <version>1.2.0</version> <!-- or latest version --> </dependency> </dependencies>
在伺服器啟動時,預先擷取 Google 簽署金鑰,讓該金鑰保存在記憶體中, 以防解密程序中的金鑰擷取作業產生網路延遲情況,對使用者造成不便。
GooglePaymentsPublicKeysManager.INSTANCE_PRODUCTION.refreshInBackground();
使用下列程式碼來解密金鑰 (這個程式碼假設
paymentMethodToken
儲存於encryptedMessage
變數中),並視情況替換粗體的部分。如要測試環境,請將
INSTANCE_PRODUCTION
替換為INSTANCE_TEST
,並將 [YOUR MERCHANT ID] 替換為12345678901234567890
。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);
以您自己的金鑰取代 PrivateKey1 和 PrivateKey2。變數可以是採用 Base64 編碼的 PKCS8 字串或
ECPrivateKey
物件。如要進一步瞭解如何產生採用 Base64 編碼的 PKCS8 私密金鑰,請參閱使用 OpenSSL 產生金鑰組的說明。如果您無法在每次解密金鑰時都呼叫 Google 伺服器,請使用下列程式碼進行解密,並視情況替換粗體的部分。
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);
在正常情況下,實際工作環境中的現有金鑰會在 2038 年 4 月 14 日到期,除非金鑰遭到盜用。如果金鑰遭到盜用,Google 會透過自助入口網站中的聯絡資訊通知所有商家,以便盡早要求重新載入
keys.json
。程式碼片段會執行下列安全性細部作業,讓您專心處理與使用酬載相關的工作:
- 擷取 Google 簽署金鑰,並儲存至快取記憶體
- 驗證簽章
- 解密