適用於商家的付款資料密碼編譯機制

Google Pay API 會利用已簽署的加密 PaymentMethodToken 酬載傳回付款方式。傳回的付款方式是由 PAN 組成的卡片,或是由裝置 PAN 和密文組成的代碼化卡片。

酬載包含 protocolVersion 欄位,可讓酬載的接收者瞭解使用中的密碼編譯基元是哪些,以及這些基元的預期格式。

本指南將說明如何產生公開金鑰,藉此要求 Google 簽署及加密的付款方式代碼;另也提供驗證及解密該代碼的詳細步驟。

本指南僅適用於 protocolVersion = ECv2

由於您會直接收到付款卡資訊,因此在繼續操作之前,請先確認您的應用程式符合 PCI DSS 規範,伺服器也具備所需的基礎架構,可以安全地處理使用者的付款憑證。

以下簡單說明整合商使用 Google Pay API ECv2 PaymentMethodToken 酬載時必須採取的步驟:

  1. 擷取 Google 根簽署金鑰
  2. 透過任何未過期的根簽署金鑰,驗證中繼簽署金鑰的簽名是否有效。
  3. 驗證酬載的中繼簽署金鑰尚未過期。
  4. 透過中繼簽署金鑰,驗證酬載的簽名是否有效。
  5. 先驗證簽名,再將酬載的內容解密。
  6. 驗證訊息是否尚未過期。這個步驟需要您查看目前的時間是否早於解密內容中的 messageExpiration 欄位。
  7. 使用解密內容中的付款方式收費。

Tink 資料庫中的範例程式碼可用來執行步驟 1 到 6。

付款方式代碼的結構

Google 在 PaymentData 回應中傳回的訊息是採用 UTF-8 編碼的序列化 JSON 物件,其中包含下表指定的金鑰:

名稱 類型 說明
protocolVersion 字串 指出建立訊息時使用的加密或簽署配置。如有需要,此項目能讓通訊協定隨時間更新。
signature 字串 驗證訊息確實來自 Google。這個字串採用 Base64 編碼,而且是透過中繼簽署金鑰以 ECDSA 建立而成。
intermediateSigningKey 物件 內含 Google 中繼簽署金鑰的 JSON 物件,其中包含擁有 keyValuekeyExpirationsignaturessignedKey。為簡化中繼簽署金鑰的簽名驗證程序,這個物件已經過序列化處理。
signedMessage 字串 已序列化為字串的 JSON 物件,當中包含 encryptedMessageephemeralPublicKeytag。為簡化簽名的驗證程序,這個物件已經過序列化處理。

示例

以下是採 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 字串

處理方可解讀的專屬商家識別碼,用於驗證訊息的接受對象確實為提出要求的商家。由處理方建立,商家會透過 Android網頁版PaymentMethodTokenizationSpecification,將 ID 傳送給 Google。

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 安全密文。
eciIndicator 字串 不一定存在,系統只會針對 Visa 發卡機構的代碼傳回這個字串。這個值會透過付款授權要求傳送。

下列 JSON 程式碼片段為 CARD paymentMethod 適用的完整 encryptedMessage 示例,而且這個付款方式具備 CRYPTOGRAM_3DS 這種 authMethod

{
  "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_idprotocolVersionsignedKey) 都必須採用 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_idGoogle,則 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_idrecipient_idprotocolVersionsignedMessage) 都必須採用 UTF-8 編碼,且位元組長度均為 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,而 recipient_id 則為 merchant:merchantIdmerchantIdGoogle Pay 商家主控台中針對具備實際工作環境存取權商家所找到的值相符。

如果 sender_idGooglerecipient_idmerchant: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 定義)。

  • 橢圓曲線:NIST P-256 (在 OpenSSL 中也稱為 prime256v1)。
  • CheckModeOldCofactorModeSingleHashModeCofactorMode為 0。
  • 採未壓縮點格式。
金鑰衍生函式

採 HMAC 和 SHA-256 (HKDFwithSHA256)。

  • 請勿提供鹽。
  • 資訊必須是 Google 採用 ASCII 編碼而成,且適用於通訊協定版本 ECv2
  • 必須要有 256 位元是針對 AES256 金鑰衍生,另外 256 位元則必須針對 HMAC_SHA256 金鑰衍生。
對稱加密演算法

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

如何解密付款方式代碼

