Работа с подписью
Общая информация
Для обеспечения защищённого обмена данными при работе с платёжной платформой ecommpay используется криптографический протокол TLS (Transport Layer Security; протокол защиты транспортного уровня) версии не ниже 1.2, а для подтверждения авторства и целостности передаваемых данных дополнительно применяется цифровая подпись. Она формируется и проверяется по заданным алгоритмам с использованием одинакового секретного ключа, доступного на двух сторонах: мерчанта и ecommpay.
Независимо от интерфейса, используемого для работы с платформой, подпись обязательна к включению в состав всех программных запросов и оповещений (по запросам, выполняемым в рамках асинхронной схемы взаимодействия), а также выборочно применяется для ответов (по запросам, выполняемым в рамках синхронной схемы). Без подписи могут оставаться ответы с исключительно служебной информацией (например, об отклонении запроса из-за его некорректности) или с информацией общего характера, не раскрывающей специфику конкретного платежа или данные пользователя (например, в ответе со списком доступных банков). В остальных случаях подпись включается в состав ответов.
Для корректной работы перед отправкой в платформу любого запроса необходимо сформировать подпись и включить её в состав этого запроса, а при получении от платформы оповещений и ответов, содержащих подпись, следует проверять целостность данных путём сличения расчётных подписей с полученными. Для этого можно использовать как собственные программные решения, так и SDK от ecommpay (подробнее). Нарушения целостности данных могут быть вызваны разными причинами, но при обнаружении таких нарушений данные не могут и не должны считаться достоверными.
Далее описаны алгоритмы подписывания данных и проверки их целостности, а также представлены примеры выполнения этих алгоритмов и интерактивные формы для самостоятельной проверки корректной работы с подписью к разным данным.
Подписывание данных
Описание алгоритма
В качестве входных данных для подписывания выступают:
- Данные, которые требуется подписать.
Как правило, это заполненное тело запроса в формате JSON без включения в него параметра
signature
. - Ключ, используемый для подписывания.Прим.: Для работы с Data API должны использоваться ключи, получаемые через интерфейс Dashboard в связке с токенами. (Подробнее в разделе Порядок доступа к данным.)
Для отладки и тестирования алгоритма подписывания могут использоваться произвольные ключи. Для рабочих запросов в платформу следует применять только актуальный секретный ключ
В качестве выходных данных подписывания в зависимости от реализации алгоритма могут выступать либо подпись, либо подписанные данные — как правило, это итоговое тело запроса в формате JSON с параметром signature
, включённым в его состав.
Далее в описании, примере и форме для тестирования представлен часто используемый и наиболее показательный вариант реализации алгоритма: с телом запроса в формате JSON на входе (без параметра signature
) и на выходе (с параметром signature
).
В состав алгоритма в этом случае включаются следующие шаги:
-
Проверка входных данных на соблюдение заданных требований:
- Структура данных для подписывания должна соответствовать формату JSON.
- В составе данных для подписывания не должен присутствовать параметр
signature
(даже с пустым значением). - Должен быть задан ключ.
-
Преобразование данных в строку UTF-8 с сортировкой параметров в естественном порядке. В рамках этого шага выполняются следующие действия:
- Логические (булевы) значения кодируются следующим образом:
false
заменяется на0
, аtrue
— на1
. Но это относится только к булевым значениям — в строковых параметрах, даже если они содержат значенияfalse
илиtrue
, замены на0
или1
не используются (например, в параметреrecurring: "{type: \"U\",register: true}"
заменаtrue
на1
не требуется). - Каждый параметр преобразуется в строку, содержащую полный путь к параметру, название параметра и его значение:
<родительский_узел_1>:...:<родительский_узел_N>:<название_параметра>:<значение_параметра>
где родительские узлы — это названия объектов и (или) массивов, в состав которых включена пара «название параметра — значение параметра». Родительские узлы располагаются в порядке их вложения, начиная с самого верхнего уровня. В качестве разделителя при этом используется двоеточие (:), между парами «название параметра — значение параметра» удаляются запятые, а у строковых значений параметров удаляются обрамляющие кавычки.
- Параметры с нулевыми, а также пустыми значениями остаются в строке, например запись
"operation_type":""
представляется в видеoperation_type:
. Замены пустых значений на пробелы илиnull
не применяются. - Кодировка всех строк приводится к формату UTF-8.
- Полученные строки упорядочиваются в естественном порядке и объединяются в одну строку с использованием в качестве разделителя точки с запятой (;).
- Логические (булевы) значения кодируются следующим образом:
- Получение двоичного кода HMAC с использованием ключа и функции SHA-512. На этом шаге для полученной строки с параметрами вычисляется HMAC (Hash-based Message Authentication Code; код аутентификации сообщений с использованием хеш-функции) с использованием функции хеширования SHA‑512 (Secure Hash Algorithm; безопасный алгоритм хеширования) и применяемого ключа. И этот HMAC представляется в виде необработанных двоичных данных.
- Кодирование двоичного кода HMAC с применением алгоритма Base64. На этом шаге полученный двоичный код HMAC кодируется с использованием алгоритма Base64. Получаемая при этом строка является подписью к исходным данным.
- Добавление подписи к данным. На этом шаге к исходным данным для подписывания добавляется параметр
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": "Asia/Singapore", "signature": "<подпись, которую нужно создать>" }
Задача заключается в том, чтобы вычислить подпись, то есть рассчитать значение параметра signature
и добавить его в запрос. Для этого необходимо:
- Убедиться, что в теле запроса нет параметра
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": "Asia/Singapore", "signature": "<подпись, которую нужно создать>" }
- Преобразовать оставшиеся параметры в строки 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:Asia/Singapore
- Отсортировать полученные строки в естественном порядке:
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:Asia/Singapore
- Объединить отсортированные строки в одну строку с использованием в качестве разделителя точки с запятой:
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:Asia/Singapore
- Вычислить HMAC полученной строки с использованием функции хеширования SHA-512 и используемого ключа, после чего кодировать двоичный код HMAC с применением алгоритма Base64:
Ini3aKje6aZskajTuRS761YOzVqierlVRafZdxIz48wmVnL7yxgy9vDsp7T2/LGPGHJ/DHoKOgP7VqObJALrUA==
- Добавить полученную подпись в тело запроса:
{ "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": "Asia/Singapore", "signature": "Ini3aKje6aZskajTuRS761YOzVqierlVRafZdxIz48wmVnL7yxgy9vDsp7T2/LGPGHJ/DHoKOgP7VqObJALrUA==" }
Форма для тестирования
Проверка данных
Описание алгоритма
В качестве входных данных для проверки целостности выступают:
- Подписанные данные, которые требуется проверить. Как правило, это тело ответа в формате JSON с параметром
signature
в его составе. - Ключ, используемый для проверки. Это должен быть ровно тот ключ, который был использован для подписывания проверяемых данных.
В качестве выходных данных при проверке целостности в зависимости от реализации алгоритма могут выступать расчётная подпись и информация о её совпадении с проверяемой подписью, то есть информация о целостности проверяемых данных.
Далее в описании, примере и форме для тестирования представлен часто используемый и наиболее показательный вариант реализации алгоритма: с телом проверяемого ответа в формате JSON на входе и с заключением о целостности этого сообщения на выходе.
В состав алгоритма в этом случае включаются следующие шаги:
- Проверка входных данных на соблюдение заданных требований:
- Структура проверяемых данных должна соответствовать формату JSON.
- В составе проверяемых данных должен присутствовать параметр
signature
с подписью. - Должен быть задан проверочный ключ.
- Извлечение подписи из проверяемых данных. На этом шаге из проверяемых данных исключается параметр
signature
, а его значение фиксируется для последующего сличения с расчётной подписью. - Формирование расчётной подписи для проверяемых данных:
- Преобразование данных в строку UTF-8 с сортировкой параметров в естественном порядке. В рамках этого шага выполняются следующие действия:
- Логические (булевы) значения кодируются следующим образом:
false
заменяется на0
, аtrue
— на1
. Но это относится только к булевым значениям — в строковых параметрах, даже если они содержат значенияfalse
илиtrue
, замены на0
или1
не используются (например, в параметреrecurring: "{type: \"U\",register: true}"
заменаtrue
на1
не требуется). - Каждый параметр преобразуется в строку, содержащую полный путь к параметру, название параметра и его значение:
<родительский_узел_1>:...:<родительский_узел_N>:<название_параметра>:<значение_параметра>
где родительские узлы — это названия объектов и (или) массивов, в состав которых включена пара «название параметра — значение параметра». Родительские узлы располагаются в порядке их вложения, начиная с самого верхнего уровня. В качестве разделителя при этом используется двоеточие (:), между парами «название параметра — значение параметра» удаляются запятые, а у строковых значений параметров удаляются обрамляющие кавычки.
- Параметры с нулевыми, а также пустыми значениями остаются в строке, например запись
"payment_description":""
представляется в видеpayment_description:
. Замены пустых значений на пробелы илиnull
не применяются. - Элементы массивов записываются отдельными строками, с указанием номера каждого элемента, начиная с нуля. Например, массив
["alpha", "beta", "gamma"]
представляется в виде трёх строк:0:alpha
,1:beta
и2:gamma
. - Пустые массивы полностью игнорируются и не включаются в набор строк для создания подписи.
- Кодировка всех строк приводится к формату UTF-8.
- Полученные строки упорядочиваются в естественном порядке и объединяются в одну строку с использованием в качестве разделителя точки с запятой (;).
- Логические (булевы) значения кодируются следующим образом:
- Получение двоичного кода HMAC с использованием ключа и функции SHA‑512. На этом шаге для полученной строки с параметрами вычисляется HMAC (Hash-based Message Authentication Code; код аутентификации сообщений с использованием хеш-функции) с использованием функции хеширования SHA‑512 (Secure Hash Algorithm; безопасный алгоритм хеширования) и применяемого ключа. И этот HMAC представляется в виде необработанных двоичных данных.
- Кодирование двоичного кода HMAC с применением алгоритма Base64. На этом шаге полученный двоичный код HMAC кодируется с использованием алгоритма Base64. Получаемая при этом строка является подписью к исходным данным.
- Преобразование данных в строку UTF-8 с сортировкой параметров в естественном порядке. В рамках этого шага выполняются следующие действия:
- Сопоставление подписей. На этом шаге расчётная подпись сопоставляется с проверяемой. Если подписи совпадают, данные признаются целостными и достоверными. При несовпадении подписей данные не могут считаться достоверными и не должны использоваться в качестве рабочих.
Пример для ответа с информацией об операциях
Допустим, что надо проверить подпись ответа при следующих условиях:
- Используемый ключ —
secret
- Тело полученного ответа выглядит так:
Для проверки подписи необходимо:
- Удалить из тела ответа параметр
signature
вместе с его значением:{ "operations": [ { "project_id": "183", "operation_id": "9048253065548", "payment_id": "EP834a-40521580376090593", "operation_type": "cancel", "operation_status": "success", "account_number": "431422******0056", "customer_ip": "192.0.0.255", "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": "EUR" }, "sum_converted": { "amount": 2000, "currency": "EUR" }, "provider_name": "Dashboard Provider Card", "fee_currency": null, "fee_amount": 0, "arn": null, "rrn": null } ], "signature": "EksxDdDygDQ30JKsfK6QSvubpNRSj3wtLI5FzWDJuNY0nEhLXt65Y77dtKMJRcd39NegA7YK1eojA2EB1hIbnQ==" }
-
Преобразовать оставшиеся параметры в строки 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:431422******0056 operations:0:customer_ip:192.0.0.255 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:EUR operations:0:sum_converted:currency:EUR 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:
- Отсортировать полученные строки в естественном порядке:
operations:0:account_number:431422******0056 operations:0:arn: operations:0:customer_ip:192.0.0.255 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:EUR operations:0:sum_initial:amount:2000 operations:0:sum_initial:currency:EUR
- Объединить отсортированные строки в одну строку с использованием в качестве разделителя точки с запятой:
operations:0:account_number:431422******0056;operations:0:arn:;operations:0:customer_ip:192.0.0.255;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:EUR;operations:0:sum_initial:amount:2000;operations:0:sum_initial:currency:EUR
- Вычислить HMAC полученной строки с использованием функции хеширования SHA-512 и используемого ключа, после чего кодировать двоичный код HMAC с применением алгоритма Base64:
orpqWm+Vu7unNcob7h+jHuk+H4/M9rnX7qFZD657nECok8oKD7IkdwGye3Ag10A5zBg1Ck2DrZnvtaptNjaIkw==
- Сравнить полученную подпись с проверяемой.
В данном случае подписи не совпадают, а это значит, что такой ответ недостоверен или ошибочен и должен быть отброшен.
Форма для тестирования