Как подписать строковые данные в формате CAdES-BES в 1С:Предприятие?

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

При интеграции систем 1С с государственными информационными ресурсами и внешними торговыми площадками разработчики часто сталкиваются с жестким требованием контрагентов: передавать данные, подписанные строго в формате усовершенствованной электронной подписи CAdES-BES (открепленная или присоединенная подпись). С такой необходимостью мы регулярно сталкиваемся при работе с маркировкой «Честный Знак» (ГИС МТ), электронными путевыми листами (ГИС ЭПД), ЕИС Закупки и другими государственными порталами.

Исторически в платформе «1С:Предприятие» встроенные механизмы работы с криптографией поддерживали только классический формат PKCS#7 (CMS). Однако сегодня эту задачу можно решить несколькими надежными способами. В этой статье мы подробно разберем, как реализовать формирование подписи CAdES-BES в зависимости от версии вашей платформы 1С, рассмотрим особенности работы на стороне сервера и приведем готовые примеры программного кода.

Решение 1. Использование встроенной поддержки CAdES-BES (для платформ 1С 8.3.20 и выше)

Если в вашей организации используется современная версия платформы «1С:Предприятие» (начиная с релиза 8.3.20), вам больше не требуются сторонние внешние компоненты, OLE/COM-объекты или вызовы командной строки. Фирма 1С реализовала нативную поддержку усовершенствованной подписи непосредственно на уровне платформы.

Для реализации этого метода мы будем использовать стандартный объект МенеджерКриптографии. В метод Подписать() был добавлен специальный параметр, позволяющий явно указать требуемый формат подписи.

Рассмотрим пример кода для подписания произвольной строки в формате CAdES-BES:


&НаСервере
Функция ПодписатьСтрокуНативно(ИсходнаяСтрока, СертификатСубъекта) Экспорт
    
    // Переводим исходную строку в двоичные данные
    ДанныеДляПодписи = ПолучитьДвоичныеДанныеИзСтроки(ИсходнаяСтрока, КодировкаТекста.UTF8);
    
    // Инициализируем менеджер криптографии
    // Параметры инициализации зависят от используемого криптопровайдера (например, КриптоПро CSP)
    ИмяПровайдера = "Crypto-Pro GOST R 34.10-2012 Cryptographic Service Provider";
    ТипПровайдера = 80; // Для ГОСТ 2012
    
    Менеджер = Новый МенеджерКриптографии(ИмяПровайдера, "", ТипПровайдера);
    
    // Находим нужный сертификат в хранилище "Личные" текущего пользователя
    Хранилище = Менеджер.ПолучитьХранилищеСертификатов("My", "CurrentUser");
    Сертификаты = Хранилище.НайтиПоСубъекту(СертификатСубъекта);
    
    Если Сертификаты.Количество() = 0 Тогда
        ВызватьИсключение "Сертификат для подписания не найден в хранилище пользователя!";
    КонецЕсли;
    
    СертификатПодписанта = Сертификаты[0];
    
    // Формируем подпись формата CAdES-BES
    // ТипПодписи.CAdES_BES — указывает платформе использовать нужный формат
    // Ложь — указывает, что мы создаем открепленную подпись (Detached)
    ДвоичныеДанныеПодписи = Менеджер.Подписать(ДанныеДляПодписи, СертификатПодписанта, Ложь, ТипЭлектроннойПодписи.CAdES_BES);
    
    // Преобразуем полученную подпись в формат Base64 для передачи клиенту
    СтрокаПодписиBase64 = ПолучитьСтрокуBase64ИзДвоичныхДанных(ДвоичныеДанныеПодписи);
    
    Возврат СтрокаПодписиBase64;

КонецФункции

Этот способ является наиболее предпочтительным, кроссплатформенным и производительным, так как он работает как под управлением Windows, так и на операционных системах семейства Linux без необходимости настройки COM-соединений.

Решение 2. Использование OLE-объекта CAdESCOM (для старых версий платформ)

Если обновить платформу 1С до версии 8.3.20 не представляется возможным, мы можем использовать библиотеки от компании КриптоПро. При установке КриптоПро CSP в операционную систему Windows регистрируются специальные COM-объекты семейства CAdESCOM. Они спроектированы по аналогии со старой библиотекой Microsoft CAPICOM и предоставляют полный доступ к функциям создания усовершенствованной подписи.

