تشفير بيانات الدفع للتجّار

تعرض Google Pay API طُرق الدفع في حمولة PaymentMethodToken موقّعة ومشفّرة. طرق الدفع التي يتم إرجاعها هي إما بطاقات تتضمّن رقم حساب أساسي أو بطاقات تم تحويلها إلى رموز مميّزة تتضمّن رقم حساب أساسي للجهاز ورموز تشفير.

تحتوي الحمولة على حقل يُسمى protocolVersion يوضّح لمستلم الحمولة بدائيات التشفير المستخدَمة والتنسيق المتوقّع.

يقدّم هذا الدليل معلومات حول كيفية إنشاء مفتاح عام لطلب رمز مميّز مشفّر وموقّع من Google لوسيلة الدفع، كما يوضّح الخطوات التي يجب اتّخاذها للتحقّق من الرمز المميّز وفكّ تشفيره.

لا ينطبق هذا الدليل إلا على protocolVersion = ECv2.

بما أنّك تتلقّى معلومات بطاقة الدفع مباشرةً، عليك التأكّد من أنّ تطبيقك متوافق مع معيار أمان بيانات قطاع بطاقات الدفع (PCI DSS) ومن أنّ خوادمك تتضمّن البنية الأساسية المطلوبة للتعامل بأمان مع بيانات اعتماد الدفع الخاصة بالمستخدم قبل المتابعة.

توضّح الخطوات التالية ما يجب أن يفعله المدمج لاستخدام حمولة ECv2 PaymentMethodToken Google Pay API:

  1. استرجِع مفاتيح التوقيع الجذرية من Google.
  2. تأكَّد من أنّ توقيع مفتاح التوقيع الوسيط صالح من خلال أي من مفاتيح التوقيع الجذرية غير المنتهية الصلاحية.
  3. تأكَّد من عدم انتهاء صلاحية مفتاح التوقيع الوسيط للحِمل.
  4. تأكَّد من أنّ توقيع الحمولة صالح باستخدام مفتاح التوقيع الوسيط.
  5. فك تشفير محتوى الحمولة بعد التحقّق من التوقيع.
  6. تأكَّد من عدم انتهاء صلاحية الرسالة. يتطلّب ذلك التحقّق من أنّ الوقت الحالي أقل من قيمة الحقل messageExpiration في المحتوى الذي تم فك تشفيره.
  7. استخدام طريقة الدفع في المحتوى الذي تم فك تشفيره وتحصيل الرسوم منها

ينفّذ الرمز النموذجي في مكتبة 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\"}"
}

مفتاح التوقيع الوسيط

intermediateSigningKey هو كائن JSON متسلسل ومُرمَّز بتنسيق UTF-8 يحتوي على القيم التالية:

الاسم النوع الوصف
signedKey سلسلة رسالة بترميز base64 تحتوي على وصف الدفع الخاص بالمفتاح.
signatures سلسلة تتحقّق هذه السمة من أنّ مفتاح التوقيع الوسيط صادر من Google. وهي مشفّرة باستخدام base64 وتم إنشاؤها باستخدام ECDSA.

مفتاح موقَّع

signedKey هو كائن JSON متسلسل بترميز UTF-8 يحتوي على القيم التالية:

الاسم النوع الوصف
keyValue سلسلة إصدار base64 من المفتاح المشفّر بنوع ASN.1 يتم تحديد SubjectPublicKeyInfo في معيار X.509.
keyExpiration سلسلة تاريخ ووقت انتهاء صلاحية المفتاح الوسيط بالمللي ثانية منذ بداية الفترة حسب التوقيت العالمي المنسَّق ويرفض المدمجون أي مفتاح انتهت صلاحيته.

رسالة موقّعة

signedMessage هو كائن JSON متسلسل بترميز UTF-8 يحتوي على القيم التالية:

الاسم النوع الوصف
encryptedMessage سلسلة رسالة مشفّرة بترميز base64 تحتوي على معلومات الدفع وبعض حقول الأمان الإضافية
ephemeralPublicKey سلسلة مفتاح عام مؤقت بترميز Base64 مرتبط بالمفتاح الخاص لتشفير الرسالة بتنسيق نقطة غير مضغوطة. لمزيد من المعلومات، يُرجى الاطّلاع على تنسيق المفتاح العام للتشفير.
tag سلسلة تمثّل هذه السمة رمز مصادقة الرسائل (MAC) المشفّر باستخدام Base64 للسلسلة encryptedMessage.

رسالة مشفَّرة

encryptedMessage الذي تم فك تشفيره هو كائن JSON متسلسل بترميز UTF-8. يحتوي ملف JSON على مستويَين. يحتوي المستوى الخارجي على بيانات وصفية وحقول مضمّنة لأغراض الأمان، بينما المستوى الداخلي هو عنصر JSON آخر يمثّل بيانات اعتماد الدفع الفعلية.

