Работа с подписью

Общая информация

Для обеспечения защищённого обмена данными при работе с платёжной платформой ECommPay используется криптографический протокол TLS (Transport Layer Security; протокол защиты транспортного уровня) версии не ниже 1.2, а для подтверждения авторства и целостности передаваемых данных дополнительно применяется цифровая подпись. Она формируется и проверяется по заданным алгоритмам с использованием одинакового секретного ключа, доступного на двух сторонах: мерчанта и ECommPay.

Независимо от интерфейса, используемого для работы с платформой, подпись обязательна к включению в состав всех программных запросов и оповещений (по запросам, выполняемым в рамках асинхронной схемы взаимодействия), а также выборочно применяется для ответов (по запросам, выполняемым в рамках синхронной схемы). Без подписи могут оставаться ответы с исключительно служебной информацией (например, об отклонении запроса из-за его некорректности) или с информацией общего характера, не раскрывающей специфику конкретного платежа или данные пользователя (например, в ответе со списком доступных банков). В остальных случаях подпись включается в состав ответов.

Для корректной работы перед отправкой в платформу любого запроса необходимо сформировать подпись и включить её в состав этого запроса, а при получении от платформы оповещений и ответов, содержащих подпись, следует проверять целостность данных путём сличения расчётных подписей с полученными. Для этого можно использовать как собственные программные решения, так и SDK от ECommPay (подробнее). Нарушения целостности данных могут быть вызваны разными причинами, но при обнаружении таких нарушений данные не могут и не должны считаться достоверными.

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

Подписывание данных

Описание алгоритма

В качестве входных данных для подписывания выступают:

  1. Данные, которые требуется подписать.

    Как правило, это заполненное тело запроса в формате JSON без включения в него параметра signature.

  2. Ключ, используемый для подписывания.
    Прим.: Для работы с Data API должны использоваться ключи, получаемые через интерфейс Dashboard (Old Dashboard) в связке с токенами. (Подробнее в разделе Порядок доступа к данным.)

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

В качестве выходных данных подписывания в зависимости от реализации алгоритма могут выступать либо подпись, либо подписанные данные — как правило, это итоговое тело запроса в формате JSON с параметром signature, включённым в его состав.

Далее в описании, примере и форме для тестирования представлен часто используемый и наиболее показательный вариант реализации алгоритма: с телом запроса в формате JSON на входе (без параметра signature) и на выходе (с параметром signature).

В состав алгоритма в этом случае включаются следующие шаги:

  1. Проверка входных данных на соблюдение заданных требований:

    1. Структура данных для подписывания должна соответствовать формату JSON.
    2. В составе данных для подписывания не должен присутствовать параметр signature (даже с пустым значением).
    3. Должен быть задан ключ.
  2. Преобразование данных в строку UTF-8 с сортировкой параметров по алфавиту. В рамках этого шага выполняются следующие действия:

    1. Логические (булевы) значения кодируются следующим образом: false заменяется на 0, а true — на 1.

      Но это относится только к булевым значениям: в строковых параметрах, даже если они содержат значения false или true, замены на 0 или 1 не используются!

    2. Каждый параметр преобразуется в строку, содержащую полный путь к параметру, название параметра и его значение:
      <родительский_узел_1>:...:<родительский_узел_N>:<название_параметра>:<значение_параметра>

      где родительские узлы — это названия объектов и (или) массивов, в состав которых включена пара «название параметра — значение параметра». Родительские узлы располагаются в порядке их вложения, начиная с самого верхнего уровня. В качестве разделителя при этом используется двоеточие (:), между парами «название параметра — значение параметра» удаляются запятые, а у строковых значений параметров удаляются обрамляющие кавычки.

    3. Параметры с нулевыми, а также пустыми значениями остаются в строке, например запись "operation_type":"" представляется в виде operation_type:. Замены пустых значений на пробелы или null не применяются.
    4. Кодировка всех строк приводится к формату UTF-8.
    5. Полученные строки упорядочиваются в алфавитном порядке и объединяются в одну строку с использованием в качестве разделителя точки с запятой (;).
  3. Получение двоичного кода HMAC с использованием ключа и функции SHA-512. На этом шаге для полученной строки с параметрами вычисляется HMAC (Hash-based Message Authentication Code; код аутентификации сообщений с использованием хеш-функции) с использованием функции хеширования SHA‑512 (Secure Hash Algorithm; безопасный алгоритм хеширования) и применяемого ключа. И этот HMAC представляется в виде необработанных двоичных данных.
  4. Кодирование двоичного кода HMAC с применением алгоритма Base64. На этом шаге полученный двоичный код HMAC кодируется с использованием алгоритма Base64. Получаемая при этом строка является подписью к исходным данным.
  5. Добавление подписи к данным. На этом шаге к исходным данным для подписывания добавляется параметр signature с полученной подписью в качестве его значения.

