Подписывание и проверка подписи

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

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

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

Подписывание запроса

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

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

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

    Как правило, это все параметры тела запроса за исключением подписи или JavaScript-объект configObj с параметрами без включения параметра signature.

  2. Ключ, используемый для подписывания.

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

В качестве выходных данных подписывания в зависимости от реализации алгоритма могут выступать либо подпись, либо подписанный JavaScript-объект configObj — как правило, это итоговый объект с параметром signature, включенным в его состав.

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

Рис.: Как рассчитать подпись

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

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

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

Пример для запроса на оплату через Payment Page

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

  • Ключ для подписывания — secret
  • Предварительная версия объекта configObj, в котором еще нет значения параметра с подписью, выглядит так:
    {
    "project_id": 12345, 
    "payment_id": "X03936", 
    "payment_amount": 2035, 
    "payment_currency": "USD",
    "payment_description": "Покупка наручных часов",
    "customer_id": "customer_386",
    "customer_first_name": "Иван",
    "customer_last_name": "Иванов",
    "customer_phone": 74991234567,
    "close_on_missclick": true,
    "signature": "<подпись, которую нужно создать>" 
    }

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

  1. Убедитесь, что в теле запроса нет параметра signature, даже с пустым значением. Если такой параметр есть, его нужно удалить:
    {
    "project_id": 12345, 
    "payment_id": "X03936", 
    "payment_amount": 2035, 
    "payment_currency": "USD",
    "payment_description": "Покупка наручных часов",
    "customer_id": "customer_386",
    "customer_first_name": "Иван",
    "customer_last_name": "Иванов",
    "customer_phone": 74991234567,
    "close_on_missclick": true,
    "signature": "<подпись, которую нужно создать>" 
    }
  2. Преобразуйте оставшиеся параметры в строки UTF-8 согласно правилам алгоритма:
    project_id:12345 
    payment_id:X03936
    payment_amount:2035
    payment_currency:USD
    payment_description:Покупка наручных часов
    customer_id:customer_386
    customer_first_name:Иван
    customer_last_name:Иванов
    customer_phone:74991234567
    close_on_missclick:1
  3. Упорядочите полученные строки по алфавиту:
    close_on_missclick:1
    customer_first_name:Иван
    customer_id:customer_173
    customer_last_name:Иванов
    customer_phone:74991234567
    payment_amount:2035
    payment_currency:USD
    payment_description:Покупка наручных часов
    payment_id:X03936
    project_id:12345 
    
  4. Объедините упорядоченные строки в одну строку с использованием в качестве разделителя точки с запятой:
    close_on_missclick:1;customer_first_name:Иван;customer_id:customer_386;customer_last_name:Иванов;customer_phone:74991234567;payment_amount:2035;payment_currency:USD;payment_description:Покупка наручных часов;payment_id:X03936;project_id:12345
  5. Вычислите HMAC полученной строки с использованием функции хеширования SHA-512 и используемого ключа, после чего закодируйте двоичный код HMAC с применением алгоритма Base64:
    flchUC07OPucpr8rTFPBpIVfjDh+Vltvo0Gz1YBg/1lYpa3BY8i+QzOaa5gZznlhaQV4oSyEQvGnCqbU9qoThQ==
  6. Добавьте полученную подпись в объект configObj:
    {
    "project_id": 12345, 
    "payment_id": "X03936", 
    "payment_amount": 2035, 
    "payment_currency": "USD",
    "payment_description": "Покупка наручных часов",
    "customer_id": "customer_386",
    "customer_first_name": "Иван",
    "customer_last_name": "Иванов",
    "customer_phone": 74991234567,
    "close_on_missclick": true,
    "signature": "flchUC07OPucpr8rTFPBpIVfjDh+Vltvo0Gz1YBg/1lYpa3BY8i+QzOaa5gZznlhaQV4oSyEQvGnCqbU9qoThQ=="
    }

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

