Как сформировать ЭЦП с помощью МенеджераКриптографии и CADESCOM, и почему "Честный ЗНАК" может не принимать подпись?

Программист 1С v8.3 (Управляемые формы) Торговля и дистрибуция
← К списку

Приветствуем вас! Сегодня мы вместе разберемся с одной из частых и весьма запутанных задач в 1С – формированием электронной подписи (ЭЦП), особенно когда речь идет о взаимодействии с внешними государственными системами, такими как "Честный ЗНАК". Мы рассмотрим как стандартные механизмы 1С с использованием объекта МенеджерКриптографии, так и альтернативные решения на базе COM-объекта CADESCOM, а также выясним, почему иногда сформированная подпись может не приниматься.

Решение 1: Использование объекта МенеджерКриптографии

Давайте начнем с основного инструмента платформы 1С для работы с криптографией — объекта МенеджерКриптографии. Нам предстоит проанализировать его возможности и типичные ошибки при использовании.

Механизм криптографии в "1С:Предприятии" не содержит собственных реализаций криптографических алгоритмов. Вместо этого он предоставляет набор объектов для взаимодействия с внешними криптографическими модулями сторонних производителей, такими как Microsoft CryptoAPI в Windows или КриптоПРО CSP в Linux и macOS. При формировании подписи методом Подписать() объекта МенеджерКриптографии сначала вычисляется хеш-сумма подписываемых данных, а затем подписывается полученная хеш-сумма. Этот подход призван сократить время выполнения операции.

Важный момент: для корректной работы рекомендуется явно указывать алгоритмы хеширования (АлгоритмХеширования) и подписи (АлгоритмПодписи), если они не устанавливаются автоматически или требуются специфические стандарты.

Особенности метода Подписать()

Рассмотрим подробнее метод Подписать(). Мы выясним, что его синтаксис может быть неочевиден, особенно в части получения результата. Изначально многие разработчики сталкиваются с тем, что ожидают получить подпись как возвращаемое значение метода, но на самом деле она может передаваться через выходной параметр.

Типичные попытки и их корректировка:

  1. Некоторые пытаются получить подпись напрямую из возвращаемого значения, передавая в качестве второго параметра имя выходного файла:

    
    Попытка
        Результат = МенеджерКриптографии.Подписать(ИмяВремФайла, ИмВых, СертификатЭЦП, ТипПодп);
        Подп = Base64Строка(Новый ДвоичныеДанные(ИмВых));
    Исключение
        ТекстОшибки = ОписаниеОшибки();
    КонецПопытки;
    

    Этот подход предполагает, что подпись будет сохранена в файл ИмВых, а затем прочитана оттуда. Однако, если требуется получить данные подписи напрямую в память, этот способ не всегда оптимален.

  2. Более распространенный вариант, который мы часто видим в обсуждениях, это передача Неопределено в качестве второго параметра, ожидая, что метод вернет двоичные данные подписи:

    
    Попытка
        Подп = Base64Строка(МенеджерКриптографии.Подписать(ИмяВремФайла, Неопределено, СертификатЭЦП, ТипПодп));
    Исключение
        ТекстОшибки = ОписаниеОшибки();
    КонецПопытки;
    

    Этот синтаксис может успешно отрабатывать, но, как показывает практика, не всегда приводит к формированию подписи в нужном формате для внешних систем, например, "Честного ЗНАКа". Мы сталкиваемся с тем, что такая подпись может быть отклонена с ошибкой 403, что указывает на проблемы с доступом или форматом.

  3. Корректный подход к получению двоичных данных подписи:

    Мы выяснили, что метод Подписать() часто использует второй параметр как выходной параметр, передаваемый по ссылке, для возврата двоичных данных подписи. Таким образом, следует объявить переменную, передать ее в метод, и после вызова получить из нее результат:

    
    ПодписьДвоичныеДанные = Неопределено;
    Результат = МенеджерКриптографии.Подписать(ИмяВремФайла, ПодписьДвоичныеДанные, СертификатЭЦП, ТипПодп);
    Подп = Base64Строка(ПодписьДвоичныеДанные);
    

    Здесь ПодписьДвоичныеДанные после вызова метода будет содержать двоичные данные сформированной подписи, которые затем мы можем преобразовать в строку Base64.

  4. Подпись двоичных данных напрямую:

    Мы также можем передавать в метод Подписать() не имя файла, а непосредственно двоичные данные для подписи. Это может быть полезно, если данные уже находятся в памяти или если мы хотим избежать операций с временными файлами.

    
    ВходящиеДвоичныеДанные = ПолучитьДвоичныеДанныеИзСтроки(СтрокаДляПодписи); // Пример получения двоичных данных
    Подп = Base64Строка(МенеджерКриптографии.Подписать(ВходящиеДвоичныеДанные, Неопределено, СертификатЭЦП, ТипПодп));
    

    Обратите внимание, что здесь мы также используем Неопределено для второго параметра, поскольку в данном контексте предполагается, что метод возвращает двоичные данные. Важно убедиться, что кодировка исходной строки для подписи (например, UTF-8 без BOM) соответствует ожиданиям системы.

