Как реализовать авторизацию на сайте через API с использованием электронной подписи КриптоПро в 1С

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

При интеграции 1С с современными веб-сервисами (например, ГИС МТ «Честный ЗНАК», МДЛП или личными кабинетами различных ведомств) разработчики часто сталкиваются с необходимостью программной авторизации по сертификату ЭЦП. Типичный сценарий выглядит так: мы получаем от сервера некую строку данных (Data/Nonce/UUID), подписываем её закрытым ключом сертификата, кодируем результат в Base64 и отправляем обратно для получения токена доступа.

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

Почему стандартный МенеджерКриптографии 1С может не подходить

Основная причина расхождений при подписании данных в 1С заключается в различии стандартов криптографии. Рассмотрим подробнее ситуацию: стандартный метод МенеджерКриптографии.Подписать() по умолчанию формирует подпись в формате CMS (PKCS#7). В то же время большинство современных API требуют формат CAdES-BES или CAdES-T.

Хотя внешне оба формата выглядят как нечитаемая строка Base64, их внутренняя структура (ASN.1 дерево) существенно отличается. CAdES — это усовершенствованная подпись, которая включает в себя дополнительные атрибуты (например, время подписания). Если сервер ожидает CAdES-BES, а получает CMS, авторизация будет отклонена с ошибкой "Unauthorized".

Второй важный момент — присоединенная и отсоединенная подписи. Метод Подписать() в 1С создает присоединенную подпись, в которую упаковываются и исходные данные. Веб-сервисы же чаще всего ждут отсоединенную (detached) подпись, где передается только сам "отпечаток" подписи без исходного текста.

Решение проблемы через COM-объект CAdESCOM

Для корректной работы с алгоритмами КриптоПро в 1С наиболее надежным способом является использование библиотеки CAdESCOM, которая устанавливается вместе с плагином КриптоПро ЭЦП Browser plug-in. Этот метод позволяет гибко управлять типом подписи и её параметрами.

Разберем по шагам процесс подписания данных для API. Сначала нам необходимо инициализировать объекты для работы с сертификатом и самой подписью. Проанализируем следующий пример кода:


// Функция принимает строку данных для подписи и отпечаток сертификата
Функция ПодписатьДанныеДляAPI(ДанныеДляПодписи, ОтпечатокСертификата) Экспорт
    
    // Создаем объект для работы с параметрами подписи
    Попытка
        Signer = Новый COMОбъект("CAdESCOM.CPSigner");
    Исключение
        Сообщить("Не удалось создать объект CAdESCOM.CPSigner. Проверьте установку КриптоПро ЭЦП Browser plug-in.");
        Возврат Неопределено;
    КонецПопытки;
    
    // Ищем сертификат в хранилище по отпечатку
    Сертификат = НайтиСертификатПоОтпечатку(ОтпечатокСертификата);
    Если Сертификат = Неопределено Тогда
        ВызватьИсключение "Сертификат с указанным отпечатком не найден в хранилище 'Личные'!";
    КонецЕсли;
    
    Signer.Certificate = Сертификат;
    // Указываем тип кодирования (0 - CAPICOM_ENCODE_BASE64, 1 - CAPICOM_ENCODE_BINARY)
    // Для большинства API лучше использовать двоичные данные внутри, а результат получать в Base64
    
    // Создаем объект для формирования подписи
    SignedData = Новый COMОбъект("CAdESCOM.CadesSignedData");
    SignedData.Content = ДанныеДляПодписи;
    
    // Параметры метода SignCades:
    // 1. Signer - объект с сертификатом
    // 2. Тип подписи (1 = CADESCOM_CADES_BES)
    // 3. Флаг отсоединенной подписи (Истина = Detached, Ложь = Attached)
    // 4. Тип кодировки результата (0 = Base64)
    
    ТипПодписи = 1; // CAdES-BES
    ОтсоединеннаяПодпись = Истина;
    КодировкаРезультата = 0; // BASE64
    
    Попытка
        РезультатПодписи = SignedData.SignCades(Signer, ТипПодписи, ОтсоединеннаяПодпись, КодировкаРезультата);
        // Важно: убираем лишние переносы строк, которые может добавить COM-объект
        РезультатПодписи = СтрЗаменить(РезультатПодписи, Символы.ПС, "");
        РезультатПодписи = СтрЗаменить(РезультатПодписи, Символы.ВК, "");
        Возврат РезультатПодписи;
    Исключение
        Сообщить("Ошибка при подписании: " + ОписаниеОшибки());
        Возврат Неопределено;
    КонецПопытки;
    
КонецФункции

Поиск сертификата в хранилище

Для работы CAdESCOM необходимо передать ему объект сертификата. Рассмотрим, как программно найти нужный сертификат в хранилище "Личные" (Personal) по его отпечатку (Thumbprint):


Функция НайтиСертификатПоОтпечатку(Отпечаток)
    Store = Новый COMОбъект("CAdESCOM.Store");
    // Открываем хранилище личных сертификатов текущего пользователя
    Store.Open(2, "My", 0); // 2 = CAPICOM_CURRENT_USER_STORE, 0 = CAPICOM_STORE_OPEN_READ_ONLY
    
    // Фильтруем сертификаты по отпечатку
    НайденныеСертификаты = Store.Certificates.Find(0, Отпечаток); // 0 = CAPICOM_CERTIFICATE_FIND_SHA1_HASH
    
    Если НайденныеСертификаты.Count = 0 Тогда
        Store.Close();
        Возврат Неопределено;
    КонецЕсли;
    
    ВыбранныйСертификат = НайденныеСертификаты.Item(1);
    Store.Close();
    
    Возврат ВыбранныйСертификат;
КонецФункции

Важные нюансы кодировки и BOM

Проанализируем ситуацию, когда даже при использовании CAdESCOM подпись не проходит проверку. Часто проблема кроется в подготовке исходных данных. Если API ожидает строку в кодировке UTF-8 без BOM, а вы передаете данные в другом формате, хэш-сумма будет неверной.

Выясним причину: 1С при работе с двоичными данными может добавлять метку BOM (Byte Order Mark). Чтобы этого избежать, при получении двоичных данных из строки следует явно указывать кодировку и проверять, не добавились ли лишние байты в начало массива. Однако при использовании свойства SignedData.Content для простых строк, COM-объект обычно корректно обрабатывает кодировку системы. Если же API требует подписи "сырых" байтов, данные лучше загружать через SignedData.ContentEncoding = 1 (Binary).

Особенности работы на сервере 1С

Рассмотрим важный аспект: где должен выполняться код подписания? Если код выполняется на стороне клиента (в тонком клиенте), то используются сертификаты и лицензия КриптоПро, установленные на рабочем месте пользователя. Это самый простой вариант.

Если же подписание должно происходить на сервере 1С (например, в фоновом задании), необходимо учитывать следующие требования:

  1. На сервере должен быть установлен КриптоПро CSP и иметь серверную лицензию.
  2. Сертификат ЭЦП должен быть установлен в хранилище "Локальный компьютер" (Local Machine), так как серверная служба 1С работает под техническим пользователем, у которого нет доступа к личному хранилищу обычного пользователя.
  3. У пользователя, под которым запущен "Агент сервера 1С", должны быть полные права на ветки реестра КриптоПро и доступ к контейнеру закрытого ключа.

Использование утилиты cryptcp как альтернатива

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

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


// Пример формирования команды для консольной утилиты
Команда = "cryptcp -sign -detached -der -thumbprint " + Отпечаток + " " + ПутьКФайлуДанных + " " + ПутьКФайлуПодписи;
ЗапуститьПриложение(Команда,, Истина); // Ожидаем завершения

После этого остается только прочитать ПутьКФайлуПодписи, перевести двоичные данные в Base64 и отправить в теле POST-запроса. Помните, что при передаче подписи в JSON-запросе, она не должна содержать символов переноса строки, поэтому всегда используйте СтрЗаменить для очистки результирующей строки.

Таким образом, для успешной авторизации через API с использованием КриптоПро в 1С следует отказаться от стандартного МенеджерКриптографии в пользу CAdESCOM или cryptcp, обеспечивая формирование подписи в формате CAdES-BES (detached), и внимательно следить за отсутствием лишних символов в итоговой Base64 строке.

← На главную