للحصول على مزيد من التفاصيل حول encryptedMessage، راجِع الجداول التالية وأمثلة كائنات JSON:

الاسم النوع الوصف
messageExpiration سلسلة التاريخ والوقت اللذان تنتهي فيهما صلاحية الرسالة بالمللي ثانية منذ بداية الحقبة حسب التوقيت العالمي المنسَّق على الجهات التي تجري عمليات الدمج رفض أي رسالة انتهت صلاحيتها.
messageId سلسلة معرّف فريد يحدّد الرسالة في حال الحاجة إلى إبطالها أو تحديد موقعها في وقت لاحق
paymentMethod سلسلة تمثّل هذه السمة نوع بيانات اعتماد الدفع. في الوقت الحالي، لا تتوفّر سوى اللغة CARD. ‫
paymentMethodDetails عنصر بيانات اعتماد الدفع نفسها يتم تحديد تنسيق هذا العنصر من خلال paymentMethod ويتم توضيحه في الجداول التالية.

بطاقة

تتكوّن بيانات اعتماد الدفع لطريقة الدفع CARD من الخصائص التالية:

الاسم النوع الوصف
pan سلسلة رقم الحساب الشخصي الذي تم تحصيل الرسوم منه يحتوي هذا السلسلة على أرقام فقط.
expirationMonth العدد يشير هذا الحقل إلى شهر انتهاء صلاحية البطاقة، حيث يمثّل الرقم 1 شهر يناير، ويمثّل الرقم 2 شهر فبراير، وهكذا.
expirationYear العدد تمثّل هذه السمة سنة انتهاء صلاحية البطاقة المكوّنة من أربعة أرقام، مثل 2020.
authMethod سلسلة طريقة مصادقة معاملة البطاقة

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 الكامل الخاص بـ CARD paymentMethod مع 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

قد توفّر شبكة البطاقة 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 خوارزمية التوقيع الرقمي المنحني الإهليلجي (ECDSA) لتوقيع الرسائل بالمعلمات التالية: خوارزمية ECDSA على NIST P-256 مع SHA-256 كدالة تجزئة، كما هو محدّد في FIPS 186-4.

التوقيع

يتم تضمين التوقيع في المستوى الخارجي من الرسالة. ويتم ترميزها باستخدام base64 بتنسيق بايت ASN.1. لمزيد من المعلومات حول ASN.1، يُرجى الاطّلاع على الملحق (أ) من أدوات 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. إذا نجحت عملية التحقّق من صحة توقيع واحد على الأقل، اعتبِر عملية التحقّق مكتملة. استخدِم الرمز 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 نظام التشفير المتكامل للمنحنى الإهليلجي (ECIES) لتأمين رمز طريقة الدفع الذي يتم عرضه في ردّ واجهة برمجة تطبيقات Google Pay. يستخدم نظام التشفير المَعلمات التالية:

المَعلمة التعريف
طريقة تغليف المفاتيح

ECIES-KEM، كما هو محدّد في ISO 18033-2

  • المنحنى البيضاوي: NIST P-256 (المعروف أيضًا في OpenSSL باسم prime256v1).
  • CheckMode وOldCofactorMode وSingleHashMode وCofactorMode تساوي 0.
  • تنسيق النقطة غير مضغوط.
دالة اشتقاق المفاتيح

استنادًا إلى HMAC مع SHA-256 (HKDFwithSHA256)

  • يجب عدم توفير الملح.
  • يجب أن تكون المعلومات مرمّزة من Google باستخدام ASCII لإصدار البروتوكول ECv2.
  • يجب استخلاص 256 بت لمفتاح AES256 و256 بت أخرى لمفتاح HMAC_SHA256.
خوارزمية التشفير المتماثل

‫DEM2، كما هو محدّد في ISO 18033-2

خوارزمية التشفير: AES-256-CTR بدون IV وبدون حشو

خوارزمية MAC HMAC_SHA256 باستخدام مفتاح 256 بت مشتق من دالة اشتقاق المفاتيح

تنسيق المفتاح العام للتشفير

يتم تنسيق المفتاح العام للتشفير وephemeralPublicKey الذي يتم عرضه في حمولات Google باستخدام تمثيل base64 للمفتاح بتنسيق النقطة غير المضغوطة. ويتألف من العنصرَين التاليَين:

  • رقم سحري واحد يحدّد التنسيق (0x04).
  • عددان صحيحان كبيران بحجم 32 بايت يمثّلان الإحداثيتَين X وY في المنحنى البيضاوي.

