При интеграции систем 1С с государственными информационными ресурсами и внешними торговыми площадками разработчики часто сталкиваются с жестким требованием контрагентов: передавать данные, подписанные строго в формате усовершенствованной электронной подписи CAdES-BES (открепленная или присоединенная подпись). С такой необходимостью мы регулярно сталкиваемся при работе с маркировкой «Честный Знак» (ГИС МТ), электронными путевыми листами (ГИС ЭПД), ЕИС Закупки и другими государственными порталами.
Исторически в платформе «1С:Предприятие» встроенные механизмы работы с криптографией поддерживали только классический формат PKCS#7 (CMS). Однако сегодня эту задачу можно решить несколькими надежными способами. В этой статье мы подробно разберем, как реализовать формирование подписи CAdES-BES в зависимости от версии вашей платформы 1С, рассмотрим особенности работы на стороне сервера и приведем готовые примеры программного кода.
Если в вашей организации используется современная версия платформы «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-соединений.
Если обновить платформу 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, подпись сформируется некорректно, и принимающая сторона вернет ошибку проверки целостности данных.
Если использование COM-объектов в коде вызывает затруднения или код должен выполняться в среде, где COM-технологии работают нестабильно, можно воспользоваться консольной утилитой cryptcp, поставляемой вместе с ПО КриптоПро.
Этот метод основан на сохранении строки во временный файл, вызове утилиты через метод 1С ЗапуститьПриложение() и последующем считывании сформированного файла подписи.
Разберем данный алгоритм по шагам:
cryptcp.exe..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С (1C:Enterprise 8.3 Server Agent) по умолчанию выполняется под системной учетной записью (USRNBF8, NetworkService или LocalSystem) в так называемом неинтерактивном сеансе (сеансе №0). Когда криптопровайдер КриптоПро CSP обращается к закрытому ключу сертификата, операционная система блокирует это действие, поскольку КриптоПро пытается вывести диалоговое окно для ввода пароля к контейнеру или выбора носителя, что физически невозможно в фоновом режиме работы сервера.
Для успешного подписания документов на сервере выполните следующие шаги:
.\USR1CV8). Установите личный сертификат электронной подписи и все промежуточные сертификаты УЦ именно под этой учетной записью.Применяя данные подходы, вы сможете успешно интегрировать механизмы подписания CAdES-BES в любые бизнес-процессы вашей конфигурации 1С, обеспечив полную совместимость со всеми современными требованиями государственных органов и информационных систем.