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

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

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

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

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

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

Ниже описано, как обрабатывать полезную нагрузку PaymentMethodToken ECv2, полученную от Google Pay API.

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

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

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

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

Имя Тип Описание
protocolVersion Строка Определяет схему шифрования и подписывания данных, использованную для создания сообщения. Позволяет при необходимости вносить изменения в протокол.
signature Строка Проверяет, является ли Google отправителем сообщения. Создается промежуточным ключом с использованием кодировки Base64 и алгоритма ECDSA.
intermediateSigningKey Объект Объект JSON, содержащий промежуточный ключ подписи Google со значениями signedKey (keyValue, keyExpiration) и signatures. Объект сериализован, чтобы упростить подтверждение подписи.
signedMessage Строка Сериализованный объект JSON в виде строки, содержащий значения 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 Объект Платежные данные. Формат этого объекта определяется значением параметра 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": 2020
      },      "messageId": "some-message-id",
      "messageExpiration": "1577862000000"
    }
    

CRYPTOGRAM_3DS

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

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

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

    {
      "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 Pay API применяется интегрированная схема шифрования на эллиптических кривых (ECIES). В этой схеме шифрования используются указанные ниже параметры.

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

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

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

Алгоритм HMAC и SHA-256 (HKDFwithSHA256).

  • Предоставлять соль не нужно.
  • В случае применения протокола ECv2 информация должна кодироваться Google (кодировка ASCII).
  • 256 бит должны быть выделены для ключа AES256, и ещё 256 бит – для ключа HMAC_SHA256.
Алгоритм симметричного шифрования

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

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

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

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

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

  • магическое число, указывающее на формат (0x04);
  • два больших 32-битных целых числа, представляющих координаты X и Y на эллиптической кривой.

Этот формат подробно описан в документе "Криптография с открытым ключом для индустрии финансовых услуг: алгоритм цифровой подписи на эллиптических кривых (ECDSA)", ANSI X9.62, 1998.

Как создать открытый ключ с помощью OpenSSL

Шаг 1. Создайте закрытый ключ

Ниже показан пример команды для создания закрытого ключа на эллиптической кривой NIST P-256 и его записи в key.pem.

    openssl ecparam -name prime256v1 -genkey -noout -out key.pem
    

Просмотр закрытых и открытых ключей (необязательно)

Чтобы посмотреть закрытые и открытые ключи, введите следующую команду:

    openssl ec -in key.pem -pubout -text -noout
    

После ввода команды вы увидите строку примерно следующего содержания:

    read EC key
    Private-Key: (256 bit)
    priv:
        08:f4:ae:16:be:22:48:86:90:a6:b8:e3:72:11:cf:
        c8:3b:b6:35:71:5e:d2:f0:c1:a1:3a:4f:91:86:8a:
        f5:d7
    pub:
        04:e7:68:5c:ff:bd:02:ae:3b:dd:29:c6:c2:0d:c9:
        53:56:a2:36:9b:1d:f6:f1:f6:a2:09:ea:e0:fb:43:
        b6:52:c6:6b:72:a3:f1:33:df:fa:36:90:34:fc:83:
        4a:48:77:25:48:62:4b:42:b2:ae:b9:56:84:08:0d:
        64:a1:d8:17:66
    ASN1 OID: prime256v1
    

Шаг 2. Создайте открытый ключ с кодировкой Base64

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

    openssl ec -in key.pem -pubout -text -noout 2> /dev/null | grep "pub:" -A5 | sed 1d | xxd -r -p | base64 | paste -sd "\0" - | tr -d '\n\r ' > publicKey.txt
    

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

    BOdoXP+9Aq473SnGwg3JU1aiNpsd9vH2ognq4PtDtlLGa3Kj8TPf+jaQNPyDSkh3JUhiS0KyrrlWhAgNZKHYF2Y=
    

В этом файле не должно быть лишних пробелов и символов возврата каретки. Чтобы убедиться в этом, запустите следующую команду в Linux или MacOS:

    od -bc publicKey.txt
    

Шаг 3. Создайте закрытый ключ с кодировкой Base64 в формате PKCS #8

Для библиотеки Tink нужен закрытый ключ с кодировкой Base64 в формате PKCS #8. Чтобы создать закрытый ключ в нужном формате на основе ключа, созаданного на первом шаге, введите следующую команду:

    openssl pkcs8 -topk8 -inform PEM -outform DER -in key.pem -nocrypt | base64 | paste -sd "\0" -
    

После ввода команды вы увидите строку примерно следующего содержания:

    MIGHAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBG0wawIBAQQgWV4oK8c/MZkCLk4qSCNjW0Zm6H0CBCtSYxkXkC9FBHehRANCAAQPldOnhO2/oXjdJD1dwlFPiNs6fcdoRgFu3/Z0iKj24SjTGyLRGAtYWLGXBZcDdPj3T2bJRHRVhE8Bc2AjkT7n
    

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

Выполните следующие действия:

  1. С помощью закрытого ключа и ephemeralPublicKey сформируйте 512-битный общий ключ, применив схему шифрования ECIES-KEM. Укажите следующие параметры:
    • эллиптическая кривая – NIST P-256 (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, возвращаемыми URL. Срок действия ключей определяется значением поля keyExpiration. Если срок действия ключей истек, запросите новые по общедоступному URL.

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

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

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

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

Выполните следующие действия:

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

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

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

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

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

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

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

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

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

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

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

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

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

    GooglePaymentsPublicKeysManager.INSTANCE_PRODUCTION.refreshInBackground();
  3. Для расшифровки ключей используйте код, приведенный ниже. Предполагается, что 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);
        
  4. Вместо PrivateKey1 и PrivateKey2 используйте свои собственные ключи. Переменные могут быть представлены в виде строки, закодированной в Base64 в формате PKCS8, или в виде объекта ECPrivateKey. Подробнее о том, как создать закрытый ключ с кодировкой Base64 в формате PKCS8, можно узнать в разделе Как создать пару ключей с помощью OpenSSL.

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

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

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