يتم وصف هذا التنسيق بمزيد من التفصيل في "تشفير المفتاح العام لمؤسسات الخدمات المالية: خوارزمية التوقيع الرقمي للمنحنى الإهليلجي (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: إنشاء مفتاح خاص بترميز base64 بتنسيق PKCS #8

تتوقّع مكتبة 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 المحدّد لإنشاء مفتاح مشترك بطول 512 بت يستخدم ECIES-KEM. استخدِم المَعلمات التالية:
    • المنحنى البيضاوي: NIST P-256، المعروف أيضًا في OpenSSL باسم prime256v1.
    • CheckMode وOldCofactorMode وSingleHashMode وCofactorMode هي 0.
    • دالة الترميز: تنسيق النقطة غير المضغوطة
    • دالة اشتقاق المفتاح: HKDFwithSHA256، كما هو موضّح في RFC 5869، مع المَعلمة التالية:
      • يجب عدم توفير الملح. وفقًا لمعايير RFC، يجب أن يكون هذا الإجراء مكافئًا لـ 32 بايت من الأصفار.
  2. قسِّم المفتاح الذي تم إنشاؤه إلى مفتاحَين بطول 256 بت: symmetricEncryptionKey وmacKey.
  3. تأكَّد من أنّ الحقل tag يمثّل عنوان MAC صالحًا لـ encryptedMessage.

    لإنشاء رمز مصادقة الرسائل المتوقّع، استخدِم HMAC (RFC 5869) مع دالة التجزئة SHA256 وmacKey الذي تم الحصول عليه في الخطوة 2.

  4. فك تشفير encryptedMessage باستخدام وضع AES-256-CTR، مع ما يلي:

    • قيمة IV تساوي صفرًا
    • لم تتم إضافة مساحة فارغة.
    • symmetricEncryptionKey الذي تم الحصول عليه في الخطوة 2

إدارة المفاتيح

مفاتيح التشفير الخاصة بالتاجر

ينشئ التجّار مفتاحًا عامًا وفقًا للمواصفات الموضّحة في مواصفات نظام التشفير.

مفاتيح التوقيع الجذرية من Google

تنشر Google مجموعة المفاتيح العامة الحالية الصالحة لتوقيع الشهادات الجذرية، ويمكن استرجاعها من عنوان URL عام. تكون المفاتيح صالحة طالما تشير عناوين ذاكرة التخزين المؤقت HTTP التي يعرضها عنوان URL إلى ذلك. ويتم تخزينها مؤقتًا إلى أن تنتهي صلاحيتها، ويتم تحديد ذلك من خلال الحقل keyExpiration. ننصحك بأنّه عند انتهاء صلاحية عملية الاسترجاع، عليك استرجاع المفاتيح من عنوان URL العلني مرة أخرى لتلقّي القائمة الحالية من المفاتيح الصالحة.

استثناء لبروتوكول ECv2: إذا تعذّر عليك استرداد المفاتيح من Google في وقت التشغيل، يمكنك استرداد keys.json من عنوان URL الخاص بالإنتاج، وحفظه في نظامك، وإعادة تحميله يدويًا بشكل دوري. في الظروف العادية، تصدر Google مفتاح توقيع جذر جديدًا لـ ECv2 قبل خمس سنوات من انتهاء صلاحية المفتاح الذي لديه أطول تاريخ انتهاء صلاحية. في حال تعرّض المفاتيح للاختراق، ترسل 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 لكل من بيئتي الاختبار والإنتاج من خلال الروابط التالية:

تدوير المفتاح

إذا كنت تفك تشفير الرمز المميز لطريقة الدفع مباشرةً على خوادمك من خلال عملية دمج مباشرة، عليك تغيير المفاتيح سنويًا.

