При интеграции 1С с внешними системами, сайтами или государственными порталами часто возникает задача формирования цифровой подписи (ЭЦП) или расчета хеш-суммы (например, SHA1) для передаваемых XML-файлов. При попытке перенести логику обмена из стороннего приложения на C# в 1С разработчики сталкиваются с проблемой: метод хеширования в 1С выдает совершенно другой результат по сравнению с C# кодом, использующим класс XmlDsigC14NTransform. Разберем по шагам, почему это происходит, и рассмотрим несколько надежных способов получить идентичный хеш в среде 1С:Предприятие.
Простое чтение XML-файла как текста или двоичных данных и последующая передача в функцию хеширования берет байты "как есть". Однако, XML — это структурированный формат. Один и тот же с точки зрения логики документ может быть записан по-разному: с переносами строк и без, с разным порядком атрибутов в тегах, с использованием одинарных или двойных кавычек.
Класс XmlDsigC14NTransform в .NET перед получением хеша выполняет процесс "нормализации" (канонизации) структуры XML в соответствии со стандартами консорциума W3C. Что именно происходит при канонизации:
<?xml version="1.0" encoding="UTF-8"?>).Именно поэтому, если просто подать текст XML в объект 1С ХешированиеДанных, результат никогда не совпадет с тем, что выдает transform.GetDigestedOutput(new SHA1Managed()) на стороне C#. Чтобы получить нужный результат, нам необходимо сначала канонизировать XML. Рассмотрим три основных решения этой задачи.
Начиная с версий платформы 1С:Предприятие 8.3.9, в системе реализован встроенный объект ПреобразованиеККаноническомуXML, который является прямым и полноценным аналогом класса XmlDsigC14NTransform. Использование встроенных объектов всегда предпочтительнее, так как избавляет от необходимости регистрировать внешние компоненты и решать проблемы совместимости версий ОС.
Проанализируем алгоритм работы в 1С. Нам потребуется прочитать исходный XML, построить из него объектную модель DOM (Document Object Model), выполнить канонизацию и вычислить хеш-сумму.
Посмотрим на пример кода, который реализует эту логику:
// 1. Инициализируем чтение нашего исходного XML из файла или строки
Чтение = Новый ЧтениеXML;
Чтение.ОткрытьФайл("C:\temp\source.xml"); // или УстановитьСтроку(ТекстXML)
// 2. Строим DOM-модель документа
Построитель = Новый ПостроительDOM;
ДокументDOM = Построитель.Прочитать(Чтение);
Чтение.Закрыть();
// 3. Создаем объект для канонизации
Канон = Новый ПреобразованиеККаноническомуXML();
// 4. Получаем нормализованную строку.
// Важно выбрать правильный тип канонизации из системного перечисления
СтрокаКанон = Канон.КанонизироватьВСтроку(ДокументDOM, ТипКаноническогоXML.ИсключающийКаноническийXML);
// 5. Вычисляем хеш-сумму (например, алгоритм SHA1)
Хеширование = Новый ХешированиеДанных(ХешФункция.SHA1);
Хеширование.Добавить(СтрокаКанон);
// Получаем результат в виде строки
ХешСумма = Хеширование.ХешСумма;
Сообщить(СтрЗаменить(Строка(ХешСумма), " ", "")); // Убираем пробелы, если нужно
Важный нюанс: В методе КанонизироватьВСтроку вторым параметром передается значение перечисления ТипКаноническогоXML. Доступны различные варианты: КаноническийXML, КаноническийXMLСКомментариями, ИсключающийКаноническийXML и т.д. Чтобы результат полностью совпал с C# кодом new XmlDsigC14NTransform(false), необходимо поэкспериментировать с типами канонизации в зависимости от того, какой именно стандарт W3C ожидает принимающая сторона. Обычно используется ИсключающийКаноническийXML.
Если штатные средства по каким-то причинам не подходят или вам необходимо перенести сложный комплексный код шифрования целиком, можно создать собственную DLL-библиотеку (COM-сервер) на языке C# и вызывать её из 1С. Рассмотрим подробнее, как это реализовать.
Для начала создадим проект библиотеки классов (Class Library) в Visual Studio. Важно: если сервер приложений 1С работает на устаревших ОС, таких как Windows Server 2008 R2, не используйте современные платформы .NET 5 или .NET Core, так как они там не зарегистрируются. Используйте .NET Framework 4.6 или выше.
В менеджере пакетов NuGet необходимо добавить ссылку на пакет System.Security.Cryptography.Xml. Разберем рабочий код COM-объекта:
using System;
using System.Runtime.InteropServices;
using System.Xml;
using System.IO;
using System.Security.Cryptography;
using System.Security.Cryptography.Xml;
namespace COMServerDES
{
// Определяем интерфейс
[ComVisible(true)]
[Guid("4C23DF7D-0013-0001-1962-7929382A5D32")]
[InterfaceType(ComInterfaceType.InterfaceIsDual)]
public interface IServer
{
string SHA1(string content);
}
// Реализуем класс
[ComVisible(true)]
[Guid("5553D96D-0013-0001-1962-E3D8FCE913A1")]
[ProgId("COMServerDES.Server")]
public class Server : IServer
{
[ComVisible(true)]
[DispId(1)]
public string SHA1(string content)
{
try
{
// Загружаем XML
XmlDocument xmlDoc = new XmlDocument();
xmlDoc.LoadXml(content);
// Инициализируем трансформер
XmlDsigC14NTransform transform = new XmlDsigC14NTransform(false);
transform.LoadInput(xmlDoc);
// Получаем хеш и конвертируем его в строку
return BitConverter.ToString(transform.GetDigestedOutput(new SHA1Managed())).Replace("-", string.Empty);
}
catch (Exception e)
{
return "Ошибка: " + e.Message;
}
}
}
}
После компиляции библиотеку необходимо зарегистрировать в операционной системе с помощью утилиты RegAsm.exe (обязательно с ключом /codebase или предварительно поместив сборку в GAC), запустив командную строку от имени Администратора. Затем в коде 1С вызов будет выглядеть предельно просто:
КомСервер = Новый COMОбъект("COMServerDES.Server");
ХешСтрока = КомСервер.SHA1(ТекстИсходногоXML);
Сообщить(ХешСтрока);
Существует компромиссный вариант: не писать собственный COM-объект на C#, но использовать оригинальные классы .NET прямо из кода 1С. Для этого можно применить популярную универсальную внешнюю компоненту (например, известную в сообществе 1С как NetObjectToIDispatch45).
Эта компонента выступает в роли "моста" между COM-средой 1С и платформой .NET Framework. Разберем по шагам, как повторить логику C# прямо в конфигураторе 1С с ее помощью:
// 1. Создаем объект-обертку (при условии, что компонента зарегистрирована)
Врап = Новый COMОбъект("NetObjectToIDispatch45");
// 2. Загружаем необходимую сборку, содержащую классы для криптографии XML
Врап.ЗагрузитьСборку("System.Security.Cryptography.Xml");
// 3. Создаем нужные объекты .NET
XmlDoc = Врап.СоздатьОбъект("System.Xml.XmlDocument");
// Загружаем наш XML из строки
XmlDoc.LoadXml(ТекстXML);
// 4. Создаем объект трансформации канонизации
C14N = Врап.СоздатьОбъект("System.Security.Cryptography.Xml.XmlDsigC14NTransform");
// Загружаем документ в трансформер
C14N.LoadInput(XmlDoc);
// 5. Создаем объект вычисления хеша SHA1
SHA1 = Врап.СоздатьОбъект("System.Security.Cryptography.SHA1Managed");
// 6. Получаем массив байт хеша
МассивБайт = C14N.GetDigestedOutput(SHA1);
// 7. Конвертируем массив байт в привычную шестнадцатеричную строку
BitConverter = Врап.СоздатьОбъект("System.BitConverter");
СтрокаСДефисами = BitConverter.ToString(МассивБайт);
// Приводим к нужному виду (убираем дефисы)
ИтоговыйХеш = СтрЗаменить(СтрокаСДефисами, "-", "");
Сообщить(ИтоговыйХеш);
Этот способ дает максимальную гибкость, так как позволяет использовать любые методы и свойства объектов .NET без перекомпиляции промежуточных DLL. Однако он требует установки и обслуживания сторонней компоненты на серверах 1С.
Подводя итог, можно сказать, что для большинства современных конфигураций наиболее правильным, надежным и кроссплатформенным решением является первый вариант — использование встроенного в платформу объекта ПреобразованиеККаноническомуXML в связке с объектом ХешированиеДанных.