Пример для запроса на получение данных через Data API

Допустим, что надо подписать запрос в Dashboard при следующих условиях:

  • Используемый ключ — secret
  • Предварительная версия тела запроса, в котором еще нет значения параметра с подписью, выглядит так:
    {
      "token": "WKiarERJ5pcceNerpM9R5TNnyPTQMl",
      "interval": {
        "from": "2020-01-01 14:53:55",
        "to": "2020-01-30 13:53:59"
      },
      "project_id": [
        183
      ],
      "limit": 3,
      "offset": 0,
      "tz": "Europe/Moscow",
     "signature": "<подпись, которую нужно создать>"
    }

Задача заключается в том, чтобы вычислить подпись, то есть рассчитать значение параметра signature и добавить его в запрос. Для этого необходимо:

  1. Убедиться, что в теле запроса нет параметра signature, даже с пустым значением. Если такой параметр есть, его нужно удалить.
    {
      "token": "WKiarERJ5pcceNerpM9R5TNnyPTQMl",
      "interval": {
        "from": "2020-01-01 14:53:55",
        "to": "2020-01-30 13:53:59"
      },
      "project_id": [
        183
      ],
      "limit": 3,
      "offset": 0,
      "tz": "Europe/Moscow",
     "signature": "<подпись, которую нужно создать>"
    }
  2. Преобразовать оставшиеся параметры в строки UTF-8 согласно правилам алгоритма:
    token:WKiarERJ5pcceNerpM9R5TNnyPTQMl
    interval:from:2020-01-01 14:53:55
    interval:to:2020-01-30 13:53:59
    project_id:0:183
    limit:3
    offset:0
    tz:Europe/Moscow
  3. Отсортировать полученные строки по алфавиту:
    interval:from:2020-01-01 14:53:55
    interval:to:2020-01-30 13:53:59
    limit:3
    offset:0
    project_id:0:183
    token:WKiarERJ5pcceNerpM9R5TNnyPTQMl
    tz:Europe/Moscow
    
  4. Объединить отсортированные строки в одну строку с использованием в качестве разделителя точки с запятой:
    interval:from:2020-01-01 14:53:55;interval:to:2020-01-30 13:53:59;limit:3;offset:0;project_id:0:183;token:WKiarERJ5pcceNerpM9R5TNnyPTQMl;tz:Europe/Moscow
  5. Вычислить HMAC полученной строки с использованием функции хеширования SHA-512 и используемого ключа, после чего кодировать двоичный код HMAC с применением алгоритма Base64:
    w6ZW/DPURM2LXYcviD5yDlwe/gV+yBhCZaL0XbfHkoefY3OuIccdDAyECbhl7/GqA8dZ+R6XFwBUzzm94ywjrw==
  6. Добавить полученную подпись в тело запроса:
    {
      "token": "WKiarERJ5pcceNerpM9R5TNnyPTQMl",
      "interval": {
        "from": "2020-01-01 14:53:55",
        "to": "2020-01-30 13:53:59"
      },
      "project_id": [
        183
      ],
      "limit": 3,
      "offset": 0,
      "tz": "Europe/Moscow",
     "signature": "w6ZW/DPURM2LXYcviD5yDlwe/gV+yBhCZaL0XbfHkoefY3OuIccdDAyECbhl7/GqA8dZ+R6XFwBUzzm94ywjrw=="
    }

Форма для тестирования

Проверка данных

Описание алгоритма

В качестве входных данных для проверки целостности выступают:

  1. Подписанные данные, которые требуется проверить. Как правило, это тело ответа в формате JSON с параметром signature в его составе.
  2. Ключ, используемый для проверки. Это должен быть ровно тот ключ, который был использован для подписывания проверяемых данных.

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

Далее в описании, примере и форме для тестирования представлен часто используемый и наиболее показательный вариант реализации алгоритма: с телом проверяемого ответа в формате JSON на входе и с заключением о целостности этого сообщения на выходе.