Проверка подписи оповещений

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

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

  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
  • Тело полученного оповещения выглядит, как показано в примере.

    Рис.: Тело оповещения

    {
      "customer": {
        "id": "1"
      },
      "account": {
        "number": "123456******1234",
        "token": "f0bdb5741032c19cc8cb2bab92adeec44c5ad56614205feb40348ab92adeec4",
        "type": "visa",
        "id": 895819971,
        "card_holder": "JOHN DOE",
        "expiry_month": "12",
        "expiry_year": "2024"
      },
      "project_id": 1124,
      "payment": {
        "id": "PAYMENT_585860",
        "type": "purchase",
        "status": "success",
        "date": "2023-05-26T06:43:19+0000",
        "method": "card",
        "sum": {
          "amount": 50000,
          "currency": "USD"
        },
        "description": "PAYMENT_585860"
      },
      "operation": {
        "sum_initial": {
          "amount": 50000,
          "currency": "USD"
        },
        "sum_converted": {
          "amount": 50000,
          "currency": "USD"
        },
        "code": "0",
        "message": "Success",
        "eci": "02",
        "provider": {
          "id": 13012,
          "payment_id": "16850833995740",
          "auth_code": "563253",
          "endpoint_id": 13012,
          "date": "2023-05-26T03:43:19+0000"
        },
        "id": 5055919010134089,
        "type": "sale",
        "status": "success",
        "date": "2023-05-26T06:43:19+0000",
        "created_date": "2023-05-26T06:43:10+0000",
        "request_id": "123456789"
      },
      "signature": "NtDutuRiksyHeBhhUs+nQxQ1FcMSueoACb4vENju0APgHgeZfRfMj46289v1vD4hJ1a8Yhg=="
    }

