Разберем довольно специфичную, но все чаще встречающуюся задачу при интеграции 1С со сторонними веб-сервисами и API (поможет инструмент логирования и анализа HTTP-запросов к API): формирование криптографической подписи для строковых данных (например, токенов авторизации) по стандарту PKCS#7 с последующим преобразованием результата в формат Base64URL.
Часто в спецификациях к API можно встретить требование: "подпись должна быть в формате PKCS#7, включать полную цепочку сертификатов x.509 пользователя, а итоговая строка должна быть закодирована в Base64URL". Давайте проанализируем, как это реализовать стандартными средствами платформы 1С:Предприятие, какие подводные камни нас ожидают и почему стандартный Base64 не всегда подходит для передачи в URL.
Прежде чем писать код, давайте выясним суть происходящих процессов. В обсуждениях часто возникает путаница между аббревиатурами PKCS#7 и CMS.
PKCS#7 — это классический стандарт синтаксиса криптографических сообщений, разработанный RSA Laboratories. CMS (Cryptographic Message Syntax) — это его современная эволюция, стандартизированная IETF. Когда мы используем МенеджерКриптографии в 1С, "под капотом" формируется пакет именно по спецификации CMS, который обладает обратной совместимостью с системами, ожидающими PKCS#7.
Важно понимать, что в отличие от легковесных подписей (например, в JWS/JWT), структура CMS/PKCS#7 — это тяжеловесный контейнер. Он содержит:
Именно из-за этого "багажа" итоговая подпись получается весьма длинной.
В процессе работы также часто возникает вопрос: зачем хешировать короткую строку перед подписью? Асимметричная криптография (алгоритмы RSA или ГОСТ) работает с блоками данных фиксированного размера. Поэтому любой криптопровайдер всегда сначала вычисляет хеш (например, SHA-256 или ГОСТ Р 34.11-2012) от исходных данных, а затем подписывает именно этот хеш. Мы не можем повлиять на этот процесс — это фундаментальное свойство криптографии.
Рассмотрим подробнее реализацию алгоритма. Для решения задачи нам потребуется встроенный объект платформы МенеджерКриптографии. Также нам необходимо учесть требование по включению x.509 сертификата в тело подписи. При необходимости подписывать готовые файлы на платформе можно применить обработку для подписи произвольных файлов сертификатом ЭЦП.
Шаг 1. Подготовка данных. Криптографические методы платформы работают с двоичными данными, а не со строками. Поэтому исходную строку нужно конвертировать.
Шаг 2. Настройка криптографии. Нам нужно указать менеджеру, что в итоговый пакет нужно вложить сертификат.
Шаг 3. Подписание и конвертация. Подписываем данные и переводим их в текст формата Base64.
Посмотрим на пример кода, реализующий эти шаги:
// Исходная строка для подписи (например, payload токена)
ИсходнаяСтрока = "test_payload_data";
// Шаг 1: Конвертация строки в двоичные данные
ДвоичныеДанныеСтроки = ПолучитьДвоичныеДанныеИзСтроки(ИсходнаяСтрока, КодировкаТекста.UTF8);
// Инициализация менеджера криптографии (укажите ваш криптопровайдер)
КриптоМенеджер = Новый МенеджерКриптографии("", "", 75);
// Шаг 2: Настройка включения сертификата в подпись
// Из требования "включая x.509 сертификат" следует установка этого свойства
КриптоМенеджер.ВключениеСертификатовВПодпись = ВключениеСертификатовКриптографии.ВключатьПолнуюЦепочку;
// Определяем, будет ли подпись присоединенной или отсоединенной.
// Для токенов чаще всего требуется отсоединенная (Ложь), когда подписываемые данные не дублируются в контейнере подписи
ПрисоединеннаяПодпись = Ложь;
Попытка
// Шаг 3: Формирование подписи. СертификатКриптографии получаем заранее из хранилища.
ПодписанныеДанные = КриптоМенеджер.Подписать(ДвоичныеДанныеСтроки, СертификатКриптографии, ПрисоединеннаяПодпись);
// Перевод двоичных данных подписи в строку Base64
СтрокаBase64 = Base64Строка(ПодписанныеДанные);
Исключение
Сообщить("Ошибка при подписании: " + ОписаниеОшибки());
КонецПопытки;
После получения строки Строка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С:Предприятие 8.3.21), стоит обратить внимание, что в платформе появились встроенные механизмы для работы с токенами (объекты для JWT). Платформа научилась нативно работать с форматом Base64URL.
Если ваша глобальная цель — это создание JWT-токена, где в качестве подписи используется сертификат, рекомендуется изучить новые объекты платформы. Это позволит избежать низкоуровневых манипуляций со строками, заменами символов и ручного формирования криптографических пакетов, делегировав эту работу ядру 1С. Однако для специфичных задач, где жестко требуется "голый" PKCS#7 пакет в URL-формате, рассмотренный нами ручной алгоритм остается наиболее надежным решением.