Google Pay API trả về các phương thức thanh toán trong một tải trọng PaymentMethodToken đã ký và mã hoá. Các phương thức thanh toán được trả về là thẻ bao gồm PAN hoặc thẻ được mã hoá bao gồm PAN thiết bị và mật mã.
Tải trọng chứa một trường có tên là protocolVersion cho biết người nhận tải trọng đang sử dụng những nguyên tắc mật mã nào và định dạng dự kiến.
Hướng dẫn này cung cấp thông tin về cách tạo khoá công khai để yêu cầu mã thông báo phương thức thanh toán đã mã hoá và do Google ký, đồng thời trình bày chi tiết các bước cần thực hiện để xác minh và giải mã mã thông báo.
Hướng dẫn này chỉ áp dụng cho protocolVersion = ECv2.
Vì bạn nhận trực tiếp thông tin thẻ thanh toán, hãy đảm bảo ứng dụng của bạn tuân thủ PCI DSS và máy chủ của bạn có cơ sở hạ tầng cần thiết để xử lý an toàn thông tin đăng nhập thanh toán của người dùng trước khi bạn tiếp tục.
Các bước sau đây trình bày những việc mà một đơn vị tích hợp phải làm để sử dụng tải trọng ECv2 PaymentMethodToken của Google Pay API:
- Tìm nạp khoá ký gốc của Google.
- Xác minh rằng chữ ký của khoá ký trung gian hợp lệ bằng bất kỳ khoá ký gốc nào chưa hết hạn.
- Xác minh rằng khoá ký trung gian của tải trọng chưa hết hạn.
- Xác minh rằng chữ ký của tải trọng là hợp lệ bằng khoá ký trung gian.
- Giải mã nội dung của tải trọng sau khi bạn xác minh chữ ký.
- Xác minh rằng thông báo chưa hết hạn. Điều này yêu cầu bạn kiểm tra để đảm bảo rằng thời gian hiện tại nhỏ hơn trường
messageExpirationtrong nội dung đã giải mã. - Sử dụng phương thức thanh toán trong nội dung đã giải mã và tính phí.
Mã mẫu trong thư viện Tink của chúng tôi thực hiện các bước 1–6.
Cấu trúc mã thông báo phương thức thanh toán
Thông báo do Google trả về trong phản hồi PaymentData là một đối tượng JSON được mã hoá UTF-8, được chuyển đổi tuần tự với các khoá được chỉ định trong bảng sau:
| Tên | Loại | Mô tả |
|---|---|---|
protocolVersion |
Chuỗi | Xác định lược đồ mã hoá hoặc ký mà thư được tạo theo. Điều này cho phép giao thức phát triển theo thời gian (nếu cần). |
signature |
Chuỗi | Xác minh rằng tin nhắn đến từ Google. Khoá này được mã hoá base64 và được tạo bằng ECDSA bằng khoá ký trung gian. |
intermediateSigningKey |
Đối tượng | Một đối tượng JSON chứa khoá ký trung gian của Google. Nội dung này chứa signedKey có keyValue, keyExpiration và signatures. Khoá này được chuyển đổi tuần tự để đơn giản hoá quy trình xác minh chữ ký khoá ký trung gian. |
signedMessage |
Chuỗi | Một đối tượng JSON được chuyển đổi tuần tự dưới dạng một chuỗi an toàn cho HTML chứa encryptedMessage, ephemeralPublicKey và tag. Nó được chuyển đổi tuần tự để đơn giản hoá quy trình xác minh chữ ký. |
Ví dụ:
Sau đây là phản hồi mã thông báo phương thức thanh toán ở định dạng 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\"}" }
Khoá ký trung gian
intermediateSigningKey là một đối tượng JSON được mã hoá UTF-8 và chuyển đổi tuần tự, chứa các giá trị sau:
| Tên | Loại | Mô tả |
|---|---|---|
signedKey |
Chuỗi | Thông báo được mã hoá base64 chứa nội dung mô tả thanh toán của khoá. |
signatures |
Chuỗi | Xác minh rằng khoá ký trung gian đến từ Google. Khoá này được mã hoá base64 và được tạo bằng ECDSA. |
Khoá đã ký
signedKey là một đối tượng JSON được mã hoá UTF-8 và chuyển đổi tuần tự, chứa các giá trị sau:
| Tên | Loại | Mô tả |
|---|---|---|
keyValue |
Chuỗi | Một phiên bản base64 của khoá được mã hoá ở loại ASN.1. SubjectPublicKeyInfo được xác định trong tiêu chuẩn X.509. |
keyExpiration |
Chuỗi | Ngày và giờ khi khoá trung gian hết hạn dưới dạng mili giây UTC tính từ thời gian bắt đầu của hệ thống. Các đơn vị tích hợp sẽ từ chối mọi khoá đã hết hạn. |
Thư đã ký
signedMessage là một đối tượng JSON được mã hoá UTF-8 và chuyển đổi tuần tự, chứa các giá trị sau:
| Tên | Loại | Mô tả |
|---|---|---|
encryptedMessage |
Chuỗi | Một thông báo được mã hoá bằng base64 chứa thông tin thanh toán và một số trường bảo mật bổ sung. |
ephemeralPublicKey |
Chuỗi | Khoá công khai tạm thời được mã hoá dạng base64, được liên kết với khoá riêng tư để mã hoá thông báo ở định dạng điểm không nén. Để biết thêm thông tin, hãy xem phần Định dạng khoá công khai mã hoá. |
tag |
Chuỗi | Một MAC được mã hoá base64 của encryptedMessage. |
Tin nhắn đã mã hóa
encryptedMessage đã giải mã là một đối tượng JSON được chuyển đổi tuần tự và mã hoá bằng UTF-8. JSON này có 2 cấp. Cấp độ bên ngoài chứa siêu dữ liệu và các trường được đưa vào để đảm bảo an toàn, trong khi cấp độ bên trong là một đối tượng JSON khác đại diện cho thông tin xác thực thanh toán thực tế.
Để biết thêm thông tin về encryptedMessage, hãy xem các bảng sau và ví dụ về đối tượng JSON:
| Tên | Loại | Mô tả |
|---|---|---|
messageExpiration |
Chuỗi | Ngày và giờ hết hạn của thông báo tính bằng mili giây UTC kể từ thời gian bắt đầu của hệ thống. Đơn vị tích hợp phải từ chối mọi thông báo đã hết hạn. |
messageId |
Chuỗi | Mã nhận dạng duy nhất giúp xác định thông báo trong trường hợp cần thu hồi hoặc xác định vị trí thông báo vào thời điểm sau đó. |
paymentMethod |
Chuỗi | Loại thông tin xác thực thanh toán.
Hiện tại, chúng tôi chỉ hỗ trợ CARD.
|
paymentMethodDetails |
Đối tượng | Chính thông tin thanh toán. Định dạng của đối tượng này được xác định bằng paymentMethod và được mô tả trong các bảng sau. |
Thẻ
Các thuộc tính sau tạo nên thông tin xác thực thanh toán cho phương thức thanh toán CARD:
| Tên | Loại | Mô tả |
|---|---|---|
pan |
Chuỗi | Số tài khoản cá nhân bị tính phí. Chuỗi này chỉ chứa chữ số. |
expirationMonth |
Số | Tháng hết hạn của thẻ, trong đó 1 là tháng 1, 2 là tháng 2, v.v. |
expirationYear |
Số | Năm hết hạn gồm 4 chữ số của thẻ, chẳng hạn như 2020. |
authMethod |
Chuỗi | Phương thức xác thực của giao dịch bằng thẻ. |
PAN_ONLY
Đoạn mã JSON sau đây là một ví dụ về encryptedMessage đầy đủ cho một CARD paymentMethod
có 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
Một CARD được xác thực bằng cách sử dụng mật mã 3-D Secure, CRYPTOGRAM_3DS authMethod. Tệp này bao gồm các trường bổ sung sau:
| Tên | Loại | Mô tả |
|---|---|---|
cryptogram |
Chuỗi | Một mật mã bảo mật 3D. |
eciIndicator |
Chuỗi | Chuỗi này không phải lúc nào cũng xuất hiện. Tham số này chỉ trả về cho các giao dịch mã thông báo thiết bị đã xác thực trên Android (CRYPTOGRAM_3DS). Giá trị này phải được truyền xuống quy trình xử lý thanh toán. |
Đoạn mã JSON sau đây là một ví dụ về encryptedMessage đầy đủ cho một CARD paymentMethod có 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
Mạng thẻ có thể cung cấp eciIndicator cho các giao dịch mã thông báo thiết bị đã xác thực (CRYPTOGRAM_3DS).
Bạn phải truyền giá trị eciIndicator trên giao dịch uỷ quyền mà không được sửa đổi hoặc mã hoá cứng; nếu không, giao dịch sẽ thất bại. Bảng sau đây trình bày chi tiết các giá trị của eciIndicator.
| Giá trị eciIndicator | Mạng thẻ | Bên chịu trách nhiệm pháp lý | authMethod |
|---|---|---|---|
""(empty) |
Mastercard | Người bán/Đơn vị phát hành | CRYPTOGRAM_3DS |
02 |
Mastercard | Tổ chức phát hành thẻ | CRYPTOGRAM_3DS |
06 |
Mastercard | Người bán/Đơn vị phát hành | CRYPTOGRAM_3DS |
05 |
Visa | Tổ chức phát hành thẻ | CRYPTOGRAM_3DS |
07 |
Visa | Người bán/Đơn vị phát hành | CRYPTOGRAM_3DS |
""(empty) |
Các mạng khác | Người bán/Đơn vị phát hành | CRYPTOGRAM_3DS |
Mọi giá trị ECI khác cho VISA và Mastercard không có trong bảng này sẽ không được trả về.
Xác minh chữ ký
Để xác minh chữ ký (bao gồm cả chữ ký khoá trung gian và chữ ký thông báo), bạn cần có các mục sau:
- Thuật toán dùng để tạo chữ ký
- Chuỗi byte dùng để tạo chữ ký
- Khoá công khai tương ứng với khoá riêng tư dùng để tạo chữ ký
- Chính chữ ký
Thuật toán chữ ký
Google sử dụng Thuật toán chữ ký số đường cong Elliptic (ECDSA) để ký các thông báo bằng các tham số sau: ECDSA qua NIST P-256 với SHA-256 làm hàm băm, như được xác định trong FIPS 186-4.
Chữ ký
Chữ ký được đưa vào cấp ngoài cùng của thư. Khoá này được mã hoá bằng base64 ở định dạng byte ASN.1. Để biết thêm thông tin về ASN.1, hãy xem Phụ lục A về các công cụ của IETF. Chữ ký bao gồm các số nguyên r và s ECDSA. Để biết thêm thông tin, hãy xem phần Thuật toán tạo chữ ký.
Sau đây là ví dụ về định dạng byte ASN.1 được chỉ định, đây là định dạng tiêu chuẩn do các phương thức triển khai ECDSA của Tiện ích mã hoá Java (JCE) tạo ra.
ECDSA-Sig-Value :: = SEQUENCE {
r INTEGER,
s INTEGER
}Cách tạo chuỗi byte cho chữ ký khoá ký trung gian
Để xác thực chữ ký khoá ký trung gian trong mã thông báo phương thức thanh toán mẫu, hãy tạo signedStringForIntermediateSigningKeySignature bằng công thức sau:
signedStringForIntermediateSigningKeySignature = length_of_sender_id || sender_id || length_of_protocol_version || protocol_version || length_of_signed_key || signed_key
Ký hiệu "||" có nghĩa là nối. Mỗi thành phần (sender_id, protocolVersion, signedKey) phải được mã hoá bằng UTF-8. signedKey phải là chuỗi của intermediateSigningKey.signedKey.
Độ dài byte của mỗi thành phần là 4 byte ở định dạng little-endian.
Ví dụ:
Ví dụ này sử dụng mã thông báo phương thức thanh toán mẫu sau:
{
"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 luôn là Google và protocol_version là ECv2.
Nếu sender_id là Google, thì signedString sẽ xuất hiện như minh hoạ trong ví dụ sau:
signedStringForIntermediateSigningKeySignature =
\x06\x00\x00\x00 || Google || | \x04\x00\x00\x00 || ECv2 || \xb5\x00\x00\x00 || {"keyExpiration":"1542323393147","keyValue":"MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAE/1+3HBVSbdv+j7NaArdgMyoSAM43yRydzqdg1TxodSzA96Dj4Mc1EiKroxxunavVIvdxGnJeFViTzFvzFRxyCw\u003d\u003d"}Cách xác minh chữ ký trên signedStringForIntermediateSigningKeySignature
Thuật toán xác minh ECDSA tiêu chuẩn được dùng khi chuỗi đã ký cho chữ ký khoá ký trung gian được tập hợp. Đối với giao thức ECv2, bạn cần lặp lại tất cả các chữ ký trong intermediateSigningKey.signatures và cố gắng xác thực từng chữ ký bằng các khoá ký của Google chưa hết hạn trong keys.json. Nếu có ít nhất một quy trình xác thực chữ ký hoạt động, hãy coi như quy trình xác minh đã hoàn tất. Dùng intermediateSigningKey.signedKey.keyValue sau để xác minh signedStringForMessageSignature. Bạn nên sử dụng một thư viện mật mã hiện có thay vì mã xác minh của riêng bạn.
Cách tạo chuỗi byte cho chữ ký thông báo
Để xác thực chữ ký trong mã thông báo phương thức thanh toán mẫu, hãy tạo signedStringForMessageSignature bằng công thức sau:
signedStringForMessageSignature = length_of_sender_id || sender_id || length_of_recipient_id || recipient_id || length_of_protocolVersion || protocolVersion || length_of_signedMessage || signedMessage
Ký hiệu "||" có nghĩa là nối. Mỗi thành phần (sender_id, recipient_id, protocolVersion, signedMessage) phải được mã hoá UTF-8. Độ dài byte của mỗi thành phần là 4 byte ở định dạng little-endian. Khi bạn tạo chuỗi byte, đừng phân tích cú pháp hoặc sửa đổi signedMessage. Ví dụ: đừng thay thế \u003d bằng ký tự =.
Ví dụ:
Ví dụ sau đây là mã thông báo mẫu của phương thức thanh toán:
{
"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 luôn là Google và recipient_id là merchant:merchantId. merchantId khớp với giá trị có trong Bảng điều khiển Google Pay và Wallet đối với những người bán có quyền truy cập vào kênh phát hành công khai.
Nếu sender_id là Google và recipient_id là merchant:12345, thì signedString sẽ xuất hiện như trong ví dụ sau:
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"}Cách xác minh chữ ký trên signedStringForMessageSignature
Thuật toán xác minh ECDSA tiêu chuẩn được dùng khi chuỗi đã ký được tập hợp. intermediateSigningKey.signedKey.keyValue đã được xác minh ở bước trước được dùng để xác minh signedMessage. Bạn nên sử dụng một thư viện mật mã hiện có thay vì mã xác minh của riêng bạn.
Quy cách về sơ đồ mã hoá
Google sử dụng Elliptic Curve Integrated Encryption Scheme (ECIES) để bảo mật mã thông báo phương thức thanh toán được trả về trong phản hồi của Google Pay API. Lược đồ mã hoá sử dụng các tham số sau:
| Tham số | Định nghĩa |
|---|---|
| Phương pháp đóng gói khoá | ECIES-KEM, theo định nghĩa trong ISO 18033-2.
|
| Hàm dẫn xuất khoá | Dựa trên HMAC với SHA-256 (
|
| Thuật toán mã hoá đối xứng |
DEM2, theo định nghĩa trong ISO 18033-2 Thuật toán mã hoá: AES-256-CTR không có IV và không được đệm. |
| Thuật toán MAC | HMAC_SHA256 bằng khoá 256 bit được lấy từ hàm dẫn xuất khoá. |
Định dạng khoá công khai mã hoá
Khoá công khai mã hoá và ephemeralPublicKey được trả về trong tải trọng của Google được định dạng bằng bản trình bày base64 của khoá ở định dạng điểm không nén. Nó bao gồm 2 phần tử sau:
- Một số nhận dạng đặc biệt chỉ định định dạng (0x04).
- Hai số nguyên lớn 32 byte biểu thị toạ độ X và Y trong Đường cong Elliptic.
Định dạng này được mô tả chi tiết hơn trong "Mã hoá khoá công khai cho ngành dịch vụ tài chính: Thuật toán chữ ký số đường cong elip (ECDSA)", ANSI X9.62, 1998.
Sử dụng OpenSSL để tạo khoá công khai
Bước 1: Tạo khoá riêng tư
Ví dụ sau đây sẽ tạo một khoá riêng tư Đường cong elip phù hợp để sử dụng với NIST P-256 và ghi khoá đó vào key.pem:
openssl ecparam -name prime256v1 -genkey -noout -out key.pem
Không bắt buộc: Xem khoá riêng tư và khoá công khai
Sử dụng lệnh sau để xem cả khoá riêng tư và khoá công khai:
openssl ec -in key.pem -pubout -text -noout
Lệnh này tạo ra kết quả tương tự như sau:
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
Bước 2: Tạo khoá công khai được mã hoá dạng base64
Khoá riêng tư và khoá công khai được tạo trong ví dụ bước không bắt buộc trước đó được mã hoá bằng hệ thập lục phân. Để lấy khoá công khai được mã hoá ở dạng base64 ở định dạng điểm chưa nén, hãy dùng lệnh sau:
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
Lệnh này tạo ra một tệp publicKey.txt có nội dung (phiên bản base64 của khoá ở định dạng điểm chưa nén) tương tự như sau:
BOdoXP+9Aq473SnGwg3JU1aiNpsd9vH2ognq4PtDtlLGa3Kj8TPf+jaQNPyDSkh3JUhiS0KyrrlWhAgNZKHYF2Y=
Nội dung tệp không được có thêm khoảng trống hoặc dấu xuống dòng. Để xác minh điều này, hãy chạy lệnh sau trong Linux hoặc MacOS:
od -bc publicKey.txt
Bước 3: Tạo khoá riêng tư được mã hoá base64 ở định dạng PKCS #8
Thư viện Tink yêu cầu khoá riêng tư của bạn được mã hoá ở dạng base64 theo định dạng PKCS #8. Sử dụng lệnh sau để tạo khoá riêng tư ở định dạng này từ khoá riêng tư được tạo ở bước đầu tiên:
openssl pkcs8 -topk8 -inform PEM -outform DER -in key.pem -nocrypt | base64 | paste -sd "\0" -
Lệnh này tạo ra kết quả tương tự như sau:
MIGHAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBG0wawIBAQQgWV4oK8c/MZkCLk4qSCNjW0Zm6H0CBCtSYxkXkC9FBHehRANCAAQPldOnhO2/oXjdJD1dwlFPiNs6fcdoRgFu3/Z0iKj24SjTGyLRGAtYWLGXBZcDdPj3T2bJRHRVhE8Bc2AjkT7n
Cách giải mã mã thông báo phương thức thanh toán
Làm theo các bước sau để giải mã mã thông báo:
- Sử dụng khoá riêng tư và
ephemeralPublicKeyđã cho để lấy khoá dùng chung có độ dài 512 bit sử dụng ECIES-KEM. Sử dụng các tham số sau: - Đường cong elip: NIST P-256, còn được gọi là prime256v1 trong OpenSSL.
CheckMode,OldCofactorMode,SingleHashModevàCofactorModelà0.- Hàm mã hoá: Định dạng điểm không nén.
- Hàm phái sinh khoá: HKDFwithSHA256, như mô tả trong RFC 5869, với tham số sau:
- Bạn không được cung cấp salt. Theo RFC, giá trị này phải tương đương với một chuỗi ngẫu nhiên gồm 32 byte được đặt thành 0.
- Chia khoá đã tạo thành hai khoá có độ dài 256 bit:
symmetricEncryptionKeyvàmacKey. Xác minh rằng trường
taglà một MAC hợp lệ choencryptedMessage.Để tạo MAC dự kiến, hãy sử dụng HMAC (RFC 5869) với hàm băm SHA256 và
macKeythu được ở Bước 2.Giải mã
encryptedMessagebằng chế độ AES-256-CTR và với những nội dung sau:- IV bằng 0.
- Không có khoảng trống.
symmetricEncryptionKeyđược lấy ở Bước 2.
Quản lý khoá
Khoá mã hoá của người bán
Người bán tạo khoá công khai theo quy cách được nêu trong Quy cách về sơ đồ mã hoá.
Khoá ký gốc của Google
Google xuất bản bộ khoá công khai ký gốc hiện đang hợp lệ mà bạn có thể tìm nạp từ một URL công khai. Các khoá này sẽ hợp lệ cho đến khi tiêu đề bộ nhớ đệm HTTP do URL trả về cho biết. Các khoá này được lưu vào bộ nhớ đệm cho đến khi hết hạn. Thời gian hết hạn được xác định bằng trường keyExpiration. Khi một lần tìm nạp hết hạn, bạn nên tìm nạp lại các khoá từ URL công khai để nhận danh sách khoá hợp lệ hiện tại.
Trường hợp ngoại lệ đối với giao thức ECv2: Nếu không thể tìm nạp các khoá từ Google trong thời gian chạy, hãy tìm nạp keys.json từ URL phát hành công khai của chúng tôi, lưu khoá đó vào hệ thống của bạn và định kỳ làm mới theo cách thủ công. Trong trường hợp bình thường, Google sẽ phát hành một khoá ký gốc mới cho ECv2 5 năm trước khi khoá có ngày hết hạn dài nhất hết hạn. Trong trường hợp khoá bị xâm phạm, Google sẽ thông báo cho tất cả người bán thông qua thông tin liên hệ được cung cấp trong cổng tự phục vụ để yêu cầu tải lại keys.json nhanh hơn. Để đảm bảo bạn không bỏ lỡ quy trình xoay vòng thường xuyên, những người bán chọn lưu khoá Google trong nội dung của keys.json nên làm mới hằng năm trong quy trình xoay vòng khoá hằng năm của riêng họ.
Các khoá được cung cấp thông qua URL công khai được liên kết theo định dạng sau:
{ "keys": [ { "keyValue": "encoded public key", "protocolVersion": "ECv2" "keyExpiration":"2000000000000" }, { "keyValue": "encoded public key", "protocolVersion": "ECv2" "keyExpiration":"3000000000000" } ] }
keyValue là phiên bản base64 của khoá được mã hoá trong loại ASN.1 SubjectPublicKeyInfo (không được bao bọc hoặc thêm phần đệm) được xác định trong tiêu chuẩn X.509. Trong Java, phương thức mã hoá ASN.1 được tham chiếu được biểu thị bằng lớp X509EncodedKeySpec.
Bạn có thể lấy được thông tin này bằng ECPublicKey.getEncoded().
URL cho cả môi trường kiểm thử và môi trường phát hành công khai được cung cấp theo các đường liên kết sau:
- Thử nghiệm:
https://payments.developers.google.com/paymentmethodtoken/test/keys.json - Sản xuất:
https://payments.developers.google.com/paymentmethodtoken/keys.json
Xoay vòng khoá
Nếu giải mã mã thông báo phương thức thanh toán trực tiếp trên máy chủ của mình bằng cách tích hợp trực tiếp, thì bạn phải xoay vòng các khoá hằng năm.
Hoàn tất các bước sau để xoay khoá mã hoá:
- Sử dụng OpenSSL để tạo một cặp khoá mới.
- Mở Bảng điều khiển Google Pay và Wallet trong khi đăng nhập bằng Tài khoản Google trước đây được dùng để quản lý ứng dụng của bạn bằng Google Play.
- Trong thẻ Google Pay API, trong ngăn Tích hợp trực tiếp, hãy nhấp vào Quản lý bên cạnh khoá công khai hiện có của bạn. Nhấp vào Thêm khoá khác.
- Chọn trường nhập văn bản Khoá mã hoá công khai rồi thêm khoá công khai mới tạo được mã hoá ở dạng base64 theo định dạng điểm không nén.
- Nhấp vào Lưu khoá mã hoá.
Để đảm bảo quá trình xoay vòng khoá diễn ra suôn sẻ, hãy hỗ trợ cả khoá riêng tư mới và cũ để giải mã trong khi bạn chuyển đổi khoá.
Nếu bạn sử dụng thư viện Tink để giải mã mã thông báo, hãy dùng mã Java sau để hỗ trợ nhiều khoá riêng tư:
String decryptedMessage = new PaymentMethodTokenRecipient.Builder() .addRecipientPrivateKey(newPrivateKey) .addRecipientPrivateKey(oldPrivateKey);
Đảm bảo rằng mã để giải mã được triển khai cho bản phát hành chính thức và bạn giám sát các hoạt động giải mã thành công.
Thay đổi khoá công khai được dùng trong mã của bạn.
Thay thế giá trị của thuộc tính
publicKeytrong thuộc tínhPaymentMethodTokenizationSpecificationparameters:/** * @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; }
- Triển khai mã từ bước 4 vào môi trường thực tế. Sau khi mã được triển khai, các giao dịch mã hoá và giải mã sẽ sử dụng các cặp khoá mới.
Xác nhận rằng khoá công khai cũ không còn được dùng để mã hoá bất kỳ giao dịch nào.
- Xoá khoá riêng tư cũ.
- Mở Bảng điều khiển Google Pay và Wallet trong khi đăng nhập bằng Tài khoản Google mà bạn đã dùng trước đây để đăng ký làm nhà phát triển bằng Google Pay.
- Trong thẻ Google Pay API, trong ngăn Tích hợp trực tiếp, hãy nhấp vào Quản lý bên cạnh khoá công khai hiện có. Nhấp vào Xoá bên cạnh khoá công khai cũ rồi nhấp vào Lưu khoá mã hoá.
Google sử dụng khoá được chỉ định trong thuộc tính publicKey trong đối tượng PaymentMethodTokenizationSpecification parameters, như minh hoạ trong ví dụ sau:
{
"protocolVersion": "ECv2",
"publicKey": "BOdoXP+9Aq473SnGwg3JU1..."
}Sử dụng thư viện Tink để quản lý phản hồi được mã hoá
Để thực hiện quy trình xác minh chữ ký và giải mã thông báo, hãy sử dụng thư viện paymentmethodtoken của Tink. Thư viện này chỉ có trong Java. Để sử dụng, hãy hoàn tất các bước sau:
Trong
pom.xml, hãy thêm ứng dụngpaymentmethodtokenTink làm phần phụ thuộc:<dependencies> <!-- other dependencies ... --> <dependency> <groupId>com.google.crypto.tink</groupId> <artifactId>apps-paymentmethodtoken</artifactId> <version>1.9.1</version> <!-- or latest version --> </dependency> </dependencies>Khi khởi động máy chủ, hãy tìm nạp trước các khoá ký Google để có thể sử dụng khoá trong bộ nhớ. Điều này ngăn người dùng nhìn thấy bất kỳ độ trễ mạng nào trong khi quy trình giải mã tìm nạp các khoá.
GooglePaymentsPublicKeysManager.INSTANCE_PRODUCTION.refreshInBackground();
Giải mã thông báo bằng mã sau. Mã này giả định rằng
paymentMethodTokenđược lưu trữ trong biếnencryptedMessagevà thay thế các phần được in đậm theo tình huống của bạn.Đối với các kiểm thử không phải là kiểm thử trong môi trường phát hành công khai, hãy thay thế
INSTANCE_PRODUCTIONbằngINSTANCE_TEST. Nếu chế độ tích hợp của bạn không hoạt động hoặc chưa được định cấu hình khoá mã hoá, hãy thay thế [YOUR MERCHANT ID] bằng.- Đang hoạt động
- Đã bật tính năng tích hợp TRỰC TIẾP
- Đã định cấu hình khoá mã hoá
không thay thế [YOUR MERCHANT ID].
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);
Thay thế
PrivateKey1bằng giá trị khoá riêng tư thích hợp liên kết với giá trị khoá công khai đã đăng ký với Google trong phần Chuẩn bị khoá và đăng ký với Google. Sau này, bạn có thể thêm nhiều giá trị khoá riêng tư khác khi cần Xoay khoá bằng Google. Các biến có thể là một chuỗi PKCS8 được mã hoá base64 hoặc một đối tượngECPrivateKey. Để biết thêm thông tin về cách tạo khoá riêng tư PKCS8 được mã hoá base64, hãy xem phần Chuẩn bị khoá và đăng ký với Google.Nếu không thể gọi máy chủ của Google mỗi khi bạn giải mã khoá, hãy giải mã bằng mã sau và thay thế các phần in đậm theo tình huống của bạn.
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);
Khoá hiện tại trong môi trường sản xuất có hiệu lực đến ngày 14/04/2038 trong điều kiện bình thường, ngoại trừ trường hợp khoá bị xâm phạm. Trong trường hợp khoá bị xâm nhập, Google sẽ thông báo cho tất cả người bán thông qua thông tin liên hệ được cung cấp trong cổng tự phục vụ để yêu cầu tải lại
keys.jsonnhanh hơn.Đoạn mã này xử lý các thông tin bảo mật sau đây để bạn có thể tập trung vào việc sử dụng tải trọng:
- Khoá ký của Google được tìm nạp và lưu vào bộ nhớ đệm trong bộ nhớ
- Xác minh chữ ký
- Giải mã