При работе с CAdESCOM критически важно соблюдать правильный порядок установки свойств. Проанализируем пример кода, который формирует открепленную подпись CAdES-BES для строковых данных:


&НаКлиенте
Функция ПодписатьСтрокуЧерезCadesCOM(ИсходнаяСтрока, ОтпечатокСертификата) Экспорт
    
    Попытка
        // Создаем объект для работы с хранилищем сертификатов
        Store = Новый COMОбъект("CAdESCOM.Store");
        // Открываем личное хранилище текущего пользователя
        Store.Open(2, "My", 0); // 2 = CAPICOM_CURRENT_USER_STORE, 0 = CAPICOM_STORE_OPEN_READ_ONLY
    Исключение
        Сообщить("Не удалось инициализировать хранилище сертификатов: " + ОписаниеОшибки());
        Возврат Неопределено;
    КонецПопытки;
    
    // Ищем нужный сертификат по его отпечатку (SHA-1 хэшу)
    Certificates = Store.Certificates.Find(0, ОтпечатокСертификата); // 0 = CAPICOM_CERTIFICATE_FIND_SHA1_HASH
    Если Certificates.Count = 0 Тогда
        Сообщить("Указанный сертификат не найден в личном хранилище пользователя!");
        Store.Close();
        Возврат Неопределено;
    КонецЕсли;
    
    SignerCert = Certificates.Item(1);
    Store.Close();
    
    Попытка
        // Создаем объект подписанта
        Signer = Новый COMОбъект("CAdESCOM.CPSigner");
        Signer.Certificate = SignerCert;
        // Рекомендуется принудительно добавлять время подписания
        Signer.Options = 1; // CAPICOM_CERTIFICATE_INCLUDE_END_ENTITY_ONLY
        
        // Создаем объект для работы с подписью
        SignedData = Новый COMОбъект("CAdESCOM.CadesSignedData");
        
        // ВАЖНО: Если мы подписываем произвольную строку, лучше перевести ее в Base64, 
        // чтобы избежать искажения кодировки при передаче бинарных данных.
        ДанныеВBase64 = ПолучитьСтрокуBase64ИзДвоичныхДанных(ПолучитьДвоичныеДанныеИзСтроки(ИсходнаяСтрока, КодировкаТекста.UTF8));
        
        // Установка кодировки должна происходить СТРОГО ДО передачи самих данных в свойство Content!
        SignedData.ContentEncoding = 1; // CADESCOM_BASE64_TO_BINARY
        SignedData.Content = ДанныеВBase64;
        
        // Формируем открепленную подпись (True - открепленная, False - присоединенная)
        // 1 = CADESCOM_CADES_BES
        СтрокаПодписи = SignedData.SignCades(Signer, 1, Истина);
        
        Возврат СтрокаПодписи;
        
    Исключение
        Сообщить("Ошибка при формировании подписи CAdES-BES: " + ОписаниеОшибки());
        Возврат Неопределено;
    КонецПопытки;

КонецФункции

Обратите внимание на важный нюанс: Свойство ContentEncoding со значением 1 указывает объекту, что строка в свойстве Content передается в формате Base64 и ее нужно декодировать в бинарный вид перед расчетом подписи. Если вы нарушите последовательность и присвоите Content раньше, чем укажете ContentEncoding, подпись сформируется некорректно, и принимающая сторона вернет ошибку проверки целостности данных.

Решение 3. Использование утилиты командной строки cryptcp

Если использование COM-объектов в коде вызывает затруднения или код должен выполняться в среде, где COM-технологии работают нестабильно, можно воспользоваться консольной утилитой cryptcp, поставляемой вместе с ПО КриптоПро.

Этот метод основан на сохранении строки во временный файл, вызове утилиты через метод 1С ЗапуститьПриложение() и последующем считывании сформированного файла подписи.

Разберем данный алгоритм по шагам:

  1. Записываем исходную строку во временный файл в кодировке UTF-8 или UTF-8 без BOM.
  2. Формируем параметры командной строки для запуска утилиты cryptcp.exe.
  3. Запускаем приложение в режиме ожидания завершения.
  4. Считываем получившийся файл подписи (обычно с расширением .sgn) и удаляем временные файлы.

Пример программного кода:


