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

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

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

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

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

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

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

  1. Получите ключи подписи root от 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 с 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"
    
  },
  
  "messageId": "some-message-id",
  "messageExpiration": "1759309000000"
}

eciIndicator

Сеть платежных карт может предоставлять eciIndicator для аутентифицированных транзакций с токенами устройств (CRYPTOGRAM_3DS).

Значение eciIndicator необходимо передать в транзакцию авторизации без его изменения или жёсткого кодирования; в противном случае транзакция завершится неудачей. В следующей таблице приведены значения eciIndicator .

значение eciIndicator Сеть карт Ответственная сторона Метод аутентификации
""(empty) Мастеркард Торговец/Покупатель КРИПТОГРАММА_3DS
02 Мастеркард Эмитент карты КРИПТОГРАММА_3DS
06 Мастеркард Торговец/Покупатель КРИПТОГРАММА_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 см. в разделе « Инструменты IETF», Приложение A. Подпись состоит из целых чисел r и s в формате ECDSA. Подробнее см. в разделе «Алгоритм генерации подписи» .

Ниже приведен пример указанного формата байтов ASN.1, который является стандартным форматом, создаваемым реализациями Java Cryptography Extension (JCE) ECDSA.

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_idGoogle , 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_idGoogle , а recipient_idmerchant: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 ) для защиты токена платежного метода, возвращаемого в ответе API Google Pay. Схема шифрования использует следующие параметры:

Параметр Определение
Метод инкапсуляции ключей

ECIES-KEM, как определено в ISO 18033-2 .

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

На основе HMAC с SHA-256 ( HKDFwithSHA256 ).

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

DEM2, как определено в ISO 18033-2

Алгоритм шифрования: AES-256-CTR с нулевым IV и без дополнения.

алгоритм MAC HMAC_SHA256 с 256-битным ключом, полученным с помощью функции вывода ключа.

Формат открытого ключа шифрования

Открытый ключ шифрования и ephemeralPublicKey , возвращаемые в полезных нагрузках Google, отформатированы с использованием представления ключа в кодировке base64 в несжатом формате point. Он состоит из следующих двух элементов:

  • Одно магическое число, определяющее формат (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 в несжатом формате Point, используйте следующую команду:

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 .

    Для генерации ожидаемого MAC используйте HMAC ( RFC 5869 ) с хеш-функцией SHA256 и macKey , полученный на шаге 2.

  4. Расшифруйте encryptedMessage с использованием режима AES-256-CTR и следующим образом:

    • А ноль IV.
    • Без подкладки.
    • symmetricEncryptionKey , полученный на шаге 2.

Управление ключами

Ключи шифрования продавца

Торговцы генерируют открытый ключ в соответствии со спецификациями, изложенными в Спецификации схемы шифрования .

Ключи подписи root от Google

Google публикует набор действующих на данный момент открытых ключей подписи root, которые можно получить по общедоступному 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 , войдя в учетную запись 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 извлечены и кэшированы в памяти
    • Проверка подписи
    • Расшифровка