Как корректно подписать файл ЭЦП в 1С 7.7 через CAPICOM и VBScript/JScript?

Программист 1С v7.7 IT и автоматизация бизнеса
← На главную

При формировании и подписании алкогольных деклараций или других юридически значимых документов непосредственно из 1С 7.7 разработчики часто сталкиваются с ошибками формата. Распространенная ситуация: алгоритм подписания через объект CAPICOM.SignedData отрабатывает без ошибок, однако итоговый файл становится нечитаемым — превращается в набор бинарных данных, теряет структуру XML или начинает содержать лишние управляющие символы в начале файла, из-за чего сторонние программы (например, КриптоАРМ) и контролирующие органы (ФСРАР) выдают ошибку формата.

В этой статье мы подробно проанализируем причины возникновения данной проблемы в 1С версии 7.7 и разберем по шагам проверенные способы ее решения с примерами кода.

Выясним причину: почему портятся данные при подписании

Проблема кроется в особенностях работы платформы 1С 7.7 с двоичными данными и объектами COM/OLE:

  1. Отсутствие встроенного типа для двоичных данных в 1С 7.7: В отличие от 1С 8.x, где есть объект ДвоичныеДанные, платформа 1С 7.7 передает данные между OLE-объектами исключительно в виде текстовых строк Unicode (тип BSTR). Бинарные «хвосты» электронной подписи при такой передаче могут искажаться.
  2. Проблема BOM-маркера в ADODB.Stream: Когда в скрипте подпись записывается в файл через поток ADODB.Stream с типом Type = 2 (текстовый поток) с использованием метода WriteText(), в начало файла автоматически помещается так называемый BOM-маркер (Byte Order Mark — байты 0xEF 0xBB 0xBF для UTF-8 или 0xFF 0xFE для UTF-16). В результате файл перестает распознаваться как валидный DER/XML и выдает ошибку формата.
  3. Ограничение метода Write(): Если установить для ADODB.Stream бинарный режим (Type = 1) и попытаться передать результат функции SignedData.Sign() напрямую в метод Write(), возникает ошибка «Аргументы имеют неверный тип», так как метод Write() ожидает массив байт (VT_ARRAY | VT_UI1), а не BSTR-строку.

Способ 1. Использование объекта CAPICOM.Utilities (Рекомендуемый)

Для корректного сохранения подписанного файла без искажений необходимо преобразовать BSTR-строку, возвращаемую методом SignedData.Sign(), в массив байт. Для этого в библиотеке CAPICOM предусмотрен специальный вспомогательный объект CAPICOM.Utilities и его метод BinaryStringToByteArray().

Разберем рабочий пример функции на 1С 7.7, которая использует компонент MSScriptControl.ScriptControl для запуска VBScript-кода:


Функция ПодписатьФайл(ИмяФайла, ВыбСертификат, ИмяВыхФайла)
    FileName    = СокрЛП(ИмяФайла);
    OutFileName = СокрЛП(ИмяВыхФайла);
    Cert        = ВыбСертификат;
    
    ScrptCtrl = СоздатьОбъект("MSScriptControl.ScriptControl");
    ScrptCtrl.Language = "vbscript";
    
    code = "
    |Function Signer(FileName, OutFileName, Cert)
    |   Set FileIn = CreateObject(""ADODB.Stream"")
    |   FileIn.Type = 1 // бинарное чтение
    |   FileIn.Mode = 3
    |   FileIn.Open() 
    |   FileIn.LoadFromFile(FileName)
    |   BinaryIn = FileIn.Read(-1)
    |   
    |   Set Signer = CreateObject(""CAPICOM.Signer"")
    |   Signer.Certificate = Cert
    |   Signer.Options = 2 // CAPICOM_CERTIFICATE_INCLUDE_END_ENTITY_ONLY
    |   
    |   Set SignedData = CreateObject(""CAPICOM.SignedData"")
    |   SignedData.Content = BinaryIn
    |   
    |   // Получаем подпись (второй параметр False - прикрепленная подпись)
    |   SignData = SignedData.Sign(Signer, False, 1)
    |   
    |   // Преобразуем строку в байтовый массив для сохранения без BOM
    |   Set Util = CreateObject(""CAPICOM.Utilities"")
    |   BinArr = Util.BinaryStringToByteArray(SignData)
    |   
    |   Set FileOut = CreateObject(""ADODB.Stream"")
    |   FileOut.Type = 1 // бинарная запись
    |   FileOut.Mode = 3
    |   FileOut.Open()
    |   FileOut.Write(BinArr)
    |   FileOut.SaveToFile OutFileName, 2
    |   FileOut.Close()
    |   FileIn.Close()
    |End Function
    |";
      
    ScrptCtrl.AddCode(code);
    Попытка
        Рез = ScrptCtrl.Modules("Global").CodeObject.Signer(FileName, OutFileName, Cert);
    Исключение
        ТекстОшибки = ОписаниеОшибки();
        Сообщить("Ошибка при подписании файла: " + ТекстОшибки, "!");        
        Возврат 0;
    КонецПопытки; 
    
    Возврат 1;
