Как в 1С подключиться к стороннему SOAP веб-сервису с WS-Security и WS-Policy?

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

При интеграции 1С с внешними корпоративными или государственными веб-сервисами разработчики часто сталкиваются с ошибками разбора WSDL при попытке создать WSСсылки или использовать объект WSОпределения. Основная причина кроется в наличии в описании сервиса секций WS-Policy (теги wsp:Policy), определяющих требования к безопасности WS-Security.

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

Почему возникает ошибка и почему WSПрокси не отправляет авторизацию

Проанализируем ситуацию: если в исходном WSDL присутствует блок wsp:Policy (например, с привязкой sp:TransportBinding и sp:SignedSupportingTokens), платформа 1С аварийно завершает чтение WSDL-схемы. Если вручную вырезать данный узел из локального файла WSDL, объект WSОпределения успешно инициализируется, однако при вызове операций через WSПрокси удаленный сервер возвращает ошибку отсутствия блока авторизации wsse:Security в заголовке SOAP-ENV:Header.

Штатно перехватить XML, который формирует WSПрокси, или добавить в него кастомный заголовок WS-Security без изменения низкоуровневого сетевого стека невозможно. Поэтому для полноценной работы применяются прямые HTTP-запросы с ручным формированием конверта SOAP либо вспомогательные скрипты.

Структура блока WS-Security UsernameToken

Стандарт безопасности OASIS WS-Security определяет профиль UsernameToken, который включает в себя следующие обязательные элементы:

  1. Username — логин учетной записи для доступа к сервису.
  2. Created — метка времени создания сообщения в формате UTC (стандарт ISO 8601, например, 2026-10-05T09:30:00Z).
  3. Nonce — одноразовый криптографический случайный набор байтов (обычно 16–32 байта) для защиты от повторных атак (replay attacks). В XML передается как строка Base64.
  4. PasswordDigest — хеш пароля, вычисляемый по строгому алгоритму.

Рассмотрим подробнее формулу расчета PasswordDigest. Она вычисляется путем конкатенации сырых (бинарных) байтов Nonce, байтов строки Created в UTF-8 и байтов пароля в UTF-8. Полученная последовательность хешируется алгоритмом SHA-1, а результат кодируется в Base64:

PasswordDigest = Base64(SHA1(RawNonceBytes + CreatedBytes + PasswordBytes))

Способ 1. Формирование запроса на встроенном языке 1С через HTTPСоединение

Выясним, как полностью штатными средствами 1С сгенерировать корректный заголовок и отправить SOAP-пакет с помощью HTTPСоединение и HTTPЗапрос без привлечения внешних библиотек.

Посмотрим на пример функции генерации случайных двоичных данных и расчета дайджеста:


Функция СформироватьЗаголовокWSSE(Логин, Пароль) Экспорт

    // 1. Формируем временную метку Created в формате UTC
    ТекущаяДатаUTC = УниверсальноеВремя(ТекущаяДатаСеанса());
    СтрокаCreated = Формат(ТекущаяДатаUTC, "ДФ=yyyy-MM-ddTHH:mm:ssZ");

    // 2. Генерируем 16 случайных сырых байтов для Nonce
    ГСЧ = Новый ГенераторСлучайныхЧисел;
    БуферНонса = Новый БуферДвоичныхДанных(16);
    Для Индекс = 0 По 15 Цикл
        БуферНонса.Установить(Индекс, ГСЧ.СлучайноеЧисло(0, 255));
    КонецЦикла;
    
    ДД_Nonce = БуферНонса.ПолучитьДвоичныеДанные();
    СтрокаNonceBase64 = Base64Строка(ДД_Nonce);

    // 3. Получаем байты строк Created и Password в UTF-8
    ДД_Created = ПолучитьДвоичныеДанныеИзСтроки(СтрокаCreated, КодировкаТекста.UTF8, Ложь);
    ДД_Password = ПолучитьДвоичныеДанныеИзСтроки(Пароль, КодировкаТекста.UTF8, Ложь);

    // 4. Склеиваем байты: Nonce + Created + Password
    Поток = Новый ПотокВПамяти;
    ЗаписьДанных = Новый ЗаписьДанных(Поток);
    ЗаписьДанных.ЗаписатьДвоичныеДанные(ДД_Nonce);
    ЗаписьДанных.ЗаписатьДвоичныеДанные(ДД_Created);
    ЗаписьДанных.ЗаписатьДвоичныеДанные(ДД_Password);
    ЗаписьДанных.Закрыть();
    
    СклеенныеДД = Поток.ЗакрытьИПолучитьДвоичныеДанные();

    // 5. Вычисляем SHA-1 хеш
    Хеширование = Новый ХешированиеДанных(ХешФункция.SHA1);
    Хеширование.Добавить(СклеенныеДД);
    ДД_Хеш = Хеширование.ХешСумма;
    
    СтрокаPasswordDigest = Base64Строка(ДД_Хеш);

    // 6. Формируем XML-структуру заголовка wsse:Security
    ЗаписьXML = Новый ЗаписьXML;
    ЗаписьXML.УстановитьСтроку();
    
    ЗаписьXML.ЗаписатьНачалоЭлемента("wsse:Security");
    ЗаписьXML.ЗаписатьСоответствиеПрефикса("wsse", "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd");
    ЗаписьXML.ЗаписатьСоответствиеПрефикса("wsu", "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd");
    ЗаписьXML.ЗаписатьАтрибут("SOAP-ENV:mustUnderstand", "1");
    
    ЗаписьXML.ЗаписатьНачалоЭлемента("wsse:UsernameToken");
    ЗаписьXML.ЗаписатьАтрибут("wsu:Id", "UsernameToken-1");
    
    ЗаписьXML.ЗаписатьНачалоЭлемента("wsse:Username");
    ЗаписьXML.ЗаписатьТекст(Логин);
    ЗаписьXML.ЗаписатьКонецЭлемента();
    
    ЗаписьXML.ЗаписатьНачалоЭлемента("wsse:Password");
    ЗаписьXML.ЗаписатьАтрибут("Type", "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-username-token-profile-1.0#PasswordDigest");
    ЗаписьXML.ЗаписатьТекст(СтрокаPasswordDigest);
    ЗаписьXML.ЗаписатьКонецЭлемента();
    
    ЗаписьXML.ЗаписатьНачалоЭлемента("wsse:Nonce");
    ЗаписьXML.ЗаписатьАтрибут("EncodingType", "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-soap-message-security-1.0#Base64Binary");
    ЗаписьXML.ЗаписатьТекст(СтрокаNonceBase64);
    ЗаписьXML.ЗаписатьКонецЭлемента();
    
    ЗаписьXML.ЗаписатьНачалоЭлемента("wsu:Created");
    ЗаписьXML.ЗаписатьТекст(СтрокаCreated);
    ЗаписьXML.ЗаписатьКонецЭлемента();
    
    ЗаписьXML.ЗаписатьКонецЭлемента(); // wsse:UsernameToken
    ЗаписьXML.ЗаписатьКонецЭлемента(); // wsse:Security
    
    Возврат ЗаписьXML.Закрыть();