В состав алгоритма в этом случае включаются следующие шаги:

  1. Проверка входных данных на соблюдение заданных требований:
    1. Структура проверяемых данных должна соответствовать формату JSON.
    2. В составе проверяемых данных должен присутствовать параметр signature с подписью.
    3. Должен быть задан проверочный ключ.
  2. Извлечение подписи из проверяемых данных. На этом шаге из проверяемых данных исключается параметр signature, а его значение фиксируется для последующего сличения с расчётной подписью.
  3. Формирование расчётной подписи для проверяемых данных:
    1. Преобразование данных в строку UTF-8 с сортировкой параметров по алфавиту. В рамках этого шага выполняются следующие действия:
      1. Логические (булевы) значения кодируются следующим образом: false заменяется на 0, а true — на 1.

        Но это относится только к булевым значениям: в строковых параметрах, даже если они содержат значения false или true, замены на 0 или 1 не используются!

      2. Каждый параметр преобразуется в строку, содержащую полный путь к параметру, название параметра и его значение:
        <родительский_узел_1>:...:<родительский_узел_N>:<название_параметра>:<значение_параметра>

        где родительские узлы — это названия объектов и (или) массивов, в состав которых включена пара «название параметра — значение параметра». Родительские узлы располагаются в порядке их вложения, начиная с самого верхнего уровня. В качестве разделителя при этом используется двоеточие (:), между парами «название параметра — значение параметра» удаляются запятые, а у строковых значений параметров удаляются обрамляющие кавычки.

      3. Параметры с нулевыми, а также пустыми значениями остаются в строке, например запись "payment_description":"" представляется в виде payment_description:. Замены пустых значений на пробелы или null не применяются.
      4. Элементы массивов записываются отдельными строками, с указанием номера каждого элемента, начиная с нуля. Например, массив ["alpha", "beta", "gamma"] представляется в виде трёх строк: 0:alpha, 1:beta и 2:gamma.
      5. Пустые массивы полностью игнорируются и не включаются в набор строк для создания подписи.
      6. Кодировка всех строк приводится к формату UTF-8.
      7. Полученные строки упорядочиваются в алфавитном порядке и объединяются в одну строку с использованием в качестве разделителя точки с запятой (;).
    2. Получение двоичного кода HMAC с использованием ключа и функции SHA‑512. На этом шаге для полученной строки с параметрами вычисляется HMAC (Hash-based Message Authentication Code; код аутентификации сообщений с использованием хеш-функции) с использованием функции хеширования SHA‑512 (Secure Hash Algorithm; безопасный алгоритм хеширования) и применяемого ключа. И этот HMAC представляется в виде необработанных двоичных данных.
    3. Кодирование двоичного кода HMAC с применением алгоритма Base64. На этом шаге полученный двоичный код HMAC кодируется с использованием алгоритма Base64. Получаемая при этом строка является подписью к исходным данным.
  4. Сопоставление подписей. На этом шаге расчётная подпись сопоставляется с проверяемой. Если подписи совпадают, данные признаются целостными и достоверными. При несовпадении подписей данные не могут считаться достоверными и не должны использоваться в качестве рабочих.

Пример для ответа с информацией об операциях

Допустим, что надо проверить подпись ответа при следующих условиях:

  • Используемый ключ — secret
  • Тело полученного ответа выглядит так:

    Рис.: Тело ответа

    {
      "operations": [
        {
          "project_id": "183",
          "operation_id": "9048253065548",
          "payment_id": "EP834a-40521580376090593",
          "operation_type": "cancel",
          "operation_status": "success",
          "account_number": "424242******4242",
          "customer_ip": "185.123.193.224",
          "payment_method_name": "visa",
          "payment_method_type": "visa",
          "payment_description": null,
          "operation_created_at": "2020-01-30T12:29:03+03:00",
          "operation_completed_at": "2020-01-30T12:29:04+03:00",
          "provider_date": null,
          "shipment_date": "",
          "mid": "3416123",
          "sum_initial": {
            "amount": 2000,
            "currency": "RUB"
          },
          "sum_converted": {
            "amount": 2000,
            "currency": "RUB"
          },
          "provider_name": "Dashboard Provider Card",
          "fee_currency": null,
          "fee_amount": 0,
          "arn": null,
          "rrn": null
        }
      ],
      "signature": "EksxDdDygDQ30JKsfK6QSvubpNRSj3wtLI5FzWDJuNY0nEhLXt65Y77dtKMJRcd39NegA7YK1eojA2EB1hIbnQ=="
    }

