API Google Pay возвращает способы оплаты в подписанном и зашифрованном объекте PaymentMethodToken . Возвращаемые способы оплаты представляют собой либо карты, содержащие номер PAN, либо токенизированные карты, содержащие номер PAN устройства и криптограммы.
Полезная нагрузка содержит поле с именем protocolVersion , которое сообщает получателю полезной нагрузки, какие криптографические примитивы используются и ожидаемый формат.
В этом руководстве содержится информация о том, как сгенерировать открытый ключ для запроса подписанного Google и зашифрованного токена способа оплаты, а также подробно описаны шаги по проверке и расшифровке токена.
Данное руководство применимо только к protocolVersion = ECv2 .
Поскольку вы получаете информацию о платежных картах напрямую, убедитесь, что ваше приложение соответствует стандарту PCI DSS и что ваши серверы обладают необходимой инфраструктурой для безопасной обработки платежных данных пользователя, прежде чем продолжить.
Ниже описаны шаги, которые должен выполнить интегратор для использования данных PaymentMethodToken ECv2 API Google Pay:
- Получите корневые ключи подписи 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\"}" }
Промежуточный ключ для подписи
Объект intermediateSigningKey представляет собой сериализованный JSON-объект, закодированный в UTF-8, который содержит следующие значения:
| Имя | Тип | Описание |
|---|---|---|
signedKey | Нить | Сообщение в кодировке base64, содержащее описание платежа по ключу. |
signatures | Нить | Проверяет, что промежуточный ключ подписи получен от Google. Он закодирован в формате Base64 и создан с помощью алгоритма ECDSA. |
Подписанный ключ
Объект signedKey представляет собой сериализованный JSON-объект, закодированный в UTF-8, который содержит следующие значения:
| Имя | Тип | Описание |
|---|---|---|
keyValue | Нить | Ключ в формате base64, закодированный в формате ASN.1. SubjectPublicKeyInfo определен в стандарте X.509. |
keyExpiration | Нить | Дата и время истечения срока действия промежуточного ключа в миллисекундах UTC с начала эпохи. Интеграторы отклоняют любой ключ, срок действия которого истек. |
Подписанное сообщение
Объект signedMessage представляет собой сериализованный JSON-объект, закодированный в UTF-8, и содержит следующие значения:
| Имя | Тип | Описание |
|---|---|---|
encryptedMessage | Нить | Зашифрованное сообщение в формате Base64, содержащее платежную информацию и некоторые дополнительные поля безопасности . |
ephemeralPublicKey | Нить | Временный открытый ключ, закодированный в base64 и связанный с закрытым ключом для шифрования сообщения в несжатом точечном формате. Для получения дополнительной информации см. раздел «Формат открытого ключа шифрования» . |
tag | Нить | MAC-код encryptedMessage , закодированный в base64. |
Зашифрованное сообщение
Расшифрованное encryptedMessage представляет собой сериализованный JSON-объект, закодированный в UTF-8. JSON содержит два уровня. Внешний уровень содержит метаданные и поля, включенные для обеспечения безопасности, а внутренний уровень — это другой JSON-объект, представляющий собой фактические платежные данные.
Для получения более подробной информации о encryptedMessage см. следующие таблицы и примеры объектов JSON:
| Имя | Тип | Описание |
|---|---|---|
messageExpiration | Нить | Дата и время истечения срока действия сообщения указаны в миллисекундах UTC с начала эпохи. Интеграторы должны отклонять любые сообщения с истекшим сроком действия. |
messageId | Нить | Уникальный идентификатор, который позволяет идентифицировать сообщение в случае необходимости его отзыва или поиска в дальнейшем. |
paymentMethod | Нить | Тип платежных данных. В настоящее время поддерживается только CARD . |
paymentMethodDetails | Объект | Сам платежный идентификатор. Формат этого объекта определяется параметром paymentMethod и описан в следующих таблицах. |
Карта
Для осуществления платежа с помощью CARD необходимы следующие данные:
| Имя | Тип | Описание |
|---|---|---|
pan | Нить | Номер личного счета, с которого произведена оплата. Эта строка содержит только цифры. |
expirationMonth | Число | Месяц истечения срока действия карты, где 1 обозначает январь, 2 — февраль и так далее. |
expirationYear | Число | Четырехзначный год истечения срока действия карты, например, 2020. |
authMethod | Нить | Метод аутентификации при совершении карточной транзакции. |
PAN_ONLY
Приведённый ниже фрагмент JSON представляет собой пример полного encryptedMessage для paymentMethod CARD с authMethod PAN_ONLY .
{ "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 | Нить | Криптограмма с защитой 3D-пространства. |
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",
},
"messageId": "some-message-id",
"messageExpiration": "1759309000000"
}eciIndicator
Платежная сеть может предоставлять eciIndicator для транзакций с аутентифицированными токенами устройств (CRYPTOGRAM_3DS).
Значение параметра eciIndicator необходимо передавать в транзакции авторизации, не изменяя и не задавая его жестко; в противном случае транзакция завершится неудачей. В следующей таблице приведены значения параметра eciIndicator .
| значение индикатора eci | Карточная сеть | Ответственная сторона | authMethod |
|---|---|---|---|
""(empty) | Mastercard | Торговец/эквайер | КРИПТОГРАММА_3DS |
02 | Mastercard | Эмитент карты | КРИПТОГРАММА_3DS |
06 | Mastercard | Торговец/эквайер | КРИПТОГРАММА_3DS |
05 | Виза | Эмитент карты | КРИПТОГРАММА_3DS |
07 | Виза | Торговец/эквайер | КРИПТОГРАММА_3DS |
""(empty) | Другие сети | Торговец/эквайер | КРИПТОГРАММА_3DS |
Любые другие значения ECI для VISA и Mastercard, отсутствующие в этой таблице, не будут возвращены.
Проверка подписи
Для проверки подписей, включая промежуточный ключ и подписи сообщений, необходимы следующие элементы:
- Алгоритм, используемый для создания подписи.
- Последовательность байтов, использованная для создания подписи.
- Открытый ключ, соответствующий закрытому ключу, использованному для создания подписи.
- Сама подпись
Алгоритм подписи
Google использует алгоритм цифровой подписи на эллиптических кривых ( 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 . Если хотя бы одна проверка подписи прошла успешно, считайте проверку завершенной. Используйте 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 для продавцов с доступом к рабочей версии.
Если 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. Для проверки signedMessage используется значение intermediateSigningKey.signedKey.keyValue , проверенное на предыдущем шаге. Google настоятельно рекомендует использовать существующую криптографическую библиотеку, а не собственный код проверки.
Спецификация схемы шифрования
Google использует схему шифрования на основе эллиптических кривых ( ECIES ) для защиты токена способа оплаты, возвращаемого в ответе API Google Pay. Схема шифрования использует следующие параметры:
| Параметр | Определение |
|---|---|
| Метод инкапсуляции ключей | ECIES-KEM, как определено в ISO 18033-2 .
|
| Функция вывода ключа | HMAC-основанный алгоритм с SHA-256 (
|
| Симметричный алгоритм шифрования | DEM2, как определено в ISO 18033-2 Алгоритм шифрования: AES-256-CTR с нулевым вектором инициализации и без дополнения. |
| алгоритм 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: Сгенерируйте закрытый ключ в формате PKCS #8, закодированный в base64.
Библиотека Tink ожидает, что ваш закрытый ключ будет закодирован в формате Base64 PKCS #8. Используйте следующую команду для генерации закрытого ключа в этом формате из закрытого ключа, сгенерированного на первом шаге:
openssl pkcs8 -topk8 -inform PEM -outform DER -in key.pem -nocrypt | base64 | paste -sd "\0" -
В результате выполнения команды будет получен вывод, аналогичный следующему:
MIGHAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBG0wawIBAQQgWV4oK8c/MZkCLk4qSCNjW0Zm6H0CBCtSYxkXkC9FBHehRANCAAQPldOnhO2/oXjdJD1dwlFPiNs6fcdoRgFu3/Z0iKj24SjTGyLRGAtYWLGXBZcDdPj3T2bJRHRVhE8Bc2AjkT7n
Как расшифровать токен способа оплаты
Выполните следующие шаги для расшифровки токена:
- Используйте свой закрытый ключ и заданный
ephemeralPublicKeyдля получения 512-битного разделяемого ключа, использующего ECIES-KEM . Используйте следующие параметры: - Эллиптическая кривая: NIST P-256, также известная в OpenSSL как prime256v1.
-
CheckMode,OldCofactorMode,SingleHashModeиCofactorModeравны0. - Функция кодирования: несжатый точечный формат.
- Функция вывода ключа: HKDFwithSHA256, как описано в RFC 5869 , со следующим параметром:
- Соль указывать не следует. Согласно RFC, она должна быть эквивалентна соли в 32 обнуленных байта.
- Разделите сгенерированный ключ на два ключа длиной 256 бит:
symmetricEncryptionKeyиmacKey. Убедитесь, что поле
tagявляется допустимым MAC-адресом дляencryptedMessage.Для генерации ожидаемого MAC-адреса используйте HMAC ( RFC 5869 ) с хеш-функцией SHA256 и
macKey, полученным на шаге 2.Расшифровать
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() .
Ссылки на тестовую и производственную среды приведены ниже:
- Тест:
https://payments.developers.google.com/paymentmethodtoken/test/keys.json - В рабочей среде:
https://payments.developers.google.com/paymentmethodtoken/keys.json
Вращение ключа
Если вы расшифровываете токен способа оплаты непосредственно на своих серверах с помощью прямой интеграции, то вам необходимо ежегодно обновлять ключи.
Для смены ключей шифрования выполните следующие шаги:
- Используйте OpenSSL для генерации новой пары ключей .
- Откройте консоль Google Pay и Wallet, войдя в свою учетную запись Google. тот, который вы ранее использовали для регистрации в качестве разработчика в Google Pay.
- На вкладке Google Pay API в разделе «Прямая интеграция » нажмите «Управление» рядом с существующим открытым ключом. Нажмите «Добавить еще один ключ» .
- Выберите текстовое поле «Открытый ключ шифрования» и добавьте сгенерированный открытый ключ в формате Base64, закодированный в несжатом виде.
- Нажмите «Сохранить ключи шифрования» .
Для обеспечения бесперебойной ротации ключей поддерживайте расшифровку как новых, так и старых закрытых ключей во время перехода на новые ключи.
Если вы используете библиотеку Tink для расшифровки токена, используйте следующий Java-код для поддержки нескольких закрытых ключей:
String decryptedMessage = new PaymentMethodTokenRecipient.Builder() .addRecipientPrivateKey(newPrivateKey) .addRecipientPrivateKey(oldPrivateKey);
Убедитесь, что код для расшифровки развернут в рабочей среде и что вы отслеживаете успешные расшифровки.
Измените открытый ключ, используемый в вашем коде.
Замените значение атрибута
publicKeyв свойствеparametersобъектаPaymentMethodTokenizationSpecification:const tokenizationSpecification = { "type": "DIRECT", "parameters": { "protocolVersion": "ECv2", "publicKey": "BOdoXP1aiNp.....kh3JUhiSZKHYF2Y=" } }
- Разверните код из шага 4 в рабочую среду. После развертывания кода операции шифрования и дешифрования будут использовать новые пары ключей.
Убедитесь, что старый открытый ключ больше не используется для шифрования каких-либо транзакций.
- Удалите старый закрытый ключ.
- Откройте консоль Google Pay и Wallet , войдя в систему с той же учетной записью Google, которую вы ранее использовали для регистрации в качестве разработчика в Google Pay.
- На вкладке Google Pay API в разделе «Прямая интеграция» нажмите «Управление» рядом с существующим открытым ключом. Нажмите «Удалить» рядом со старым открытым ключом и нажмите «Сохранить ключи шифрования» .
Google использует ключ, указанный в свойстве publicKey объекта parameters PaymentMethodTokenizationSpecification , как показано в следующем примере:
{
"protocolVersion": "ECv2",
"publicKey": "BOdoXP+9Aq473SnGwg3JU1..."
}Используйте библиотеку Tink для управления зашифрованным ответом.
Для проверки подписи и расшифровки сообщений используйте библиотеку Tink paymentmethodtoken . Эта библиотека доступна только для Java. Для её использования выполните следующие шаги:
В файл
pom.xmlдобавьте приложение Tinkpaymentmethodtokenв качестве зависимости:<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.Приведенный фрагмент кода обрабатывает следующие аспекты безопасности, позволяя вам сосредоточиться на обработке полезной нагрузки:
- Ключи подписи Google извлекаются и кэшируются в памяти.
- Проверка подписи
- Расшифровка