При формировании и подписании алкогольных деклараций или других юридически значимых документов непосредственно из 1С 7.7 разработчики часто сталкиваются с ошибками формата. Распространенная ситуация: алгоритм подписания через объект CAPICOM.SignedData отрабатывает без ошибок, однако итоговый файл становится нечитаемым — превращается в набор бинарных данных, теряет структуру XML или начинает содержать лишние управляющие символы в начале файла, из-за чего сторонние программы (например, КриптоАРМ) и контролирующие органы (ФСРАР) выдают ошибку формата.
В этой статье мы подробно проанализируем причины возникновения данной проблемы в 1С версии 7.7 и разберем по шагам проверенные способы ее решения с примерами кода.
Проблема кроется в особенностях работы платформы 1С 7.7 с двоичными данными и объектами COM/OLE:
ДвоичныеДанные, платформа 1С 7.7 передает данные между OLE-объектами исключительно в виде текстовых строк Unicode (тип BSTR). Бинарные «хвосты» электронной подписи при такой передаче могут искажаться.ADODB.Stream с типом Type = 2 (текстовый поток) с использованием метода WriteText(), в начало файла автоматически помещается так называемый BOM-маркер (Byte Order Mark — байты 0xEF 0xBB 0xBF для UTF-8 или 0xFF 0xFE для UTF-16). В результате файл перестает распознаваться как валидный DER/XML и выдает ошибку формата.ADODB.Stream бинарный режим (Type = 1) и попытаться передать результат функции SignedData.Sign() напрямую в метод Write(), возникает ошибка «Аргументы имеют неверный тип», так как метод Write() ожидает массив байт (VT_ARRAY | VT_UI1), а не BSTR-строку.Для корректного сохранения подписанного файла без искажений необходимо преобразовать 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;
КонецФункции
Пояснения к коду:
ADODB.Stream с параметром Type = 1 в бинарную переменную BinaryIn.CAPICOM.SignedData, помещая исходные данные в свойство Content.BinaryStringToByteArray из объекта CAPICOM.Utilities, который превращает строку с подписью в настоящий массив байтов.FileOut.Type = 1 методом Write(). В этом случае никаких посторонних 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: " + ОписаниеОшибки(), "!");
КонецПопытки;
КонецПроцедуры
Стоит учитывать, что библиотека CAPICOM является устаревшей: ее поддержка прекращена Microsoft, она доступна только в 32-разрядном исполнении и не поддерживается на современных 64-битных операционных системах без специальных условий запуска. Кроме того, CAPICOM не поддерживает новые стандарты подписи по ГОСТ Р 34.10-2012.
Если вы разрабатываете решение для 1С 7.7 с заделом на будущее, рекомендуем рассмотреть альтернативные подходы:
Вместо работы с COM-объектами в 1С 7.7 достаточно выгрузить XML-файл на диск и вызвать утилиту cryptcp.exe с помощью команды КомандаСистемы() или ЗапуститьПриложение(). Этот подход работает стабильно, не зависит от разрядности ОС и проблем с кодировками.
Пример команды подписания:
cryptcp -sign -der -dn "CN=ИмяОрганизации" input.xml output.xml.sigДля работы с актуальными ГОСТ-сертификатами и современными ОС Cryptopro предлагает компонент CAdESCOM.CadesSignedData (КриптоПро ЭЦП Browser plug-in), который пришел на смену CAPICOM и поддерживает создание подписей формата CAdES-BES/CAdES-X Long Item.