Чтобы проверить подпись:

  1. Удалите из тела оповещения параметр signature вместе с его значением.
    {
      "customer": {
        "id": "1"
      },
      "account": {
        "number": "123456******1234",
        "token": "f0bdb5741032c19cc8cb2bab92adeec44c5ad56614205feb40348ab92adeec4",
        "type": "visa",
        "id": 895819971,
        "card_holder": "JOHN DOE",
        "expiry_month": "12",
        "expiry_year": "2024"
      },
      "project_id": 1124,
      "payment": {
        "id": "PAYMENT_585860",
        "type": "purchase",
        "status": "success",
        "date": "2023-05-26T06:43:19+0000",
        "method": "card",
        "sum": {
          "amount": 50000,
          "currency": "USD"
        },
        "description": "PAYMENT_585860"
      },
      "operation": {
        "sum_initial": {
          "amount": 50000,
          "currency": "USD"
        },
        "sum_converted": {
          "amount": 50000,
          "currency": "USD"
        },
        "code": "0",
        "message": "Success",
        "eci": "02",
        "provider": {
          "id": 13012,
          "payment_id": "16850833995740",
          "auth_code": "563253",
          "endpoint_id": 13012,
          "date": "2023-05-26T03:43:19+0000"
        },
        "id": 5055919010134089,
        "type": "sale",
        "status": "success",
        "date": "2023-05-26T06:43:19+0000",
        "created_date": "2023-05-26T06:43:10+0000",
        "request_id": "123456789"
      },
      "signature": "NtDutuRiksyHeBhhUs+nQxQ1FcMSueoACb4vENju0APgHgeZfRfMj46289v1vD4hJ1a8Yhg=="
    }
  2. Преобразуйте оставшиеся параметры в строки UTF-8 согласно алгоритму.
    customer:id:1
    account:number:123456******1234
    account:token:f0bdb5741032c19cc8cb2bab92adeec44c5ad56614205feb40348ab92adeec4
    account:type:visa
    account:id:895819971
    account:card_holder:JOHN DOE
    account:expiry_month:12
    account:expiry_year:2024
    project_id:1124
    payment:id:PAYMENT_585860
    payment:type:purchase
    payment:status:success
    payment:date:2023-05-26T06:43:19+0000
    payment:method:card
    payment:sum:amount:50000
    payment:sum:currency:USD
    payment:description:PAYMENT_585860
    operation:sum_initial:amount:50000
    operation:sum_initial:currency:USD
    operation:sum_converted:amount:50000
    operation:sum_converted:currency:USD
    operation:code:0
    operation:message:Success
    operation:eci:02
    operation:provider:id:13012
    operation:provider:payment_id:16850833995740
    operation:provider:auth_code:563253
    operation:provider:endpoint_id:13012
    operation:provider:date:2023-05-26T03:43:19+0000
    operation:id:5055919010134089
    operation:type:sale
    operation:status:success
    operation:date:2023-05-26T06:43:19+0000
    operation:created_date:2023-05-26T06:43:10+0000
    operation:request_id:123456789
  3. Упорядочите полученные строки по алфавиту.
    account:card_holder:JOHN DOE
    account:expiry_month:12
    account:expiry_year:2024
    account:id:895819971
    account:number:123456******1234
    account:token:f0bdb5741032c19cc8cb2bab92adeec44c5ad56614205feb40348ab92adeec4
    account:type:visa
    customer:id:1
    operation:code:0
    operation:created_date:2023-05-26T06:43:10+0000
    operation:date:2023-05-26T06:43:19+0000
    operation:eci:02
    operation:id:5055919010134089
    operation:message:Success
    operation:provider:auth_code:563253
    operation:provider:date:2023-05-26T03:43:19+0000
    operation:provider:endpoint_id:13012
    operation:provider:id:13012
    operation:provider:payment_id:16850833995740
    operation:request_id:123456789
    operation:status:success
    operation:sum_converted:amount:50000
    operation:sum_converted:currency:USD
    operation:sum_initial:amount:50000
    operation:sum_initial:currency:USD
    operation:type:sale
    payment:date:2023-05-26T06:43:19+0000
    payment:description:PAYMENT_585860
    payment:id:PAYMENT_585860
    payment:method:card
    payment:status:success
    payment:sum:amount:50000
    payment:sum:currency:USD
    payment:type:purchase
    project_id:1124
  4. Объедините упорядоченные строки в одну строку с использованием в качестве разделителя точки с запятой.
    account:card_holder:JOHN DOE;account:expiry_month:12;account:expiry_year:2024;account:id:895819971;account:number:123456******1234;account:token:f0bdb5741032c19cc8cb2bab92adeec44c5ad56614205feb40348ab92adeec4;account:type:visa;customer:id:1;operation:code:0;operation:created_date:2023-05-26T06:43:10+0000;operation:date:2023-05-26T06:43:19+0000;operation:eci:02;operation:id:5055919010134089;operation:message:Success;operation:provider:auth_code:563253;operation:provider:date:2023-05-26T03:43:19+0000;operation:provider:endpoint_id:13012;operation:provider:id:13012;operation:provider:payment_id:16850833995740;operation:request_id:123456789;operation:status:success;operation:sum_converted:amount:50000;operation:sum_converted:currency:USD;operation:sum_initial:amount:50000;operation:sum_initial:currency:USD;operation:type:sale;payment:date:2023-05-26T06:43:19+0000;payment:description:PAYMENT_585860;payment:id:PAYMENT_585860;payment:method:card;payment:status:success;payment:sum:amount:50000;payment:sum:currency:USD;payment:type:purchase;project_id:1124
  5. Вычислите HMAC полученной строки с использованием функции хеширования SHA-512 и ключа подписывания, после чего закодируйте двоичный HMAC-код с применением алгоритма Base64.
    kUJXSM6oRS1kHDxtd6veTg11pKFD2g02BduwDGRIdQskW4yCRD/odf1skZ9tmHGwTJi5k64tv7Og8Yu0/74oTQ==
  6. Сравните полученную подпись с проверяемой.

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

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