Google Pay API จะแสดงผลวิธีการชำระเงินในเพย์โหลด PaymentMethodToken ที่ลงชื่อและเข้ารหัสแล้ว วิธีการชำระเงินที่แสดงผลจะเป็นบัตรที่ประกอบด้วย PAN หรือบัตรที่แปลงเป็นโทเค็นซึ่งประกอบด้วย PAN ของอุปกรณ์และคริปโตแกรม
เพย์โหลดมีฟิลด์ชื่อ protocolVersion ซึ่งจะบอกผู้รับเพย์โหลดว่ามีการใช้พื้นฐานการเข้ารหัสใดและรูปแบบที่คาดไว้
คู่มือนี้จะให้ข้อมูลเกี่ยวกับวิธีสร้างคีย์สาธารณะเพื่อขอโทเค็นวิธีการชำระเงินที่เข้ารหัสและลงนามโดย Google รวมถึงรายละเอียดขั้นตอนในการยืนยันและถอดรหัสโทเค็น
คู่มือนี้ใช้กับ protocolVersion = ECv2 เท่านั้น
เนื่องจากคุณได้รับข้อมูลบัตรสำหรับชำระเงินโดยตรง โปรดตรวจสอบว่าแอปเป็นไปตามมาตรฐาน PCI DSS และเซิร์ฟเวอร์มีโครงสร้างพื้นฐานที่จำเป็นในการจัดการข้อมูลเข้าสู่ระบบการชำระเงินของผู้ใช้อย่างปลอดภัยก่อนดำเนินการต่อ
ขั้นตอนต่อไปนี้สรุปสิ่งที่ผู้ผสานรวมต้องทำเพื่อใช้เพย์โหลด ECv2 PaymentMethodToken ของ Google Pay API
- ดึงข้อมูลคีย์การรับรองรูทของ Google
- ยืนยันว่าลายเซ็นของคีย์การรับรองระดับกลางถูกต้องโดยใช้คีย์การรับรองรูทที่ยังไม่หมดอายุ
- ตรวจสอบว่าคีย์การรับรองระดับกลางของเพย์โหลดยังไม่หมดอายุ
- ยืนยันว่าลายเซ็นของเพย์โหลดถูกต้องตามคีย์การลงนามระดับกลาง
- ถอดรหัสเนื้อหาของเพย์โหลดหลังจากยืนยันลายเซ็นแล้ว
- ตรวจสอบว่าข้อความยังไม่หมดอายุ ซึ่งกำหนดให้คุณต้องตรวจสอบว่าเวลาปัจจุบันน้อยกว่าฟิลด์
messageExpirationในเนื้อหาที่ถอดรหัสแล้ว - ใช้วิธีการชำระเงินในเนื้อหาที่ถอดรหัสแล้วและเรียกเก็บเงิน
โค้ดตัวอย่างในไลบรารี Tink จะทำตามขั้นตอนที่ 1-6
โครงสร้างโทเค็นของวิธีการชำระเงิน
ข้อความที่ Google แสดงในการตอบกลับ PaymentData เป็นออบเจ็กต์ JSON ที่เข้ารหัส UTF-8 และเรียงลำดับแล้ว โดยมีคีย์ที่ระบุไว้ในตารางต่อไปนี้
| ชื่อ | ประเภท | คำอธิบาย |
|---|---|---|
protocolVersion |
สตริง | ระบุรูปแบบการเข้ารหัสหรือการลงนามที่ใช้สร้างข้อความ ซึ่งช่วยให้โปรโตคอลพัฒนาขึ้นเมื่อเวลาผ่านไป หากจำเป็น |
signature |
สตริง | ยืนยันว่าข้อความมาจาก Google ซึ่งเข้ารหัส base64 และสร้างด้วย ECDSA โดยคีย์การรับรองระดับกลาง |
intermediateSigningKey |
วัตถุ | ออบเจ็กต์ JSON ที่มีคีย์การรับรองระดับกลางจาก Google โดยมี signedKey พร้อมด้วย keyValue, keyExpiration และ signatures ซึ่งจะได้รับการจัดเรียงเป็นอนุกรมเพื่อลดความซับซ้อนของกระบวนการยืนยันลายเซ็นคีย์การรับรองระดับกลาง |
signedMessage |
สตริง | ออบเจ็กต์ JSON ที่เรียงลำดับเป็นสตริงที่ปลอดภัยสำหรับ HTML ซึ่งมี 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\"}" }
คีย์ Signing ระดับกลาง
intermediateSigningKey คือออบเจ็กต์ JSON ที่เข้ารหัส UTF-8 และเรียงลำดับแล้ว ซึ่งมีค่าต่อไปนี้
| ชื่อ | ประเภท | คำอธิบาย |
|---|---|---|
signedKey |
สตริง | ข้อความที่เข้ารหัส Base64 ซึ่งมีคำอธิบายการชำระเงินของคีย์ |
signatures |
สตริง | ยืนยันว่าคีย์การรับรองระดับกลางมาจาก Google ซึ่งเข้ารหัส Base64 และสร้างด้วย ECDSA |
คีย์ที่ลงนามแล้ว
signedKey คือออบเจ็กต์ JSON ที่เข้ารหัส UTF-8 และเรียงลำดับแล้ว ซึ่งมีค่าต่อไปนี้
| ชื่อ | ประเภท | คำอธิบาย |
|---|---|---|
keyValue |
สตริง | คีย์เวอร์ชัน base64 ที่เข้ารหัสในประเภท ASN.1 SubjectPublicKeyInfo กำหนดไว้ในมาตรฐาน X.509 |
keyExpiration |
สตริง | วันที่และเวลาที่คีย์ระดับกลางจะหมดอายุเป็นมิลลิวินาที UTC นับตั้งแต่ Epoch ผู้ผสานรวมจะปฏิเสธคีย์ที่หมดอายุ |
ข้อความที่ลงชื่อกำกับ
signedMessage คือออบเจ็กต์ JSON ที่เข้ารหัส UTF-8 และเรียงลำดับแล้ว ซึ่งมีค่าต่อไปนี้
| ชื่อ | ประเภท | คำอธิบาย |
|---|---|---|
encryptedMessage |
สตริง | ข้อความที่เข้ารหัสแบบ Base64 ซึ่งมีข้อมูลการชำระเงินและช่องความปลอดภัยเพิ่มเติมบางช่อง |
ephemeralPublicKey |
สตริง | คีย์สาธารณะชั่วคราวที่เข้ารหัส Base64 ซึ่งเชื่อมโยงกับคีย์ส่วนตัวเพื่อเข้ารหัสข้อความในรูปแบบจุดที่ไม่มีการบีบอัด ดูข้อมูลเพิ่มเติมได้ที่รูปแบบคีย์สาธารณะสำหรับการเข้ารหัส |
tag |
สตริง | MAC ที่เข้ารหัส Base64 ของ encryptedMessage |
ข้อความที่เข้ารหัส
encryptedMessage ที่ถอดรหัสแล้วคือออบเจ็กต์ JSON ที่เข้ารหัสแบบ UTF-8 และแปลงเป็นอนุกรม JSON มี 2 ระดับ ระดับภายนอกประกอบด้วยข้อมูลเมตาและช่องที่รวมไว้เพื่อความปลอดภัย ส่วนระดับภายในเป็นออบเจ็กต์ JSON อีกรายการหนึ่งที่แสดงข้อมูลเข้าสู่ระบบการชำระเงินจริง
ดูรายละเอียดเพิ่มเติมเกี่ยวกับ encryptedMessage ได้ในตารางและตัวอย่างออบเจ็กต์ JSON ต่อไปนี้
| ชื่อ | ประเภท | คำอธิบาย |
|---|---|---|
messageExpiration |
สตริง | วันที่และเวลาที่ข้อความหมดอายุเป็นมิลลิวินาที UTC นับตั้งแต่ Epoch ผู้ผสานรวมควรปฏิเสธข้อความที่หมดอายุ |
messageId |
สตริง | รหัสที่ไม่ซ้ำกันซึ่งระบุข้อความในกรณีที่ต้องเพิกถอนหรือค้นหาในภายหลัง |
paymentMethod |
สตริง | ประเภทของข้อมูลเข้าสู่ระบบสำหรับการชำระเงิน
ปัจจุบันรองรับเฉพาะ CARD เท่านั้น
|
paymentMethodDetails |
วัตถุ | ข้อมูลเข้าสู่ระบบสำหรับการชำระเงิน รูปแบบของออบเจ็กต์นี้จะกำหนดโดย paymentMethod และอธิบายไว้ในตารางต่อไปนี้ |
การ์ด
พร็อพเพอร์ตี้ต่อไปนี้ประกอบขึ้นเป็นข้อมูลเข้าสู่ระบบสำหรับการชำระเงินด้วยวิธีการชำระเงิน CARD
| ชื่อ | ประเภท | คำอธิบาย |
|---|---|---|
pan |
สตริง | หมายเลขบัญชีส่วนตัวที่เรียกเก็บเงิน สตริงนี้มีเฉพาะตัวเลขเท่านั้น |
expirationMonth |
ตัวเลข | เดือนที่บัตรหมดอายุ โดย 1 แทนเดือนมกราคม 2 แทนเดือนกุมภาพันธ์ และอื่นๆ |
expirationYear |
ตัวเลข | ปีที่หมดอายุของบัตรซึ่งเป็นตัวเลข 4 หลัก เช่น 2020 |
authMethod |
สตริง | วิธีการตรวจสอบสิทธิ์ของธุรกรรมบัตร |
merchantTokenId |
สตริง | รหัสโทเค็นของผู้ขายของธุรกรรมบัตร ระบบจะแสดงช่องที่ไม่บังคับนี้ก็ต่อเมื่อเป็นไปตามเงื่อนไขต่อไปนี้ทั้งหมด
|
PAN_ONLY
ข้อมูลโค้ด JSON ต่อไปนี้เป็นตัวอย่างของ encryptedMessage แบบเต็มสำหรับ CARD paymentMethod
ที่มี 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
CARD ได้รับการตรวจสอบสิทธิ์โดยใช้คริปโตแกรม 3-D Secure CRYPTOGRAM_3DS authMethod โดยจะมีช่องเพิ่มเติมต่อไปนี้
| ชื่อ | ประเภท | คำอธิบาย |
|---|---|---|
cryptogram |
สตริง | คริปโตแกรม 3-D Secure |
eciIndicator |
สตริง | สตริงนี้ไม่ได้แสดงอยู่เสมอ โดยจะแสดงผลเฉพาะธุรกรรมโทเค็นของอุปกรณ์ที่ตรวจสอบสิทธิ์แล้วใน Android (CRYPTOGRAM_3DS) เท่านั้น ค่านี้ต้องส่งผ่านขั้นตอนการประมวลผลการชำระเงิน |
ข้อมูลโค้ด JSON ต่อไปนี้เป็นตัวอย่างของ encryptedMessage แบบเต็มสำหรับ paymentMethod CARD ที่มี authMethod CRYPTOGRAM_3DS
{
"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
เครือข่ายบัตรอาจระบุ eciIndicator สำหรับธุรกรรมโทเค็นอุปกรณ์ที่ตรวจสอบสิทธิ์แล้ว (CRYPTOGRAM_3DS)
คุณต้องส่งค่า eciIndicator ในธุรกรรมการให้สิทธิ์โดยไม่มีการแก้ไขหรือฮาร์ดโค้ด มิฉะนั้นธุรกรรมจะดำเนินการไม่สำเร็จ ตารางต่อไปนี้แสดงรายละเอียดค่าของ eciIndicator
| ค่า eciIndicator | เครือข่ายบัตร | ฝ่ายที่รับผิดชอบ | authMethod |
|---|---|---|---|
""(empty) |
Mastercard | ผู้ขาย/ผู้รับบัตร | CRYPTOGRAM_3DS |
02 |
Mastercard | ผู้ออกบัตร | CRYPTOGRAM_3DS |
06 |
Mastercard | ผู้ขาย/ผู้รับบัตร | CRYPTOGRAM_3DS |
05 |
Visa | ผู้ออกบัตร | CRYPTOGRAM_3DS |
07 |
Visa | ผู้ขาย/ผู้รับบัตร | CRYPTOGRAM_3DS |
""(empty) |
เครือข่ายอื่น | ผู้ขาย/ผู้รับบัตร | CRYPTOGRAM_3DS |
ระบบจะไม่แสดงค่า ECI อื่นๆ สำหรับ VISA และ Mastercard ที่ไม่อยู่ในตารางนี้
การยืนยันลายเซ็น
หากต้องการยืนยันลายเซ็น ซึ่งรวมถึงคีย์ระดับกลางและลายเซ็นข้อความ คุณต้องมีรายการต่อไปนี้
- อัลกอริทึมที่ใช้สร้างลายเซ็น
- สตริงไบต์ที่ใช้สร้างลายเซ็น
- คีย์สาธารณะที่สอดคล้องกับคีย์ส่วนตัวที่ใช้สร้างลายเซ็น
- ลายเซ็น
อัลกอริทึมลายเซ็น
Google ใช้อัลกอริทึมของลายเซ็นดิจิทัลแบบ Elliptic Curve (ECDSA) เพื่อลงนามข้อความด้วยพารามิเตอร์ต่อไปนี้ ECDSA ผ่าน NIST P-256 โดยมี SHA-256 เป็นฟังก์ชันแฮชตามที่กำหนดไว้ใน FIPS 186-4
ลายเซ็น
ลายเซ็นจะรวมอยู่ในระดับนอกสุดของข้อความ โดยจะเข้ารหัสด้วย base64 ในรูปแบบไบต์ ASN.1 ดูข้อมูลเพิ่มเติมเกี่ยวกับ ASN.1 ได้ที่ภาคผนวก A ของเครื่องมือ IETF ลายเซ็นประกอบด้วยจำนวนเต็ม r และ s ของ ECDSA ดูข้อมูลเพิ่มเติมได้ที่อัลกอริทึมการสร้างลายเซ็น
ต่อไปนี้เป็นตัวอย่างของรูปแบบไบต์ ASN.1 ที่ระบุ ซึ่งเป็นรูปแบบมาตรฐานที่สร้างโดยการใช้งาน ECDSA ของ Java Cryptography Extension (JCE)
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 ไบต์ในรูปแบบ Little-Endian
ตัวอย่าง
ตัวอย่างนี้ใช้โทเค็นวิธีการชำระเงินตัวอย่างต่อไปนี้
{
"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 และพยายามตรวจสอบลายเซ็นแต่ละรายการด้วยคีย์การรับรองของ Google ที่ยังไม่หมดอายุใน keys.json หากการตรวจสอบลายเซ็นอย่างน้อย 1 รายการทำงานได้ ให้ถือว่าการยืนยันเสร็จสมบูรณ์ ใช้ 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 ไบต์ในรูปแบบ Little-Endian เมื่อสร้างสตริงไบต์ อย่าแยกวิเคราะห์หรือแก้ไข 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 & Wallet Console สำหรับผู้ขายที่มีสิทธิ์เข้าถึงเวอร์ชันที่ใช้งานจริง
หาก 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 ใช้ Elliptic Curve Integrated Encryption Scheme (ECIES) เพื่อรักษาความปลอดภัยของโทเค็นวิธีการชำระเงินที่แสดงในการตอบกลับจาก API ของ Google Pay รูปแบบการเข้ารหัสใช้พารามิเตอร์ต่อไปนี้
| พารามิเตอร์ | คำจำกัดความ |
|---|---|
| วิธีการห่อหุ้มคีย์ | ECIES-KEM ตามที่กำหนดไว้ใน ISO 18033-2
|
| ฟังก์ชันการรับคีย์ | HMAC-based with SHA-256 (
|
| อัลกอริทึมการเข้ารหัสแบบสมมาตร |
DEM2 ตามที่กำหนดไว้ใน ISO 18033-2 อัลกอริทึมการเข้ารหัส: AES-256-CTR ที่มี IV เป็น 0 และไม่มีการเติม |
| อัลกอริทึม MAC | HMAC_SHA256 พร้อมคีย์ 256 บิตที่ได้มาจากฟังก์ชันการรับคีย์ |
รูปแบบคีย์สาธารณะการเข้ารหัส
คีย์สาธารณะสำหรับการเข้ารหัสและ ephemeralPublicKey ที่แสดงในเพย์โหลดของ Google จะอยู่ในรูปแบบการแสดงคีย์ base64 ในรูปแบบจุดที่ไม่มีการบีบอัด ซึ่งประกอบด้วย 2 องค์ประกอบต่อไปนี้
- หมายเลขวิเศษ 1 หมายเลขที่ระบุรูปแบบ (0x04)
- จำนวนเต็มขนาดใหญ่ 32 ไบต์ 2 จำนวนที่แสดงพิกัด X และ Y ในเส้นโค้งวงรี
รูปแบบนี้อธิบายไว้อย่างละเอียดใน "วิทยาการเข้ารหัสคีย์สาธารณะสำหรับอุตสาหกรรมบริการทางการเงิน: อัลกอริทึมของลายเซ็นดิจิทัลแบบเส้นโค้งวงรี (ECDSA)" ANSI X9.62, 1998
ใช้ OpenSSL เพื่อสร้างคีย์สาธารณะ
ขั้นตอนที่ 1: สร้างคีย์ส่วนตัว
ตัวอย่างต่อไปนี้จะสร้างคีย์ส่วนตัว Elliptic Curve ที่เหมาะสำหรับใช้กับ 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
คีย์ส่วนตัวและคีย์สาธารณะที่สร้างขึ้นในตัวอย่างขั้นตอนที่ไม่บังคับก่อนหน้านี้จะเข้ารหัสเป็นเลขฐาน 16 หากต้องการรับคีย์สาธารณะที่เข้ารหัส 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: สร้างคีย์ส่วนตัวที่เข้ารหัส Base64 ในรูปแบบ PKCS #8
ไลบรารี Tink คาดหวังให้คีย์ส่วนตัวของคุณเข้ารหัสฐาน 64 ในรูปแบบ PKCS #8 ใช้คำสั่งต่อไปนี้เพื่อสร้างคีย์ส่วนตัวในรูปแบบนี้จากคีย์ส่วนตัวที่สร้างขึ้นในขั้นตอนแรก
openssl pkcs8 -topk8 -inform PEM -outform DER -in key.pem -nocrypt | base64 | paste -sd "\0" -
คำสั่งจะสร้างเอาต์พุตที่คล้ายกับต่อไปนี้
MIGHAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBG0wawIBAQQgWV4oK8c/MZkCLk4qSCNjW0Zm6H0CBCtSYxkXkC9FBHehRANCAAQPldOnhO2/oXjdJD1dwlFPiNs6fcdoRgFu3/Z0iKj24SjTGyLRGAtYWLGXBZcDdPj3T2bJRHRVhE8Bc2AjkT7n
วิธีถอดรหัสโทเค็นวิธีการชำระเงิน
ทำตามขั้นตอนต่อไปนี้เพื่อถอดรหัสโทเค็น
- ใช้คีย์ส่วนตัวและ
ephemeralPublicKeyที่ระบุเพื่อรับคีย์ที่ใช้ร่วมกันแบบ 512 บิตซึ่งใช้ ECIES-KEM ใช้พารามิเตอร์ต่อไปนี้ - Elliptic curve: NIST P-256 หรือที่เรียกว่า prime256v1 ใน OpenSSL
CheckMode,OldCofactorMode,SingleHashModeและCofactorModeเป็น0- ฟังก์ชันการเข้ารหัส: รูปแบบจุดที่ไม่มีการบีบอัด
- ฟังก์ชันการสืบทอดคีย์: HKDFwithSHA256 ตามที่อธิบายไว้ใน RFC 5869 โดยมีพารามิเตอร์ต่อไปนี้
- ต้องไม่ระบุ Salt ตาม RFC ค่านี้ต้องเทียบเท่ากับ Salt ที่มีไบต์เป็นศูนย์ 32 ไบต์
- แยกคีย์ที่สร้างขึ้นออกเป็นคีย์ยาว 256 บิต 2 คีย์ ได้แก่
symmetricEncryptionKeyและmacKey ตรวจสอบว่าช่อง
tagเป็น MAC ที่ถูกต้องสำหรับencryptedMessageหากต้องการสร้าง MAC ที่คาดไว้ ให้ใช้ HMAC (RFC 5869) กับฟังก์ชันแฮช SHA256 และ
macKeyที่ได้รับในขั้นตอนที่ 2ถอดรหัส
encryptedMessageโดยใช้โหมด AES-256-CTR และใช้สิ่งต่อไปนี้- IV เป็น 0
- ไม่มีการเว้นระยะห่างจากขอบ
symmetricEncryptionKeyที่ได้มาจากขั้นตอนที่ 2
การจัดการคีย์
คีย์การเข้ารหัสของผู้ขาย
ผู้ขายจะสร้างคีย์สาธารณะตามข้อกำหนดที่ระบุไว้ในข้อกำหนดของรูปแบบการเข้ารหัส
คีย์การรับรองรูทของ Google
Google เผยแพร่ชุดคีย์สาธารณะสำหรับการลงนามรูทที่ถูกต้องในปัจจุบันซึ่งดึงข้อมูลได้จาก URL สาธารณะ คีย์จะใช้ได้นานเท่าที่ส่วนหัวของแคช HTTP ที่ URL แสดงผลระบุไว้ ระบบจะแคชคีย์ไว้จนกว่าจะหมดอายุ ซึ่งกำหนดโดยช่อง keyExpiration เราขอแนะนำว่าเมื่อการดึงข้อมูลหมดอายุ ให้ดึงข้อมูลคีย์จาก URL สาธารณะอีกครั้งเพื่อรับรายการคีย์ที่ถูกต้องในปัจจุบัน
ข้อยกเว้นสำหรับโปรโตคอล ECv2: หากคุณไม่สามารถดึงข้อมูลคีย์จาก Google ขณะรันไทม์ ให้ดึงข้อมูล keys.json จาก URL เวอร์ชันที่ใช้งานจริงของเรา บันทึกลงในระบบ และรีเฟรชด้วยตนเองเป็นระยะๆ ในสถานการณ์ปกติ Google จะออกคีย์การรับรองรูทใหม่สำหรับ ECv2 เป็นเวลา 5 ปีก่อนที่คีย์ที่มีวันที่หมดอายุนานที่สุดจะหมดอายุ ในกรณีที่คีย์ถูกบุกรุก Google จะแจ้งให้ผู้ขายทุกรายทราบผ่านข้อมูลติดต่อที่ระบุไว้ในพอร์ทัลแบบบริการตนเองเพื่อขอให้โหลด keys.json ซ้ำได้เร็วขึ้น เพื่อให้แน่ใจว่าคุณจะไม่พลาดการหมุนเวียนคีย์ตามปกติ เราขอแนะนำให้ผู้ขายที่เลือกบันทึกคีย์ Google ไว้ในเนื้อหาของ keys.json รีเฟรชคีย์เป็นประจำทุกปีโดยเป็นส่วนหนึ่งของการหมุนเวียนคีย์ประจำปีของตนเอง
คีย์ที่ระบุผ่าน URL สาธารณะจะแมปในรูปแบบต่อไปนี้
{ "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()
URL สำหรับทั้งสภาพแวดล้อมการทดสอบและสภาพแวดล้อมฮาร์ดแวร์และซอฟต์แวร์มีอยู่ในลิงก์ต่อไปนี้
- การทดสอบ:
https://payments.developers.google.com/paymentmethodtoken/test/keys.json - เวอร์ชันที่ใช้งานจริง:
https://payments.developers.google.com/paymentmethodtoken/keys.json
การหมุนเวียนคีย์
หากคุณถอดรหัสโทเค็นวิธีการชำระเงินโดยตรงในเซิร์ฟเวอร์ด้วยการผสานรวมโดยตรง คุณต้องหมุนเวียนคีย์เป็นประจำทุกปี
ทำตามขั้นตอนต่อไปนี้เพื่อหมุนเวียนคีย์การเข้ารหัส
- ใช้ OpenSSL เพื่อสร้างคู่คีย์ใหม่
- เปิด Google Pay & Wallet Console ขณะลงชื่อเข้าใช้ด้วยบัญชี Google ที่คุณใช้ลงชื่อสมัครเป็นนักพัฒนาแอปสำหรับ Google Pay ก่อนหน้านี้
- ในแท็บ Google Pay API ในแผงการผสานรวมโดยตรง ให้คลิกจัดการข้างคีย์สาธารณะที่มีอยู่ คลิกเพิ่มคีย์อื่น
- เลือกช่องป้อนข้อความคีย์การเข้ารหัสสาธารณะ แล้วเพิ่มคีย์สาธารณะที่สร้างขึ้นใหม่ซึ่งเข้ารหัส Base64 ในรูปแบบจุดที่ไม่มีการบีบอัด
- คลิกบันทึกคีย์การเข้ารหัส
เพื่อรับประกันการหมุนเวียนคีย์ที่ราบรื่น ให้รองรับทั้งการถอดรหัสคีย์ส่วนตัวใหม่และเก่าขณะเปลี่ยนคีย์
หากใช้ไลบรารี Tink เพื่อถอดรหัสโทเค็น ให้ใช้โค้ด Java ต่อไปนี้เพื่อรองรับคีย์ส่วนตัวหลายรายการ
String decryptedMessage = new PaymentMethodTokenRecipient.Builder() .addRecipientPrivateKey(newPrivateKey) .addRecipientPrivateKey(oldPrivateKey);
ตรวจสอบว่าได้ติดตั้งใช้งานโค้ดสำหรับการถอดรหัสในเวอร์ชันที่ใช้งานจริงแล้ว และคุณตรวจสอบการถอดรหัสที่สำเร็จ
เปลี่ยนคีย์สาธารณะที่ใช้ในโค้ด
แทนที่ค่าของแอตทริบิวต์
publicKeyในพร็อพเพอร์ตี้PaymentMethodTokenizationSpecificationparametersดังนี้const tokenizationSpecification = { "type": "DIRECT", "parameters": { "protocolVersion": "ECv2", "publicKey": "BOdoXP1aiNp.....kh3JUhiSZKHYF2Y=" } }
- ทำให้โค้ดจากขั้นตอนที่ 4 ใช้งานได้จริง เมื่อติดตั้งใช้งานโค้ดแล้ว ธุรกรรมการเข้ารหัสและการถอดรหัสจะใช้คู่คีย์ใหม่
ยืนยันว่าไม่มีการใช้คีย์สาธารณะเก่าเพื่อเข้ารหัสธุรกรรมอีกต่อไป
- นำคีย์ส่วนตัวเก่าออก
- เปิด Google Pay & Wallet Console ขณะลงชื่อเข้าใช้ด้วยบัญชี Google ที่คุณเคยใช้ลงชื่อสมัครเป็นนักพัฒนาแอปกับ Google Pay
- ในแท็บ Google Pay API ในแผงการผสานรวมโดยตรง ให้คลิกจัดการข้างคีย์สาธารณะที่มีอยู่ คลิกลบข้างคีย์สาธารณะเก่า แล้วคลิกบันทึกคีย์การเข้ารหัส
Google จะใช้คีย์ที่ระบุในพร็อพเพอร์ตี้ publicKey ภายในออบเจ็กต์ PaymentMethodTokenizationSpecification parameters ดังที่แสดงในตัวอย่างต่อไปนี้
{
"protocolVersion": "ECv2",
"publicKey": "BOdoXP+9Aq473SnGwg3JU1..."
}ใช้ไลบรารี Tink เพื่อจัดการการตอบกลับที่เข้ารหัส
หากต้องการตรวจสอบลายเซ็นและถอดรหัสข้อความ ให้ใช้ไลบรารี paymentmethodtoken ของ Tink ไลบรารีนี้ใช้ได้เฉพาะใน Java เท่านั้น หากต้องการใช้ ให้ทำตามขั้นตอนต่อไปนี้
ใน
pom.xmlให้เพิ่มแอป Tinkpaymentmethodtokenเป็นทรัพยากร Dependency ดังนี้<dependencies> <!-- other dependencies ... --> <dependency> <groupId>com.google.crypto.tink</groupId> <artifactId>apps-paymentmethodtoken</artifactId> <version>1.9.1</version> <!-- or latest version --> </dependency> </dependencies>เมื่อเริ่มต้นเซิร์ฟเวอร์ ให้ดึงข้อมูลคีย์การรับรองของ Google ล่วงหน้าเพื่อให้คีย์พร้อมใช้งานในหน่วยความจำ ซึ่งจะช่วยป้องกันไม่ให้ผู้ใช้เห็นเวลาในการตอบสนองของเครือข่ายขณะที่กระบวนการถอดรหัสกำลังดึงข้อมูลคีย์
GooglePaymentsPublicKeysManager.INSTANCE_PRODUCTION.refreshInBackground();
ถอดรหัสข้อความด้วยโค้ดต่อไปนี้ ซึ่งถือว่า
paymentMethodTokenจัดเก็บอยู่ในตัวแปรencryptedMessageและแทนที่ส่วนที่เป็นตัวหนาตามสถานการณ์ของคุณสำหรับการทดสอบที่ไม่ใช่เวอร์ชันที่ใช้งานจริง ให้แทนที่
INSTANCE_PRODUCTIONด้วยINSTANCE_TESTและหากการผสานรวมของคุณไม่ได้ใช้งานหรือไม่ได้กำหนดค่าคีย์การเข้ารหัสไว้ ให้แทนที่ [YOUR MERCHANT ID] ด้วย12345678901234567890- ใช้งานอยู่
- เปิดใช้การผสานรวมโดยตรงแล้ว
- มีการกำหนดค่าคีย์การเข้ารหัส
อย่าแทนที่ [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);
แทนที่
PrivateKey1ด้วยค่าคีย์ส่วนตัวที่เหมาะสมซึ่งเชื่อมโยงกับค่าคีย์สาธารณะที่ลงทะเบียนไว้กับ Google จากหัวข้อเตรียมคีย์และลงทะเบียนกับ Google คุณสามารถเพิ่มค่าคีย์ส่วนตัวอื่นๆ ได้ในภายหลังเมื่อระบบกำหนดให้คุณหมุนเวียนคีย์กับ Google ตัวแปรอาจเป็นสตริง PKCS8 ที่เข้ารหัส base64 หรือออบเจ็กต์ECPrivateKeyก็ได้ ดูข้อมูลเพิ่มเติมเกี่ยวกับวิธีสร้างคีย์ส่วนตัว PKCS8 ที่เข้ารหัส Base64 ได้ที่เตรียมคีย์และลงทะเบียนกับ Googleหากไม่สามารถเรียกใช้เซิร์ฟเวอร์ 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);
คีย์ปัจจุบันในสภาพแวดล้อมฮาร์ดแวร์และซอฟต์แวร์จะใช้ได้จนถึงวันที่ 14/04/2038 ภายใต้สถานการณ์ปกติ ยกเว้นกรณีที่คีย์ถูกบุกรุก ในกรณีที่คีย์ถูกบุกรุก Google จะแจ้งให้ผู้ขายทั้งหมดทราบผ่านข้อมูลติดต่อที่ระบุไว้ในพอร์ทัลแบบบริการตนเองเพื่อขอให้โหลด
keys.jsonซ้ำได้เร็วขึ้นข้อมูลโค้ดจะจัดการรายละเอียดด้านความปลอดภัยต่อไปนี้เพื่อให้คุณมุ่งเน้นที่การใช้เพย์โหลดได้
- ดึงข้อมูลคีย์ Signing ของ Google และแคชไว้ในหน่วยความจำแล้ว
- การยืนยันลายเซ็น
- การถอดรหัส