Как программно подписать входящий документ в Диадок через HTTP API в 1С?

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

Интеграция с сервисом электронного документооборота (ЭДО) Диадок через HTTP API — задача, требующая глубокого понимания принципов криптографии и структуры протокола Контура. Одной из наиболее сложных подзадач является подписание входящих документов (есть готовый модуль юридически значимого ЭДО для 1С), когда необходимо не просто отправить файл, а сформировать корректный патч сообщения с валидной электронной подписью (ЭП). В рамках данной статьи мы подробно разберем, как реализовать этот процесс на платформе 1С:Предприятие 8.3, используя универсальные методы организации API, начиная от авторизации по сертификату и заканчивая отправкой подписи на сервер.

Шаг 1. Авторизация по сертификату и получение токена

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

Сначала мы обращаемся к методу V3/Authenticate, передавая в теле запроса открытый ключ сертификата (файл .cer). В ответ сервер присылает данные, зашифрованные на этом сертификате. Нам необходимо расшифровать их локально, используя закрытый ключ.

Важный нюанс: расшифрованные данные перед отправкой в метод подтверждения (AuthenticateConfirm) должны быть закодированы в Base64, а затем обязательно обработаны функцией URL-кодирования. Правильная обработка параметров критична, когда реализуется надежная выгрузка и механизмы получения токенов доступа для интеграции с внешними системами. Если этого не сделать, символы "+" или "/" в строке Base64 будут интерпретированы сервером некорректно, что приведет к ошибке 400 или 403.

Проанализируем пример функции для получения токена:


Функция ПолучитьТокенПоСертификату(КлючРазработчика, ОтпечатокСертификата) Экспорт
    
    // 1. Получаем объект сертификата из хранилища 1С
    СертификатКриптографии = НайтиСертификатПоОтпечатку(ОтпечатокСертификата);
    HTTPСоединение = УстановитьHTTPСоединениеССерверомДиадок();
    
    // 2. Запрос на первичную аутентификацию
    ЗапросHTTP = Новый HTTPЗапрос("V3/Authenticate?type=certificate");
    ЗапросHTTP.Заголовки.Вставить("Authorization", "DiadocAuth ddauth_api_client_id=" + КлючРазработчика);
    ЗапросHTTP.Заголовки.Вставить("Content-type", "application/octet-stream");
    
    // Передаем бинарное содержимое сертификата
    ЗапросHTTP.УстановитьТелоИзДвоичныхДанных(СертификатКриптографии.Выгрузить());
    
    Ответ = HTTPСоединение.ОтправитьДляОбработки(ЗапросHTTP);
    
    Если Ответ.КодСостояния = 200 Тогда
        ЗашифрованныеДанные = Ответ.ПолучитьТелоКакДвоичныеДанные();
        
        // 3. Расшифровка полученных данных
        МенеджерКриптографии = Новый МенеджерКриптографии("", "", 80); // 80 - тип провайдера для ГОСТ 2012
        МенеджерКриптографии.ПарольДоступаКЗакрытомуКлючу = "ВашПароль";
        РасшифрованныеДанные = МенеджерКриптографии.Расшифровать(ЗашифрованныеДанные);
        
        // 4. Подготовка данных для подтверждения
        // Важно: убираем переносы строк из Base64 и кодируем в URL
        СтрокаBase64 = Base64Строка(РасшифрованныеДанные);
        СтрокаBase64 = СтрЗаменить(СтрокаBase64, Символы.ВК, "");
        СтрокаBase64 = СтрЗаменить(СтрокаBase64, Символы.ПС, "");
        
        РасшифрованныеДанныеURL = КодироватьСтрокуВURL(СтрокаBase64);
        
        // 5. Финальное подтверждение (AuthenticateConfirm)
        РесурсConfirm = СтрШаблон("V2/AuthenticateConfirm?token=%1", РасшифрованныеДанныеURL);
        ЗапросConfirm = Новый HTTPЗапрос(РесурсConfirm);
        ЗапросConfirm.Заголовки.Вставить("Authorization", "DiadocAuth ddauth_api_client_id=" + КлючРазработчика);
        
        ОтветConfirm = HTTPСоединение.ОтправитьДляОбработки(ЗапросConfirm);
        Возврат ОтветConfirm.ПолучитьТелоКакСтроку(); // Это и есть наш токен
    КонецЕсли;
    
КонецФункции

Шаг 2. Формирование валидной электронной подписи (CMS SignedData)

Для подписания входящего документа в Диадоке используется метод PostMessagePatch — для этой задачи есть автоматизация получения и архивации документов из Диадок через API. Одной из частых проблем является ошибка "Invalid signature". Выясним причину: Диадок ожидает подпись в формате CMS SignedData в DER-кодировке. Более того, подпись должна быть отсоединенной (detached), то есть не содержать в себе исходные данные документа.

Хотя подпись файла методами криптографии возможна с помощью встроенного объекта МенеджерКриптографии, иногда его работа с определенными версиями КриптоПро (например, 4.0) приводит к формированию структуры, которую сервер Диадока считает невалидной. В таких случаях, чтобы обеспечить корректную работу с криптографией в 1С и шифрованием, рекомендуется использовать COM-объект библиотеки CAdESCOM.

Рассмотрим по шагам, как программно создать отсоединенную подпись через CAdESCOM:


