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

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

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

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

Инструкции действительны только для protocolVersion = ECv2.

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

Далее описано, как обрабатывать данные полезной нагрузки ECv2 Google Pay API PaymentMethodToken.

  1. Получите корневые ключи подписи Google.
  2. Проверьте правильность подписи промежуточного ключа с помощью одного из активных корневых ключей.
  3. Проверьте, не истек ли срок действия промежуточного ключа полезной нагрузки.
  4. Проверьте правильность подписей данных в полезной нагрузке с помощью промежуточного ключа.
  5. Расшифруйте данные полезной нагрузки.
  6. Убедитесь, что срок действия сообщения не истек, проверив поле messageExpiration в расшифрованных данных.
  7. Используйте способ оплаты, указанный в расшифрованном сообщении, для списания средств.

В библиотеке Tink можно найти пример кода, исполняющего пункты 1–6.

Структура токенов способа оплаты

Сообщение, отправляемое Google в ответе PaymentData, представляет собой закодированный по стандарту UTF-8 сериализованный объект JSON с указанными ниже ключами.

Название Тип Описание
protocolVersion Строка Определяет схему шифрования и подписи данных, использованную для создания сообщения. Позволяет протоколу при необходимости меняться.
signature Строка Проверяет, является ли Google отправителем сообщения. Создается с использованием кодировки Base64 и алгоритма ECDSA.
intermediateSigningKey Объект Объект JSON, содержащий промежуточный ключ подписи Google со значениями signedKey, keyValue, keyExpiration и signatures. Объект сериализован для упрощения процесса подтверждения подписи.
signedMessage Строка Сериализованная строка JSON, содержащая 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 представляет собой закодированный по стандарту UTF-8 сериализованный объект JSON с указанными ниже значениями.

Название Тип Описание
signedKey Строка Сообщение в кодировке Base64, содержащее платежное описание ключа.
signatures Строка Проверяет, является ли Google отправителем промежуточного ключа. Создается с использованием кодировки Base64 и алгоритма ECDSA.

Подписанный ключ

signedKey представляет собой закодированный по стандарту UTF-8 сериализованный объект JSON с указанными ниже значениями.

Название Тип Описание
keyValue Строка Версия ключа в формате ASN.1, зашифрованная с использованием Base64. Информация в строке SubjectPublicKeyInfo определяется в соответствии со стандартом X.509.
keyExpiration Строка Дата и время окончания действия промежуточного ключа (UTC) в миллисекундах UNIX-времени. Интеграторы отклоняют все ключи с истекшим сроком действия.

Подписанное сообщение

signedMessage представляет собой закодированный по стандарту UTF-8 сериализованный объект JSON с указанными ниже значениями.

Название Тип Описание
encryptedMessage Строка Сообщение в кодировке Base64, содержащее сведения об оплате и дополнительные поля безопасности.
ephemeralPublicKey Строка Эфемерный открытый ключ, закодированный по стандартному алгоритму Base64 и связанный с закрытым ключом шифрования в несжатом формате. Дополнительную информацию вы можете найти в разделе Формат открытого ключа шифрования.
tag Строка Код проверки подлинности сообщения encryptedMessage, закодированный с использованием Base64.

Зашифрованное сообщение

Расшифрованное значение строки encryptedMessage представляет собой закодированный по стандарту UTF-8 сериализованный объект JSON. Объект JSON состоит из двух уровней. Внешний уровень содержит метаданные и поля, обеспечивающие безопасность. Внутренний же уровень – это ещё один объект JSON, включающий фактические платежные данные.

Подробная информация о сообщении encryptedMessage представлена в следующих таблицах и примерах объектов JSON.

Название Тип Описание
gatewayMerchantId Строка

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

messageExpiration Строка Дата и время окончания действия сообщения (UTC) в миллисекундах UNIX-времени. Интеграторы должны отклонять все сообщения с истекшим сроком действия.
messageId Строка Уникальный идентификатор, позволяющий найти сообщение в случае его отмены или переноса на более поздний срок.
paymentMethod Строка Тип платежных данных. В настоящее время поддерживается только формат CARD.
paymentMethodDetails Объект Платежные данные. Формат этого объекта JSON определяется значением параметра 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": 2020
  },
  "messageId": "some-message-id",
  "messageExpiration": "1577862000000"
}

CRYPTOGRAM_3DS

Формат CARD, для аутентификации которого используется криптограмма 3-D Secure (CRYPTOGRAM_3DS authMethod), включает дополнительные поля:

Название Тип Описание
cryptogram Строка Криптограмма 3-DSecure.
eciIndicator Строка Присутствует не всегда. Используется, только если карта относится к платежной системе Visa. Это значение должно быть передано в запросе авторизации платежа.

Фрагмент кода в формате JSON ниже – это пример полного сообщения encryptedMessage для формата CARD paymentMethod со способом аутентификации CRYPTOGRAM_3DS authMethod.

{
  "paymentMethod": "CARD",
  "paymentMethodDetails": {
    "authMethod": "CRYPTOGRAM_3DS",
    "pan": "1111222233334444",
    "expirationMonth": 10,
    "expirationYear": 2020,
    "cryptogram": "AAAAAA...",
    "eciIndicator": "eci indicator"
  },
  "messageId": "some-message-id",
  "messageExpiration": "1577862000000"
}