أكمِل الخطوات التالية لتدوير مفاتيح التشفير:

  1. استخدِم OpenSSL لإنشاء مفتاحَي تشفير جديدَين.
  2. افتح Google Pay & Wallet Console أثناء تسجيل الدخول باستخدام حساب Google الذي سبق استخدامه لإدارة تطبيقك على Google Play.
  3. في علامة التبويب Google Pay API، ضمن لوحة الدمج المباشر، انقر على إدارة بجانب مفتاحك العام الحالي. انقر على إضافة مفتاح آخر.
  4. اختَر حقل إدخال النص مفتاح التشفير العام وأضِف المفتاح العام الذي تم إنشاؤه حديثًا بترميز base64 بتنسيق النقطة غير المضغوطة.
  5. انقر على حفظ مفاتيح التشفير.
  6. لضمان عملية تناوب سلسة للمفاتيح، يجب توفير إمكانية فك تشفير المفتاحَين الخاصَّين الجديد والقديم أثناء نقل المفاتيح.

    إذا كنت تستخدم مكتبة Tink لفك تشفير الرمز المميّز، استخدِم رمز Java التالي لتوفير مفاتيح خاصة متعددة:

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

    تأكَّد من نشر رمز فك التشفير في مرحلة الإنتاج ومن مراقبة عمليات فك التشفير الناجحة.

  7. غيِّر المفتاح العام المستخدَم في الرمز.

    استبدِل قيمة السمة publicKey في السمة PaymentMethodTokenizationSpecification parameters:

    /**
     * @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;
    }
  8. انشر الرمز من الخطوة 4 في مرحلة الإنتاج. بعد نشر الرمز، تستخدم معاملات التشفير وفك التشفير أزواج المفاتيح الجديدة.
  9. تأكَّد من أنّه لم يعُد يتم استخدام المفتاح العام القديم لتشفير أي معاملات.

  10. أزِل المفتاح الخاص القديم.
  11. افتح وحدة تحكّم Google Pay & Wallet أثناء تسجيل الدخول باستخدام حساب Google الذي استخدمته سابقًا للاشتراك كمطوِّر في Google Pay.
  12. في علامة التبويب Google Pay API، ضمن لوحة الدمج المباشر، انقر على إدارة بجانب مفتاحك العام الحالي. انقر على حذف بجانب مفتاحك العام القديم، ثم انقر على حفظ مفاتيح التشفير.

تستخدم Google المفتاح المحدّد في السمة publicKey ضمن العنصر PaymentMethodTokenizationSpecification parameters، كما هو موضّح في المثال التالي:

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

استخدِم مكتبة Tink لإدارة الرد المشفّر.

لإجراء عملية التحقّق من التوقيع وفك تشفير الرسالة، استخدِم مكتبة paymentmethodtoken في Tink. لا تتوفّر هذه المكتبة إلا في Java. لاستخدامها، يُرجى إكمال الخطوات التالية:

  1. في ملف pom.xml، أضِف تطبيق Tink paymentmethodtoken كعنصر تابع:

    <dependencies>
      <!-- other dependencies ... -->
      <dependency>
        <groupId>com.google.crypto.tink</groupId>
        <artifactId>apps-paymentmethodtoken</artifactId>
        <version>1.9.1</version>  <!-- or latest version -->
      </dependency>
    </dependencies>
  2. عند بدء تشغيل الخادم، يتم جلب مفاتيح التوقيع من Google مسبقًا لإتاحة المفتاح في الذاكرة. يمنع ذلك المستخدم من رؤية أي تأخير في الشبكة أثناء جلب مفاتيح عملية فك التشفير.

    GooglePaymentsPublicKeysManager.INSTANCE_PRODUCTION.refreshInBackground();
  3. فك تشفير الرسالة باستخدام الرمز التالي، الذي يفترض أنّ paymentMethodToken مخزّن في المتغيّر encryptedMessage، واستبدِل الأقسام المكتوبة بخط غامق وفقًا للسيناريو الخاص بك.

    بالنسبة إلى الاختبارات غير الإنتاجية، استبدِل INSTANCE_PRODUCTION بـ INSTANCE_TEST، وإذا كان الدمج غير نشط أو لم يتم ضبط مفتاح تشفير له، استبدِل [YOUR MERCHANT ID] بـ .

    • نشطة
    • تم تفعيل التكامل المباشر
    • تم ضبط مفتاح تشفير

    لا تستبدِل [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);
  4. استبدِل PrivateKey1 بقيمة المفتاح الخاص المناسبة المرتبطة بقيمة المفتاح العام المسجَّلة لدى Google من خلال إعداد المفاتيح والتسجيل لدى Google. يمكنك إضافة قيم مفاتيح خاصة أخرى لاحقًا عندما يُطلب منك تدوير المفاتيح باستخدام Google. يمكن أن تكون المتغيّرات إما سلسلة PKCS8 بترميز base64 أو كائن ECPrivateKey. لمزيد من المعلومات حول كيفية إنشاء مفتاح خاص بتنسيق PKCS8 مشفّر باستخدام base64، يُرجى الاطّلاع على إعداد المفاتيح والتسجيل لدى Google.

  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);

    المفتاح الحالي في بيئة الإنتاج صالح حتى 14/04/2038 في الظروف العادية باستثناء حالات اختراق المفتاح. في حال تعرّض المفاتيح للاختراق، تُرسل Google إشعارًا إلى جميع التجّار من خلال معلومات الاتصال المقدَّمة في بوابة الخدمة الذاتية لطلب إعادة تحميل keys.json بشكل أسرع.

    يتعامل مقتطف الرمز مع تفاصيل الأمان التالية حتى تتمكّن من التركيز على استهلاك الحمولة:

    • استرداد مفاتيح التوقيع من Google وتخزينها مؤقتًا في الذاكرة
    • التحقّق من التوقيع
    • فك التشفير