Работа с подписью к данным
Общая информация
Для обеспечения защищённого обмена данными при работе с платёжной платформой ECommPay используется криптографический протокол TLS (Transport Layer Security; протокол защиты транспортного уровня) версии не ниже 1.2, а для подтверждения авторства и целостности передаваемых данных дополнительно применяется цифровая подпись. Она формируется и проверяется по заданным алгоритмам с использованием одинакового секретного ключа, доступного на двух сторонах: мерчанта и ECommPay.
Подпись обязательна к включению в состав всех программных запросов, ответов с запрашиваемой информацией (по запросам, выполняемым в рамках синхронной схемы взаимодействия) и оповещений (по запросам, выполняемым в рамках асинхронной схемы) независимо от используемого интерфейса для работы с платформой. Поэтому перед отправкой любого запроса в платформу необходимо сформировать подпись и включить её в состав этого запроса, а при получении ответов и оповещений от платформы следует проверять целостность данных путём сличения расчётных подписей с полученными. Для этого можно использовать собственные программные решенияили и SDK от ECommPay (подробнее).
Нарушения целостности данных могут быть вызваны разными причинами, но при обнаружении таких нарушений данные не могут и не должны считаться достоверными.
Далее описаны алгоритмы подписывания данных и проверки их целостности, а также представлены примеры выполнения этих алгоритмов и интерактивные формы для самостоятельной проверки корректной работы с подписью к разным данным.
Подписывание данных
Описание алгоритма
В качестве входных данных для подписывания выступают:
- Данные, которые требуется подписать.
Как правило, это заполненное тело запроса в формате JSON без включения в него параметра
signature
. - Ключ («соль»), используемый для подписывания.Прим.: Для работы с Data API должны использоваться ключи, получаемые через интерфейс Dashboard (Old Dashboard) в связке с токенами. (Подробнее в разделе API Токены.)
Для отладки и тестирования могут использоваться произвольные ключи, а для рабочих запросов в платформу — только актуальный секретный ключ.
В качестве выходных данных подписывания в зависимости от реализации алгоритма могут выступать либо подпись, либо подписанные данные — как правило, это итоговое тело запроса в формате JSON с параметром signature
, включённым в его состав.
Далее в описании, примере и форме для тестирования представлен часто используемый и наиболее показательный вариант реализации алгоритма: с телом запроса в формате JSON на входе (без параметра signature
) и на выходе (с параметром signature
).
В состав алгоритма в этом случае включаются следующие шаги:
-
Проверка входных данных на соблюдение заданных требований:
- Структура данных для подписывания должна соответствовать формату JSON.
- В составе данных для подписывания не должен присутствовать параметр
signature
(даже с пустым значением). - Должен быть задан ключ («соль»).
-
Преобразование данных в строку UTF-8 с сортировкой параметров по алфавиту. В рамках этого шага выполняются следующие действия:
-
Логические (булевы) значения кодируются следующим образом:
false
заменяется на0
, аtrue
— на1
.Но это относится только к булевым значениям: в строковых параметрах, даже если они содержат значения
false
илиtrue
, замены на0
или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
Допустим, что надо подписать запрос в Old 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
и добавить его в запрос. Для этого необходимо:
- Убедиться, что в теле запроса нет параметра
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": "<подпись, которую нужно создать>" }
- Преобразовать оставшиеся параметры в строки 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
- Отсортировать полученные строки по алфавиту:
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
- Объединить отсортированные строки в одну строку с использованием в качестве разделителя точки с запятой:
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
- Вычислить HMAC полученной строки с использованием функции хеширования SHA-512 и используемого ключа, после чего кодировать двоичный код HMAC с применением алгоритма Base64:
w6ZW/DPURM2LXYcviD5yDlwe/gV+yBhCZaL0XbfHkoefY3OuIccdDAyECbhl7/GqA8dZ+R6XFwBUzzm94ywjrw==
- Добавить полученную подпись в тело запроса:
{ "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==" }
Форма для тестирования
Проверка данных
Описание алгоритма
В качестве входных данных для проверки целостности выступают:
- Подписанные данные, которые требуется проверить. Как правило, это тело ответа в формате JSON с параметром
signature
в его составе. - Ключ («соль»), используемый для проверки. Это должен быть ровно тот ключ, который был использован для подписывания проверяемых данных.
В качестве выходных данных при проверке целостности в зависимости от реализации алгоритма могут выступать расчётная подпись и информация о её совпадении с проверяемой подписью, то есть информация о целостности проверяемых данных.
Далее в описании, примере и форме для тестирования представлен часто используемый и наиболее показательный вариант реализации алгоритма: с телом проверяемого ответа в формате JSON на входе и с заключением о целостности этого сообщения на выходе.
В состав алгоритма в этом случае включаются следующие шаги:
- Проверка входных данных на соблюдение заданных требований:
- Структура проверяемых данных должна соответствовать формату JSON.
- В составе проверяемых данных должен присутствовать параметр
signature
с подписью. - Должен быть задан проверочный ключ («соль»).
- Извлечение подписи из проверяемых данных. На этом шаге из проверяемых данных исключается параметр
signature
, а его значение фиксируется для последующего сличения с расчётной подписью. - Формирование расчётной подписи для проверяемых данных:
- Преобразование данных в строку UTF-8 с сортировкой параметров по алфавиту. В рамках этого шага выполняются следующие действия:
-
Логические (булевы) значения кодируются следующим образом:
false
заменяется на0
, аtrue
— на1
.Но это относится только к булевым значениям: в строковых параметрах, даже если они содержат значения
false
илиtrue
, замены на0
или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
- Тело полученного ответа выглядит так:
Рис.: Тело ответа
{ "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==" }
Для проверки подписи необходимо:
- Удалить из тела ответа параметр
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==" }
-
Преобразовать оставшиеся параметры в строки 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:
- Отсортировать полученные строки по алфавиту:
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
- Объединить отсортированные строки в одну строку с использованием в качестве разделителя точки с запятой:
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
- Вычислить HMAC полученной строки с использованием функции хеширования SHA-512 и используемого ключа, после чего кодировать двоичный код HMAC с применением алгоритма Base64:
TiDoAPZSQR4Clhg5qj2wLD2tTatx0AUa0erwV++fkOV4kfxy/bBuE5hsnvyk2dzADS3tFC20+czK1dK6YCaJ3g==
- Сравнить полученную подпись с проверяемой.
В данном случае подписи не совпадают, а это значит, что такой ответ недостоверен или ошибочен и должен быть отброшен.
Форма для тестирования