Форматы подписи и параметр ТипПодп

Метод Подписать() объекта МенеджерКриптографии может формировать подписи в различных форматах стандарта CAdES. Одним из самых распространенных форматов является CMS, на основе которого построена целая группа форматов CAdES, таких как CAdES-BES (по умолчанию), CAdES-T, CAdES-C и другие.

Ключевое свойство: ВключениеДанныхВПодпись.

Проблемы и возможные причины:

Проблемы с МенеджерКриптографии.Подписать() могут возникать из-за некорректной связи сертификата с закрытым ключом, неверных настроек алгоритмов подписи или хеширования, или некорректной установки программы электронной подписи (CSP). Иногда установка нескольких программ электронной подписи на одном компьютере может вызвать ошибки. Если внешняя система ожидает присоединенную подпись, а МенеджерКриптографии по умолчанию создает отсоединенную (или наоборот), мы можем столкнуться с ошибками валидации.

Решение 2: Использование COM-объекта CADESCOM для интеграции с государственными системами (например, "Честный ЗНАК")

В случаях, когда стандартный МенеджерКриптографии не обеспечивает требуемый формат или функциональность, особенно при взаимодействии с государственными информационными системами (ГИС), такими как "Честный ЗНАК", мы часто обращаемся к альтернативным решениям. Одним из таких мощных инструментов является COM-объект CADESCOM.

Почему CADESCOM?

"Честный ЗНАК" требует усиленную квалифицированную электронную подпись (УКЭП) и может быть очень чувствителен к формату подписи. Ошибки типа "Подпись не прошла проверку в crypto" или 403 при запросе токена могут указывать на то, что сервис ожидает подпись в строго определенном формате (например, CAdES-BES в Base64) или с определенными атрибутами (например, штамп времени).

CADESCOM позволяет формировать подписи с различными параметрами, включая прикрепленную и открепленную, и добавлять атрибуты, такие как штамп времени, что часто является критичным для ГИС.

Реализация подписи через CADESCOM

Мы предлагаем рассмотреть подробный пример реализации функций подписи с использованием CADESCOM, адаптированный из документации True API ЦРПТ.

Важно: Для работы с CADESCOM необходимо, чтобы на клиентском компьютере был установлен соответствующий криптопровайдер (например, КриптоПро CSP) и плагин для работы с электронной подписью.

Шаг 1: Получение сертификата из системного хранилища.

Мы помним, что CADESCOM ожидает COM-объект сертификата, а не объект СертификатКриптографии 1С. Поэтому нам нужно получить сертификат непосредственно из системного хранилища по его отпечатку.


//Отпечаток - строка HEX
Функция ПолучитьСертификатПоОтпечатку(ОтпечатокСтр)
    Отпечаток = СтрЗаменить(ОтпечатокСтр, " ", "");	
    Рез 	= Неопределено; // Найденный сертификат (Com-объект)
    CAPICOM_CURRENT_USER_STORE = 2; // 2 - Искать сертификат в ветке "Личное" хранилища.
    CAPICOM_MY_STORE = "My"; // Указываем, что ветку "Личное" берем из хранилища текущего пользователя
    CAPICOM_STORE_OPEN_READ_ONLY = 0; // Открыть хранилище только на чтение
    oStore 	= Новый COMОбъект("CAdESCOM.Store");
    oStore.Open(CAPICOM_CURRENT_USER_STORE, CAPICOM_MY_STORE, CAPICOM_STORE_OPEN_READ_ONLY); // Открыть хранилище сертификатов
    Certs 	= oStore.Certificates;
    Для СчСер = 1 По Certs.Count Цикл
        ТекСертификат = Certs.Item (СчСер); 
        ТекОтпечаток  = ТекСертификат.Thumbprint; // возвращается отпечаток в шестнадцатеричном виде
        Если ВРЕГ(ТекОтпечаток) = ВРЕГ(Отпечаток) Тогда
            Рез = ТекСертификат;
            Прервать;
        КонецЕсли;
    КонецЦикла;
    oStore.Close(); // Закрыть хранилище сертификатов и освободить объект
    Возврат Рез;
КонецФункции

В этой функции мы открываем системное хранилище сертификатов текущего пользователя (`CAPICOM_CURRENT_USER_STORE`, "Личное") и ищем сертификат по его отпечатку. Найденный сертификат возвращается как COM-объект.

Шаг 2: Функция подписи текста.

Теперь мы можем использовать полученный COM-объект сертификата для формирования подписи.


