Как подписать строку по стандарту PKCS#7 в кодировке Base64URL с сертификатом x.509 в 1С?

Программист 1С v8.3 (Управляемые формы) IT и автоматизация бизнеса
← На главную

Разберем довольно специфичную, но все чаще встречающуюся задачу при интеграции 1С со сторонними веб-сервисами и API (поможет инструмент логирования и анализа HTTP-запросов к API): формирование криптографической подписи для строковых данных (например, токенов авторизации) по стандарту PKCS#7 с последующим преобразованием результата в формат Base64URL.

Часто в спецификациях к API можно встретить требование: "подпись должна быть в формате PKCS#7, включать полную цепочку сертификатов x.509 пользователя, а итоговая строка должна быть закодирована в Base64URL". Давайте проанализируем, как это реализовать стандартными средствами платформы 1С:Предприятие, какие подводные камни нас ожидают и почему стандартный Base64 не всегда подходит для передачи в URL.

Теоретическая база: PKCS#7, CMS и процесс подписания

Прежде чем писать код, давайте выясним суть происходящих процессов. В обсуждениях часто возникает путаница между аббревиатурами PKCS#7 и CMS.

PKCS#7 — это классический стандарт синтаксиса криптографических сообщений, разработанный RSA Laboratories. CMS (Cryptographic Message Syntax) — это его современная эволюция, стандартизированная IETF. Когда мы используем МенеджерКриптографии в 1С, "под капотом" формируется пакет именно по спецификации CMS, который обладает обратной совместимостью с системами, ожидающими PKCS#7.

Важно понимать, что в отличие от легковесных подписей (например, в JWS/JWT), структура CMS/PKCS#7 — это тяжеловесный контейнер. Он содержит:

  1. Сложную структуру ASN.1.
  2. Идентификаторы используемых алгоритмов хеширования и шифрования.
  3. Сам открытый сертификат (если указано требование его включить).
  4. Криптографическую подпись (зашифрованный закрытым ключом хеш).

Именно из-за этого "багажа" итоговая подпись получается весьма длинной.

В процессе работы также часто возникает вопрос: зачем хешировать короткую строку перед подписью? Асимметричная криптография (алгоритмы RSA или ГОСТ) работает с блоками данных фиксированного размера. Поэтому любой криптопровайдер всегда сначала вычисляет хеш (например, SHA-256 или ГОСТ Р 34.11-2012) от исходных данных, а затем подписывает именно этот хеш. Мы не можем повлиять на этот процесс — это фундаментальное свойство криптографии.

Пошаговое решение задачи в 1С

Рассмотрим подробнее реализацию алгоритма. Для решения задачи нам потребуется встроенный объект платформы МенеджерКриптографии. Также нам необходимо учесть требование по включению x.509 сертификата в тело подписи. При необходимости подписывать готовые файлы на платформе можно применить обработку для подписи произвольных файлов сертификатом ЭЦП.

Шаг 1. Подготовка данных. Криптографические методы платформы работают с двоичными данными, а не со строками. Поэтому исходную строку нужно конвертировать.

Шаг 2. Настройка криптографии. Нам нужно указать менеджеру, что в итоговый пакет нужно вложить сертификат.

Шаг 3. Подписание и конвертация. Подписываем данные и переводим их в текст формата Base64.

Посмотрим на пример кода, реализующий эти шаги:


// Исходная строка для подписи (например, payload токена)
ИсходнаяСтрока = "test_payload_data";

// Шаг 1: Конвертация строки в двоичные данные
ДвоичныеДанныеСтроки = ПолучитьДвоичныеДанныеИзСтроки(ИсходнаяСтрока, КодировкаТекста.UTF8);

// Инициализация менеджера криптографии (укажите ваш криптопровайдер)
КриптоМенеджер = Новый МенеджерКриптографии("", "", 75);

// Шаг 2: Настройка включения сертификата в подпись
// Из требования "включая x.509 сертификат" следует установка этого свойства
КриптоМенеджер.ВключениеСертификатовВПодпись = ВключениеСертификатовКриптографии.ВключатьПолнуюЦепочку;

// Определяем, будет ли подпись присоединенной или отсоединенной.
// Для токенов чаще всего требуется отсоединенная (Ложь), когда подписываемые данные не дублируются в контейнере подписи
ПрисоединеннаяПодпись = Ложь;