КонецФункции

Теперь разберем отправку сформированного конверта через HTTPСоединение:


Процедура ВыполнитьЗапросКВебСервису(АдресСервера, Ресурс, SOAPAction, ТелоОперацииXML, Логин, Пароль)

    ЗаголовокБезопасности = СформироватьЗаголовокWSSE(Логин, Пароль);
    
    // Собираем полный SOAP Envelope
    ТекстЗапроса = 
        "<SOAP-ENV:Envelope xmlns:SOAP-ENV=""http://schemas.xmlsoap.org/soap/envelope/"">" +
        "   <SOAP-ENV:Header>" +
                ЗаголовокБезопасности +
        "   </SOAP-ENV:Header>" +
        "   <SOAP-ENV:Body>" +
                ТелоОперацииXML +
        "   </SOAP-ENV:Body>" +
        "</SOAP-ENV:Envelope>";

    SSL = Новый ЗащищенноеСоединениеOpenSSL;
    HTTPСоединение = Новый HTTPСоединение(АдресСервера, 443, , , , 30, SSL);
    
    HTTPЗапрос = Новый HTTPЗапрос(Ресурс);
    HTTPЗапрос.Заголовки.Вставить("Content-Type", "text/xml; charset=utf-8");
    Если Не ПустаяСтрока(SOAPAction) Тогда
        HTTPЗапрос.Заголовки.Вставить("SOAPAction", SOAPAction);
    КонецЕсли;
    HTTPЗапрос.УстановитьТелоИзСтроки(ТекстЗапроса, КодировкаТекста.UTF8, ИспользованиеByteOrderMark.НеИспользовать);
    
    HTTPОтвет = HTTPСоединение.ОтправитьДляОбработки(HTTPЗапрос);
    
    Если HTTPОтвет.КодСостояния = 200 Тогда
        ТекстОтвета = HTTPОтвет.ПолучитьТелоКакСтроку(КодировкаТекста.UTF8);
        // Обработка XML ответа
    Иначе
        ВызватьИсключение "Ошибка сервиса: " + HTTPОтвет.КодСостояния + " " + HTTPОтвет.ПолучитьТелоКакСтроку();
    КонецЕсли;

КонецПроцедуры

Способ 2. Вычисление параметров через внешние скрипты (MSScriptControl)

Если в конфигурации не поддерживаются современные методы работы с двоичными данными или требуется использовать готовую реализацию на JavaScript, можно воспользоваться объектом MSScriptControl.ScriptControl в среде Windows:


ScriptCtrl = Новый COMОбъект("MSScriptControl.ScriptControl");
ScriptCtrl.Language = "JavaScript";

ScriptFile = Новый ТекстовыйДокумент;
ScriptFile.Прочитать("C:\Scripts\wsse.js");
ScriptSrc = ScriptFile.ПолучитьТекст();

ScriptCtrl.AddCode(ScriptSrc);
wsseHeader = ScriptCtrl.Run("wsseHeader", "UserLogin", "UserPassword");

Полученную строку заголовка можно аналогичным образом подставить в исходящий SOAP-конверт.

Разбор ответа сервиса

Полученный XML-ответ от HTTPСоединение можно разобрать с помощью объекта ЧтениеXML и фабрики XDTO:

  1. Инициализировать ЧтениеXML и загрузить полученный текст ответа.
  2. Перейти к тегу Body и извлечь вложенный XML-узел полезной нагрузки.
  3. Если схемы XSD добавлены в конфигурацию или загружены в пакеты, использовать метод ФабрикаXDTO.ПрочитатьXML(ЧтениеXML) для получения строго типизированных объектов 1С.
← На главную