// sThumbprint - отпечаток сертификата, используемого для подписи; строка,
// представляющая отпечаток в шестнадцатеричном виде
// пример 195934d72dcdf69149901d6632aca4562d8806d8
// bDetached - Истина/Ложь - откреплённая(для подписания документов)/прикреплённая(для получения токена авторизации) подпись
Функция ПодписатьТекстЦРПТ(ТекстДляПодписи, sThumbprint, bDetached)
	ВходящиеДвоичныеДанные = Base64Строка(ПолучитьДвоичныеДанныеИзСтроки(ТекстДляПодписи));
	CADESCOM_BASE64_TO_BINARY = 1; // Входные данные пришли в Base64
	CADESCOM_CADES_TYPE = 1; // Тип усовершенствованной подписи
	CAPICOM_AUTHENTICATED_ATTRIBUTE_SIGNING_TIME = 0; // Атрибут штампа времени подписи
	oSigner = Новый COMОбъект("CAdESCOM.CPSigner");
	oSigner.Certificate = ПолучитьСертификатПоОтпечатку(sThumbprint);
	oSigningTimeAttr = Новый COMОбъект("CAdESCOM.CPAttribute");
	oSigningTimeAttr.Name = CAPICOM_AUTHENTICATED_ATTRIBUTE_SIGNING_TIME;
	oSigningTimeAttr.Value = ТекущаяДата();
	oSigner.AuthenticatedAttributes2.Add(oSigningTimeAttr);
	ТекстДляПодписи = СокрЛП(ТекстДляПодписи);
	oSignedData = Новый COMОбъект("CAdESCOM.CadesSignedData");
	oSignedData.ContentEncoding = CADESCOM_BASE64_TO_BINARY;
	oSignedData.Content = ВходящиеДвоичныеДанные;
	EncodingType = 0;
	sSignedMessage = oSignedData.SignCades(oSigner, CADESCOM_CADES_TYPE, bDetached, EncodingType);
	Возврат sSignedMessage; // Подпись в формате Base64
КонецФункции

Здесь мы создаем объекты CAdESCOM.CPSigner и CAdESCOM.CadesSignedData. Мы устанавливаем сертификат для подписи, добавляем атрибут штампа времени (что часто требуется для юридически значимых подписей) и указываем тип подписи (прикрепленная или открепленная) с помощью параметра bDetached. Обратите внимание, что входные данные для подписи сначала преобразуются в Base64, а затем передаются в oSignedData.Content.

Шаг 3: Вызов функции подписи с клиента.

Для вызова этих функций из клиентского кода 1С, например, из формы, мы можем использовать следующую структуру:


&НаКлиенте
Функция ПодписатьСтроку(СтрокаДляПодписи, СертификатЭЦП) Экспорт // Здесь СертификатЭЦП - сертификат полученный в 1С через МенеджерКриптографии
  	Отпечаток = СертификатЭЦП.Отпечаток;	
	Подп = ПодписатьТекстЦРПТ(СтрокаДляПодписи, Отпечаток, Ложь); // третий параметр прикрепленная. Если нужно открепленная - то Истина
	Возврат Подп;
КонецФункции

В этом примере мы передаем объект СертификатЭЦП, полученный стандартными средствами 1С (например, через МенеджерКриптографии), чтобы извлечь из него отпечаток. Затем этот отпечаток используется функцией ПодписатьТекстЦРПТ для получения COM-объекта сертификата и выполнения подписи. Параметр Ложь означает прикрепленную подпись, которая часто используется для получения токена авторизации в ГИС.

Дополнительные аспекты и рекомендации

В процессе работы с ЭЦП мы сталкиваемся с рядом нюансов, которые важно учитывать:

  1. Кодировки: Убедитесь, что строка для подписи имеет правильную кодировку (например, UTF-8 без BOM), иначе хеш-сумма может быть рассчитана некорректно, и подпись не пройдет проверку.
  2. Типы подписей: Для юридически значимого документооборота и взаимодействия с госорганами, включая "Честный ЗНАК", обычно требуется усиленная квалифицированная электронная подпись (УКЭП).
  3. Машиночитаемые доверенности (МЧД): С 1 сентября 2024 года сертификаты КЭП юрлица или ИП, выданные в коммерческих удостоверяющих центрах, будут недействительны для сотрудников. Для работы с маркированными товарами сотрудникам может понадобиться МЧД.
  4. Проблемы с установкой: Проблемы могут возникать из-за некорректной установки криптопровайдера, плагина для браузера (для CADESCOM) или несоответствия версий компонентов.
  5. Отладка: Если подпись не принимается, мы рекомендуем тщательно проверять логи внешней системы (если доступны) и использовать тестовые стенды для отладки. Иногда ошибки могут быть неочевидными и связаны с очень специфическими требованиями к формату или составу подписи.

Мы надеемся, что это подробное руководство поможет вам успешно справиться с задачей формирования электронной подписи в 1С и ее интеграцией с внешними системами. Вместе мы проанализировали различные подходы и выяснили ключевые моменты для успешной реализации.

← К списку