&НаСервере
Функция ПодписатьСтрокуЧерезCryptCP(ИсходнаяСтрока, ОтпечаткомСертификата)
    
    ИмяВременногоФайлаДанных = ПолучитьИмяВременногоФайла("txt");
    ИмяВременногоФайлаПодписи = ИмяВременногоФайлаДанных + ".sgn";
    
    // Записываем данные
    ТекстовыйДокумент = Новый ТекстовыйДокумент;
    ТекстовыйДокумент.УстановитьТекст(ИсходнаяСтрока);
    ТекстовыйДокумент.Записать(ИмяВременногоФайлаДанных, КодировкаТекста.UTF8);
    
    // Путь к установленной утилите cryptcp (может отличаться в зависимости от разрядности ОС)
    ПутьКПрограмме = """C:\Program Files\Crypto Pro\CSP\cryptcp.exe""";
    
    // Формируем ключи запуска:
    // -signf : подписать файл
    // -dir : директория для сохранения результата
    // -thumbprint : отпечаток сертификата
    // -der : сформировать подпись в кодировке DER (бинарная) или без этого ключа для Base64
    // -detached : создать открепленную подпись
    // -pin : пин-код контейнера (если требуется)
    ШаблонКоманды = "%1 -signf -dir ""%2"" -thumbprint %3 -detached -nochain ""%4""";
    
    СтрокаЗапуска = СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(ШаблонКоманды, 
        ПутьКПрограмме, 
        КаталогВременныхФайлов(), 
        ОтпечаткомСертификата, 
        ИмяВременногоФайлаДанных);
        
    // Запускаем консольное приложение в скрытом режиме и ожидаем завершения
    КодВозврата = Неопределено;
    ЗапуститьПриложение(СтрокаЗапуска, , Истина, КодВозврата);
    
    Если КодВозврата = 0 Тогда
        // Считываем двоичные данные подписи
        ДвоичныеДанныеПодписи = Новый ДвоичныеДанные(ИмяВременногоФайлаПодписи);
        РезультатBase64 = ПолучитьСтрокуBase64ИзДвоичныхДанных(ДвоичныеДанныеПодписи);
        
        // Заметаем следы (удаляем файлы)
        УдалитьФайлы(ИмяВременногоФайлаДанных);
        УдалитьФайлы(ИмяВременногоФайлаПодписи);
        
        Возврат РезультатBase64;
    Иначе
        ВызватьИсключение "Ошибка выполнения cryptcp. Код возврата: " + Строка(КодВозврата);
    КонецЕсли;

КонецФункции

Важно: Решение проблем при работе на сервере 1С (Автоматический контекст)

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

«Поставщику не удалось выполнить действие, так как запрошено выполнение в автоматическом контексте»

Выясним причину этой ошибки: Служба сервера 1С (1C:Enterprise 8.3 Server Agent) по умолчанию выполняется под системной учетной записью (USRNBF8, NetworkService или LocalSystem) в так называемом неинтерактивном сеансе (сеансе №0). Когда криптопровайдер КриптоПро CSP обращается к закрытому ключу сертификата, операционная система блокирует это действие, поскольку КриптоПро пытается вывести диалоговое окно для ввода пароля к контейнеру или выбора носителя, что физически невозможно в фоновом режиме работы сервера.

Для успешного подписания документов на сервере выполните следующие шаги:

  1. Перенесите сертификаты в профиль пользователя службы 1С. Служба сервера 1С должна запускаться не от имени системной учетной записи, а от имени специально созданного доменного или локального пользователя (например, .\USR1CV8). Установите личный сертификат электронной подписи и все промежуточные сертификаты УЦ именно под этой учетной записью.
  2. Настройте сохранение PIN-кода. Контейнер закрытого ключа должен быть настроен без пароля, либо пароль (PIN-код) должен быть жестко сохранен в системе через оснастку «КриптоПро CSP» (свойство «Запомнить пароль» при тестировании контейнера под пользователем службы 1С).
  3. Используйте считыватель «Реестр». Если закрытый ключ хранится на физическом USB-токене, серверная служба не всегда сможет получить к нему доступ. Оптимальное решение для сервера — скопировать контейнер закрытого ключа в локальный реестр сервера Windows под учетной записью пользователя службы 1С.

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

← На главную