Подтверждение подписи

Для подтверждения подписей с промежуточным ключом и подписями сообщений нужно следующее:

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

Алгоритм подписи

Google использует алгоритм цифровой подписи на эллиптических кривых (ECDSA) для подписи сообщений со следующими параметрами: алгоритм ECDSA на эллиптической кривой NIST P-256 с хеш-функцией SHA-256 в соответствии с FIPS 186-4.

Подпись

Подпись включена во внешний уровень сообщения. Она кодируется с использованием Base64 в байтовом формате ASN.1. Чтобы получить дополнительную информацию о формате ASN.1, изучите приложение А к документу "Инструменты IETF". Подпись состоит из целых чисел 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_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. Достаточно подтвердить всего одну подпись. Чтобы подтвердить signedStringForMessageSignature, используйте значение intermediateSigningKey.signedKey.keyValue. Компания 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.

Пример

В этом примере используется следующий токен способа оплаты:

{
  "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 для продавцов с доступом к рабочей версии.

Поскольку у параметра 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 API. В схеме шифрования используются указанные ниже параметры.

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

ECIES-KEM в соответствии со стандартом ISO 18033-2.

  • Эллиптическая кривая – NIST P-256 (также известна как prime256v1 в OpenSSL).
  • Значения 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" -

После ввода команды вы увидите строку примерно следующего содержания (это версия ключа с кодировкой Base64 в несжатом формате):

BOdoXP+9Aq473SnGwg3JU1aiNpsd9vH2ognq4PtDtlLGa3Kj8TPf+jaQNPyDSkh3JUhiS0KyrrlWhAgNZKHYF2Y=

Шаг 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 (также известна как prime256v1 в OpenSSL);
    • значения CheckMode, OldCofactorMode, SingleHashMode и CofactorMode равны 0;
    • функция кодировки – несжатый формат;
    • функция формирования ключа – HKDF с SHA256; она подробно описана в документе 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, которые кешируются до истечения срока действия ключей. Срок действия ключей определяется значением поля keyExpiration. Если срок действия ключей истек, запросите новые с общедоступного URL.

Примечание для пользователей протокола ECv2. Если получить ключи подписи Google не удается, перейдите по URL рабочего развертывания, сохраните в системе файл keys.json и периодически обновляйте их вручную. Обычно новые ключи подписи для протокола 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 – это версия ключа в формате ASN.1, зашифрованная с использованием Base64 (без переносов строк, с заполнением). Информация в строке SubjectPublicKeyInfo определяется в соответствии со стандартом X.509. В языке Java формат кодирования ASN.1 представлен классом X509EncodedKeySpec. Его можно получить с помощью параметра ECPublicKey.getEncoded().

Предоставляются URL-адреса для тестовой и рабочей среды.

Обновление ключей

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

Чтобы сделать это, выполните следующие действия:

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

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

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

    Убедитесь, что код для расшифровки развернут в среде эксплуатации, и следите за тем, насколько успешно проходит расшифровка.

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

    Укажите в свойстве publicKey PaymentMethodTokenizationSpecification новое значение атрибута parameters. Вот оно:

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

  7. Удалите старый закрытый ключ.
  8. В профиле разработчика Google Pay удалите старый открытый ключ из раздела "Открытые ключи шифрования" и нажмите Сохранить.

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

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

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

Используйте криптографическую библиотеку Tink для подтверждения подписей и расшифровки сообщений. Чтобы интегрировать свое приложение с библиотекой Tink и совершить подтверждение/расшифровку, необходимо выполнить указанные ниже действия.

  • В файле pom.xml добавьте утилиту Tink paymentmethodtoken в качестве зависимости.

    <dependencies>
      <!-- other dependencies ... -->
      <dependency>
        <groupId>com.google.crypto.tink</groupId>
        <artifactId>apps-paymentmethodtoken</artifactId>
        <version>1.2.0</version>
      </dependency>
    </dependencies>
    
  • При запуске сервера создайте выборку ключей подписи Google. Благодаря этому при расшифровке данных ключ будет загружаться быстрее.

    GooglePaymentsPublicKeysManager.INSTANCE_PRODUCTION.refreshInBackground();
  • Используйте следующий код для расшифровки данных. Предполагается, что paymentMethodToken хранится внутри переменной encryptedMessage. Обратите внимание, что элементы, выделенные полужирным, нужно изменить в соответствии с вашим сценарием.

    Для тестирования среды замените INSTANCE_PRODUCTION на INSTANCE_TEST, а [YOUR MERCHANT ID] – на 12345678901234567890.

    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 и PrivateKey2 используйте свои собственные ключи. Переменные могут быть представлены в виде строки, закодированной в Base64 в формате PKCS8, или в виде объекта ECPrivateKey. Подробную информацию о том, как создать закрытый ключ с кодировкой Base64 в формате PKCS8, вы найдете в разделе Как создать пару ключей с помощью OpenSSL.

    Если вы не можете каждый раз при расшифровке ключей вызывать сервер 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 апреля 2038 года. В случае компрометации ключа Google отправит на адреса электронной почты, которые продавцы указали на портале, уведомление о том, что необходимо перезапустить файл keys.json раньше срока.

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

    • получение ключей подписи Google и их кеширование в памяти;
    • подтверждение подписи;
    • расшифровка.