Криптография платежных данных для продавцов

API Google Pay возвращает способы оплаты в подписанном и зашифрованном объекте PaymentMethodToken . Возвращаемые способы оплаты представляют собой либо карты, содержащие номер PAN, либо токенизированные карты, содержащие номер PAN устройства и криптограммы.

Полезная нагрузка содержит поле с именем protocolVersion , которое сообщает получателю полезной нагрузки, какие криптографические примитивы используются и ожидаемый формат.

В этом руководстве содержится информация о том, как сгенерировать открытый ключ для запроса подписанного Google и зашифрованного токена способа оплаты, а также подробно описаны шаги по проверке и расшифровке токена.

Данное руководство применимо только к protocolVersion = ECv2 .

Поскольку вы получаете информацию о платежных картах напрямую, убедитесь, что ваше приложение соответствует стандарту PCI DSS и что ваши серверы обладают необходимой инфраструктурой для безопасной обработки платежных данных пользователя, прежде чем продолжить.

Ниже описаны шаги, которые должен выполнить интегратор для использования данных PaymentMethodToken ECv2 API Google Pay:

  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 Нить Дата и время истечения срока действия промежуточного ключа в миллисекундах 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_versionECv2 .

Если 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_idmerchant: 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 .

  • Эллиптическая кривая: NIST P-256 (также известная в OpenSSL как prime256v1).
  • CheckMode , OldCofactorMode , SingleHashMode и CofactorMode равны 0.
  • Формат точек не сжат.
Функция вывода ключа

HMAC-основанный алгоритм с SHA-256 ( HKDFwithSHA256 ).

  • Соль предоставлять нельзя.
  • Для протокола версии ECv2 информация должна быть закодирована в формате ASCII с использованием кодировки Google.
  • Для ключа AES256 необходимо получить 256 бит, и еще 256 бит необходимо получить для ключа HMAC_SHA256 .
Симметричный алгоритм шифрования

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

Как расшифровать токен способа оплаты

Выполните следующие шаги для расшифровки токена:

  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 .

    Для генерации ожидаемого MAC-адреса используйте 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() .

Ссылки на тестовую и производственную среды приведены ниже:

Вращение ключа

Если вы расшифровываете токен способа оплаты непосредственно на своих серверах с помощью прямой интеграции, то вам необходимо ежегодно обновлять ключи.

Для смены ключей шифрования выполните следующие шаги:

  1. Используйте OpenSSL для генерации новой пары ключей .
  2. Откройте консоль Google Pay и Wallet, войдя в свою учетную запись Google. тот, который вы ранее использовали для регистрации в качестве разработчика в Google Pay.
  3. На вкладке Google Pay API в разделе «Прямая интеграция » нажмите «Управление» рядом с существующим открытым ключом. Нажмите «Добавить еще один ключ» .
  4. Выберите текстовое поле «Открытый ключ шифрования» и добавьте сгенерированный открытый ключ в формате Base64, закодированный в несжатом виде.
  5. Нажмите «Сохранить ключи шифрования» .
  6. Для обеспечения бесперебойной ротации ключей поддерживайте расшифровку как новых, так и старых закрытых ключей во время перехода на новые ключи.

    Если вы используете библиотеку Tink для расшифровки токена, используйте следующий Java-код для поддержки нескольких закрытых ключей:

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

    Убедитесь, что код для расшифровки развернут в рабочей среде и что вы отслеживаете успешные расшифровки.

  7. Измените открытый ключ, используемый в вашем коде.

    Замените значение атрибута publicKey в свойстве parameters объекта PaymentMethodTokenizationSpecification :

    const tokenizationSpecification = {
      "type": "DIRECT",
      "parameters": {
        "protocolVersion": "ECv2",
        "publicKey": "BOdoXP1aiNp.....kh3JUhiSZKHYF2Y="
      }
    }
  8. Разверните код из шага 4 в рабочую среду. После развертывания кода операции шифрования и дешифрования будут использовать новые пары ключей.
  9. Убедитесь, что старый открытый ключ больше не используется для шифрования каких-либо транзакций.

  10. Удалите старый закрытый ключ.
  11. Откройте консоль Google Pay и Wallet , войдя в систему с той же учетной записью Google, которую вы ранее использовали для регистрации в качестве разработчика в Google Pay.
  12. На вкладке Google Pay API в разделе «Прямая интеграция» нажмите «Управление» рядом с существующим открытым ключом. Нажмите «Удалить» рядом со старым открытым ключом и нажмите «Сохранить ключи шифрования» .

Google использует ключ, указанный в свойстве publicKey объекта parameters PaymentMethodTokenizationSpecification , как показано в следующем примере:

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

Используйте библиотеку Tink для управления зашифрованным ответом.

Для проверки подписи и расшифровки сообщений используйте библиотеку Tink paymentmethodtoken . Эта библиотека доступна только для 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] на 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);
  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 извлекаются и кэшируются в памяти.
    • Проверка подписи
    • Расшифровка