請按照下列步驟解密代碼:

  1. 使用您的私密金鑰和所取得的 ephemeralPublicKey,衍生出採用 ECIES-KEM 的 512 位元共用金鑰。請使用以下參數:
    • 橢圓曲線:NIST P-256 (在 OpenSSL 中又稱為「prime256v1」)。
    • CheckModeOldCofactorModeSingleHashModeCofactorMode0
    • 編碼函式:未壓縮點格式。
    • 金鑰衍生函式:HKDFwithSHA256 (如 RFC 5869 頁面中所述),其中包含下列參數:
      • 請勿提供 Salt。根據 RFC,這必須等同於 32 個零位元組的鹽。
  2. 將產生的金鑰分成兩組長度為 256 位元的金鑰:symmetricEncryptionKeymacKey
  3. 驗證 tag 欄位是 encryptedMessage 的有效 MAC。

    如要產生預期的 MAC,請使用具備雜湊函式 SHA256 的 HMAC (RFC 5869) 和步驟 2 中取得的 macKey

  4. 使用 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() 取得。

測試版和正式版環境的網址如下:

金鑰輪替

如果您在具備直接整合功能的伺服器上,直接將付款方式代碼解密,則必須每年輪替一次金鑰。

如要輪替加密金鑰,請完成下列步驟:

  1. 透過 OpenSSL 產生新的金鑰組
  2. 開啟 Google Pay 商家主控台,請確認開啟時,您已登入先前用來申請成為 Google Pay 開發人員的 Google 帳戶。
  3. 在「整合」分頁的「直接整合」窗格下方,按一下現有公開金鑰旁邊的 [管理]。按一下 [新增其他金鑰]
  4. 選取 [公開加密金鑰] 文字輸入欄位,然後新增您剛剛產生的未壓縮點格式 Base64 編碼公開金鑰。
  5. 按一下 [儲存加密金鑰]
  6. 為順利執行金鑰的輪替作業,請在替換金鑰時確保新舊私密金鑰均適用於解密作業。

    如果您使用 Tink 程式庫解密代碼,請使用下列 Java 程式碼支援使用多個私密金鑰:

    String decryptedMessage =
        new PaymentMethodTokenRecipient.Builder()
            .addRecipientPrivateKey(newPrivateKey)
            .addRecipientPrivateKey(oldPrivateKey);

    請確認解密作業的程式碼已部署至實際工作環境,解密作業也能順利完成。

  7. 變更程式碼中使用的公開金鑰。

    替換 PaymentMethodTokenizationSpecification parameters 屬性中的 publicKey 屬性值:

    const tokenizationSpecification = {
      "type": "DIRECT",
      "parameters": {
        "protocolVersion": "ECv2",
        "publicKey": "BOdoXP1aiNp.....kh3JUhiSZKHYF2Y="
      }
    }
  8. 將步驟 4 中的程式碼部署至實際工作環境。程式碼部署完畢之後,加密和解密交易就會使用新的金鑰組。
  9. 確認系統已不再使用舊的公開金鑰來加密任何交易。

  10. 移除舊的私密金鑰。
  11. 登入您先前用來申請成為 Google Pay 開發人員的 Google 帳戶,然後開啟 Google Pay 商家主控台
  12. 在「整合」分頁的「直接整合」窗格下方,按一下現有公開金鑰旁邊的 [管理]。按一下舊公開金鑰旁邊的 [刪除],然後按一下 [儲存加密金鑰]

Google 會使用 PaymentMethodTokenizationSpecification parameters 物件中 publicKey 屬性所指定的金鑰,如下例所示:

{
  "protocolVersion": "ECv2",
  "publicKey": "BOdoXP+9Aq473SnGwg3JU1..."
}

使用 Tink 程式庫管理加密回應

如要驗證簽名及解密訊息,請使用 Tink 密碼編譯資料庫。如要與 Tink 整合並執行驗證和解密作業,請完成下列步驟:

  1. 在您的 pom.xml 中,新增 Tink paymentmethodtoken 應用程式做為依附元件:

    <dependencies>
      <!-- other dependencies ... -->
      <dependency>
        <groupId>com.google.crypto.tink</groupId>
        <artifactId>apps-paymentmethodtoken</artifactId>
        <version>1.2.0</version>
      </dependency>
    </dependencies>
    
  2. 在伺服器啟動時,預先擷取 Google 簽署金鑰,讓該金鑰保存在記憶體中, 以防解密程序中的金鑰擷取作業產生網路延遲情況,對使用者造成不便。

    GooglePaymentsPublicKeysManager.INSTANCE_PRODUCTION.refreshInBackground();
  3. 使用下列程式碼來解密金鑰 (這個程式碼假設 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);
    
  4. 以您自己的金鑰取代 PrivateKey1PrivateKey2。變數可以是採用 Base64 編碼的 PKCS8 字串或 ECPrivateKey 物件。如要進一步瞭解如何產生採用 Base64 編碼的 PKCS8 私密金鑰,請參閱使用 OpenSSL 產生金鑰組的說明。

  5. 如果您無法在每次解密金鑰時都呼叫 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 簽署金鑰,並儲存至快取記憶體
    • 驗證簽名
    • 解密