Функция СформироватьОтсоединеннуюПодпись(ДвоичныеДанныеДокумента, ОтпечатокСертификата)
    
    // Инициализируем работу с хранилищем сертификатов через COM
    Store = Новый COMОбъект("CAdESCOM.Store");
    Store.Open(2); // 2 = CAPICOM_CURRENT_USER_STORE
    
    // Ищем нужный сертификат по отпечатку
    FoundCert = Неопределено;
    Для Каждого ТекущийСертификат Из Store.Certificates Цикл
        Если ВРег(СтрЗаменить(ТекущийСертификат.Thumbprint, " ", "")) = ВРег(ОтпечатокСертификата) Тогда
            FoundCert = ТекущийСертификат;
            Прервать;
        КонецЕсли;
    КонецЦикла;
    
    Если FoundCert = Неопределено Тогда
        ВызватьИсключение "Сертификат не найден в личном хранилище пользователя.";
    КонецЕсли;

    // Настраиваем объект подписания
    Signer = Новый COMОбъект("CAdESCOM.CPSigner");
    Signer.Certificate = FoundCert;
    Signer.CheckCertificate = Истина;

    // Создаем объект для формирования данных подписи
    SignedData = Новый COMОбъект("CAdESCOM.CadesSignedData");
    // Устанавливаем исходные данные, которые подписываем
    SignedData.Content = Base64Строка(ДвоичныеДанныеДокумента);
    
    // Формируем подпись
    // 1 - CADESCOM_CADES_BES (базовая подпись)
    // Истина - Признак отсоединенной подписи (Detached)
    // 0 - Кодировка возвращаемого значения (0 = CADESCOM_ENCODE_ANY, фактически вернет Base64)
    РезультатПодписи = SignedData.SignCades(Signer, 1, Истина);
    
    Возврат РезультатПодписи;
КонецФункции

Шаг 3. Отправка патча сообщения (PostMessagePatch)

После того как мы получили токен и сформировали строку подписи, нам необходимо отправить эти данные на сервер. Для этого формируется JSON-структура MessagePatchToPost. Проанализируем ключевые поля этой структуры:

  1. BoxId: Идентификатор вашего ящика в Диадоке.
  2. MessageId: Идентификатор сообщения, в котором пришел документ.
  3. Signatures: Массив структур, где указывается ParentEntityId (ID самого документа внутри сообщения) и Signature (наша подпись в Base64).

Рассмотрим пример формирования тела запроса:


Процедура ОтправитьПодписьВДиадок(Токен, КлючРазработчика, MessageId, EntityId, ТекстПодписиBase64)
    
    HTTPСоединение = УстановитьHTTPСоединениеССерверомДиадок();
    ЗапросHTTP = Новый HTTPЗапрос("V3/PostMessagePatch");
    
    // Формируем заголовки авторизации
    AuthHeader = СтрШаблон("DiadocAuth ddauth_api_client_id=%1, ddauth_token=%2", КлючРазработчика, Токен);
    ЗапросHTTP.Заголовки.Вставить("Authorization", AuthHeader);
    ЗапросHTTP.Заголовки.Вставить("Content-type", "application/json; charset=utf-8");
    
    // Собираем структуру патча
    Патч = Новый Структура;
    Патч.Вставить("BoxId", "Ваш_BoxId");
    Патч.Вставить("MessageId", MessageId);
    
    ПодписьСтруктура = Новый Структура;
    ПодписьСтруктура.Вставить("ParentEntityId", EntityId);
    ПодписьСтруктура.Вставить("Signature", ТекстПодписиBase64);
    
    МассивПодписей = Новый Массив;
    МассивПодписей.Добавить(ПодписьСтруктура);
    Патч.Вставить("Signatures", МассивПодписей);
    
    // Сериализация в JSON
    ЗаписьJSON = Новый ЗаписьJSON;
    ЗаписьJSON.УстановитьСтроку();
    ЗаписатьJSON(ЗаписьJSON, Патч);
    ТелоЗапроса = ЗаписьJSON.Закрыть();
    
    ЗапросHTTP.УстановитьТелоИзСтроки(ТелоЗапроса);
    Ответ = HTTPСоединение.ОтправитьДляОбработки(ЗапросHTTP);
    
    Если Ответ.КодСостояния = 200 Тогда
        Сообщить("Документ успешно подписан!");
    Иначе
        Сообщить("Ошибка при подписании: " + Ответ.ПолучитьТелоКакСтроку());
    КонецЕсли;
    
КонецПроцедуры

Особенности работы с неформализованными документами

При работе с неформализованными документами (PDF, Excel, Word) важно учитывать статус документооборота. Если вы являетесь отправителем и хотите, чтобы контрагент обязательно подписал документ, при первичной отправке через метод PostMessage необходимо установить флаг NeedRecipientSignature = true в структуре вложения DocumentAttachment.

Если этот флаг не установлен, Диадок может перевести документ в статус "Документооборот завершен" сразу после получения его контрагентом, даже если тот не поставил ответную подпись. Это критично для юридически значимых договоров и соглашений.

Рекомендация по версии КриптоПро: на практике замечено, что использование КриптоПро CSP 5.0 снимает ряд проблем с совместимостью алгоритмов подписи (ГОСТ 2001/2012) при взаимодействии с API. Если ваша задача подразумевает более сложные механизмы, такие как подпись RSA с хешированием SHA512 или реализация на мобильных платформах, правильный выбор криптопровайдера становится решающим фактором успеха.

Таким образом, для успешного подписания входящих документов в Диадок через HTTP API необходимо обеспечить корректную URL-кодировку токена, использовать библиотеку CAdESCOM для создания отсоединенной CMS-подписи и правильно соотнести MessageId и EntityId в JSON-запросе к серверу.

← На главную