Попытка
    // Шаг 3: Формирование подписи. СертификатКриптографии получаем заранее из хранилища.
    ПодписанныеДанные = КриптоМенеджер.Подписать(ДвоичныеДанныеСтроки, СертификатКриптографии, ПрисоединеннаяПодпись);
    
    // Перевод двоичных данных подписи в строку Base64
    СтрокаBase64 = Base64Строка(ПодписанныеДанные);
Исключение
    Сообщить("Ошибка при подписании: " + ОписаниеОшибки());
КонецПопытки;

Разбор кодировки: Base64 против Base64URL

После получения строки СтрокаBase64 мы сталкиваемся с главной проблемой. Классический Base64 содержит символы +, / и =, а также переносы строк. Символы + и / имеют специальное назначение в URL (пробел и разделитель пути соответственно), поэтому передавать стандартный Base64 в HTTP-запросах небезопасно.

Проанализируем решение, которое часто применяется на практике, но содержит техническую ошибку.

Вариант 1 (Суррогатный Base64URL / URL-Encoding). В некоторых случаях разработчики просто применяют URL-кодирование (Percent-encoding) к символам Base64. Именно этот метод сработал у автора оригинальной задачи:


// ОШИБОЧНЫЙ, но иногда работающий подход (URL-кодирование вместо Base64URL)
ТекстДляURL = СтрЗаменить(СтрокаBase64, Символы.ПС, "");
ТекстДляURL = СтрЗаменить(ТекстДляURL, Символы.ВК, "");
ТекстДляURL = СтрЗаменить(ТекстДляURL, "=", "%3d");
ТекстДляURL = СтрЗаменить(ТекстДляURL, "+", "%2b");
ТекстДляURL = СтрЗаменить(ТекстДляURL, "/", "%2f");

Почему это решение могло сработать? Потому что принимающий сервер перед валидацией токена сначала применил операцию URL-декодирования и получил валидный классический Base64. Однако, строго говоря, по спецификации RFC 4648 это НЕ является форматом Base64URL.

Вариант 2 (Истинный Base64URL по стандарту RFC 4648). Настоящий стандарт Base64URL требует замены символов на безопасные для URL (минус и подчеркивание), а также полного удаления (а не кодирования) символов выравнивания = в конце строки. Если API строго требует спецификацию JWT/Base64URL, первый вариант вызовет ошибку валидации.

Разберем правильную реализацию конвертации по стандарту:


// ПРАВИЛЬНЫЙ подход (стандарт RFC 4648 для Base64URL)
ИтоговыйBase64URL = СтрЗаменить(СтрокаBase64, Символы.ПС, "");
ИтоговыйBase64URL = СтрЗаменить(ИтоговыйBase64URL, Символы.ВК, "");

// Заменяем "+" на "-"
ИтоговыйBase64URL = СтрЗаменить(ИтоговыйBase64URL, "+", "-");

// Заменяем "/" на "_"
ИтоговыйBase64URL = СтрЗаменить(ИтоговыйBase64URL, "/", "_");

// Удаляем символы паддинга "=" в конце строки (их может быть 0, 1 или 2)
Пока Прав(ИтоговыйBase64URL, 1) = "=" Цикл
    ИтоговыйBase64URL = Лев(ИтоговыйBase64URL, СтрДлина(ИтоговыйBase64URL) - 1);
КонецЦикла;

Используя этот алгоритм, мы получаем полностью валидную, безопасную для передачи в URL строку подписи CMS, содержащую сертификат пользователя.

Современный подход в новых версиях платформы 1С

Если вы работаете на современных версиях платформы (начиная с 1С:Предприятие 8.3.21), стоит обратить внимание, что в платформе появились встроенные механизмы для работы с токенами (объекты для JWT). Платформа научилась нативно работать с форматом Base64URL.

Если ваша глобальная цель — это создание JWT-токена, где в качестве подписи используется сертификат, рекомендуется изучить новые объекты платформы. Это позволит избежать низкоуровневых манипуляций со строками, заменами символов и ручного формирования криптографических пакетов, делегировав эту работу ядру 1С. Однако для специфичных задач, где жестко требуется "голый" PKCS#7 пакет в URL-формате, рассмотренный нами ручной алгоритм остается наиболее надежным решением.

← На главную