При интеграции 1С с внешними корпоративными или государственными веб-сервисами разработчики часто сталкиваются с ошибками разбора WSDL при попытке создать WSСсылки или использовать объект WSОпределения. Основная причина кроется в наличии в описании сервиса секций WS-Policy (теги wsp:Policy), определяющих требования к безопасности WS-Security.
Штатные механизмы платформы 1С поддерживают базовый протокол SOAP, но не умеют автоматически интерпретировать политики безопасности и генерировать заголовок wsse:Security. Разберем по шагам, как устроена эта авторизация, как рассчитать все необходимые криптографические поля и отправить корректный запрос к сервису.
Проанализируем ситуацию: если в исходном WSDL присутствует блок wsp:Policy (например, с привязкой sp:TransportBinding и sp:SignedSupportingTokens), платформа 1С аварийно завершает чтение WSDL-схемы. Если вручную вырезать данный узел из локального файла WSDL, объект WSОпределения успешно инициализируется, однако при вызове операций через WSПрокси удаленный сервер возвращает ошибку отсутствия блока авторизации wsse:Security в заголовке SOAP-ENV:Header.
Штатно перехватить XML, который формирует WSПрокси, или добавить в него кастомный заголовок WS-Security без изменения низкоуровневого сетевого стека невозможно. Поэтому для полноценной работы применяются прямые HTTP-запросы с ручным формированием конверта SOAP либо вспомогательные скрипты.
Стандарт безопасности OASIS WS-Security определяет профиль UsernameToken, который включает в себя следующие обязательные элементы:
2026-10-05T09:30:00Z).Рассмотрим подробнее формулу расчета PasswordDigest. Она вычисляется путем конкатенации сырых (бинарных) байтов Nonce, байтов строки Created в UTF-8 и байтов пароля в UTF-8. Полученная последовательность хешируется алгоритмом SHA-1, а результат кодируется в Base64:
PasswordDigest = Base64(SHA1(RawNonceBytes + CreatedBytes + PasswordBytes))
Выясним, как полностью штатными средствами 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Ответ.ПолучитьТелоКакСтроку();
КонецЕсли;
КонецПроцедуры
Если в конфигурации не поддерживаются современные методы работы с двоичными данными или требуется использовать готовую реализацию на 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:
ЧтениеXML и загрузить полученный текст ответа.Body и извлечь вложенный XML-узел полезной нагрузки.ФабрикаXDTO.ПрочитатьXML(ЧтениеXML) для получения строго типизированных объектов 1С.