Для проверки подписи необходимо:

  1. Удалить из тела ответа параметр signature вместе с его значением:
    {
      "operations": [
        {
          "project_id": "183",
          "operation_id": "9048253065548",
          "payment_id": "EP834a-40521580376090593",
          "operation_type": "cancel",
          "operation_status": "success",
          "account_number": "424242******4242",
          "customer_ip": "185.123.193.224",
          "payment_method_name": "visa",
          "payment_method_type": "visa",
          "payment_description": null,
          "operation_created_at": "2020-01-30T12:29:03+03:00",
          "operation_completed_at": "2020-01-30T12:29:04+03:00",
          "provider_date": null,
          "shipment_date": "",
          "mid": "3416123",
          "sum_initial": {
            "amount": 2000,
            "currency": "RUB"
          },
          "sum_converted": {
            "amount": 2000,
            "currency": "RUB"
          },
          "provider_name": "Dashboard Provider Card",
          "fee_currency": null,
          "fee_amount": 0,
          "arn": null,
          "rrn": null
        }
      ],
      "signature": "EksxDdDygDQ30JKsfK6QSvubpNRSj3wtLI5FzWDJuNY0nEhLXt65Y77dtKMJRcd39NegA7YK1eojA2EB1hIbnQ=="
    }
  2. Преобразовать оставшиеся параметры в строки UTF-8 согласно правилам алгоритма:

    operations:0:project_id:183
    operations:0:operation_id:9048253065548
    operations:0:payment_id:EP834a-40521580376090593
    operations:0:operation_type:cancel
    operations:0:operation_status:success
    operations:0:account_number:424242******4242
    operations:0:customer_ip:185.123.193.224
    operations:0:payment_method_name:visa
    operations:0:payment_method_type:visa
    operations:0:payment_description:
    operations:0:operation_created_at:2020-01-30T12:29:03+03:00
    operations:0:operation_completed_at:2020-01-30T12:29:04+03:00
    operations:0:provider_date:
    operations:0:shipment_date:
    operations:0:mid:3416123
    operations:0:sum_initial:amount:2000
    operations:0:sum_initial:currency:RUB
    operations:0:sum_converted:currency:RUB
    operations:0:sum_converted:amount:2000
    operations:0:provider_name:Dashboard Provider Card
    operations:0:fee_currency:
    operations:0:fee_amount:0
    operations:0:arn:
    operations:0:rrn:
  3. Отсортировать полученные строки по алфавиту:
    operations:0:account_number:424242******4242
    operations:0:arn:
    operations:0:customer_ip:185.123.193.224
    operations:0:fee_amount:0
    operations:0:fee_currency:
    operations:0:mid:3416123
    operations:0:operation_completed_at:2020-01-30T12:29:04+03:00
    operations:0:operation_created_at:2020-01-30T12:29:03+03:00
    operations:0:operation_id:9048253065548
    operations:0:operation_status:success
    operations:0:operation_type:cancel
    operations:0:payment_description:
    operations:0:payment_id:EP834a-40521580376090593
    operations:0:payment_method_name:visa
    operations:0:payment_method_type:visa
    operations:0:project_id:183
    operations:0:provider_date:
    operations:0:provider_name:Dashboard Provider Card
    operations:0:rrn:
    operations:0:shipment_date:
    operations:0:sum_converted:amount:2000
    operations:0:sum_converted:currency:RUB
    operations:0:sum_initial:amount:2000
    operations:0:sum_initial:currency:RUB
    
  4. Объединить отсортированные строки в одну строку с использованием в качестве разделителя точки с запятой:
    operations:0:account_number:424242******4242;operations:0:arn:;operations:0:customer_ip:185.123.193.224;operations:0:fee_amount:0;operations:0:fee_currency:;operations:0:mid:3416123;operations:0:operation_completed_at:2020-01-30T12:29:04+03:00;operations:0:operation_created_at:2020-01-30T12:29:03+03:00;operations:0:operation_id:9048253065548;operations:0:operation_status:success;operations:0:operation_type:cancel;operations:0:payment_description:;operations:0:payment_id:EP834a-40521580376090593;operations:0:payment_method_name:visa;operations:0:payment_method_type:visa;operations:0:project_id:183;operations:0:provider_date:;operations:0:provider_name:Dashboard Provider Card;operations:0:rrn:;operations:0:shipment_date:;operations:0:sum_converted:amount:2000;operations:0:sum_converted:currency:RUB;operations:0:sum_initial:amount:2000;operations:0:sum_initial:currency:RUB
  5. Вычислить HMAC полученной строки с использованием функции хеширования SHA-512 и используемого ключа, после чего кодировать двоичный код HMAC с применением алгоритма Base64:
    TiDoAPZSQR4Clhg5qj2wLD2tTatx0AUa0erwV++fkOV4kfxy/bBuE5hsnvyk2dzADS3tFC20+czK1dK6YCaJ3g==
  6. Сравнить полученную подпись с проверяемой.

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

Форма для тестирования