КонецФункции

Пояснения к коду:

  1. Мы считываем исходный XML-файл через ADODB.Stream с параметром Type = 1 в бинарную переменную BinaryIn.
  2. Формируем объект CAPICOM.SignedData, помещая исходные данные в свойство Content.
  3. Вызываем метод BinaryStringToByteArray из объекта CAPICOM.Utilities, который превращает строку с подписью в настоящий массив байтов.
  4. Записываем массив байтов в выходной бинарный поток FileOut.Type = 1 методом Write(). В этом случае никаких посторонних BOM-символов в начале файла не появляется.

Способ 2. Программная обрезка BOM-маркера из выходного файла

Если по каким-то причинам преобразование через BinaryStringToByteArray недоступно, можно записать текстовый поток с подписью на диск, а затем сдвинуть позицию считывания на 2 байта вперед с помощью ADODB.Stream, создав копию файла без байтового маркера кодировки.

Разберем функцию удаления BOM-символов:


Процедура УдалитьBOM(ИмяФайла)
    ScrptCtrl = СоздатьОбъект("MSScriptControl.ScriptControl");
    ScrptCtrl.Language = "vbscript";
    
    code = "
    |Sub DeleteBom(FileName)
    |   Set reader = CreateObject(""Adodb.Stream"")
    |   reader.Type = 1
    |   reader.Open
    |   reader.LoadFromFile FileName
    |   
    |   Set writer = CreateObject(""Adodb.Stream"")
    |   writer.Mode = 3
    |   writer.Type = 1
    |   writer.Open
    |   
    |   // Пропускаем первые 2 байта (BOM-маркер)
    |   reader.Position = 2
    |   reader.CopyTo writer, -1 
    |   reader.Close
    |   
    |   writer.SaveToFile FileName, 2
    |   writer.Close
    |End Sub
    |";
    
    ScrptCtrl.AddCode(code);
    Попытка
        ScrptCtrl.Modules("Global").CodeObject.DeleteBom(ИмяФайла);
    Исключение
        Сообщить("Ошибка удаления BOM: " + ОписаниеОшибки(), "!");
    КонецПопытки;
КонецПроцедуры

Альтернативные и наиболее надежные решения для 1С 7.7

Стоит учитывать, что библиотека CAPICOM является устаревшей: ее поддержка прекращена Microsoft, она доступна только в 32-разрядном исполнении и не поддерживается на современных 64-битных операционных системах без специальных условий запуска. Кроме того, CAPICOM не поддерживает новые стандарты подписи по ГОСТ Р 34.10-2012.

Если вы разрабатываете решение для 1С 7.7 с заделом на будущее, рекомендуем рассмотреть альтернативные подходы:

  1. Использование консольной утилиты CryptCP от КриптоПро:

    Вместо работы с COM-объектами в 1С 7.7 достаточно выгрузить XML-файл на диск и вызвать утилиту cryptcp.exe с помощью команды КомандаСистемы() или ЗапуститьПриложение(). Этот подход работает стабильно, не зависит от разрядности ОС и проблем с кодировками.

    Пример команды подписания:

    cryptcp -sign -der -dn "CN=ИмяОрганизации" input.xml output.xml.sig
  2. Использование библиотеки CAdESCOM:

    Для работы с актуальными ГОСТ-сертификатами и современными ОС Cryptopro предлагает компонент CAdESCOM.CadesSignedData (КриптоПро ЭЦП Browser plug-in), который пришел на смену CAPICOM и поддерживает создание подписей формата CAdES-BES/CAdES-X Long Item.

← На главную