Как подключиться к HTTP-сервису с авторизацией по клиентскому SSL-сертификату в 1С:Предприятии?

Программист 1С v8.3 (Обычные формы)
← На главную

Подключение к внешним HTTP-сервисам с использованием клиентских SSL-сертификатов для авторизации может стать непростой задачей для разработчиков на платформе 1С:Предприятие. Нам предстоит рассмотреть типичные проблемы, с которыми сталкиваются пользователи при работе со встроенными средствами 1С, проводя тестирование http сервисов, и предложить рабочее решение, а также альтернативные подходы.

Попытка 1: Использование встроенных средств 1С – ЗащищенноеСоединениеOpenSSL

Платформа 1С:Предприятие предоставляет объект ЗащищенноеСоединениеOpenSSL для установки защищенных соединений. Для авторизации по клиентскому сертификату требуется предоставить файл клиентского сертификата в формате PKCS#12 (обычно с расширением .p12 или .pfx) и пароль к нему. Эти форматы содержат как публичную часть сертификата, так и закрытый ключ, необходимый для аутентификации.

Наш первоначальный подход, часто используемый разработчиками, выглядит следующим образом:


// Шаг 1: Конвертация .cer в .p12 (если исходный сертификат только публичный)
// Этот шаг выполняется вне 1С, например, с помощью утилиты OpenSSL.
// Пример команды OpenSSL для создания p12 из закрытого ключа (.key), сертификата (.crt) и цепочки УЦ (если есть):
// openssl pkcs12 -export -out cert.p12 -inkey private.key -in cert.crt -certfile CAcert.crt -name "MyClientCert" -passout pass:мойпароль

// Шаг 2: Создание защищенного соединения в 1С
// "e:\Install\pki.p12" - путь к файлу клиентского сертификата в формате PKCS#12
// "пароль" - пароль к файлу .p12
ССЛ = Новый ЗащищенноеСоединениеOpenSSL(Новый СертификатКлиентаФайл("e:\Install\pki.p12","пароль"));

// Хост HTTP-сервиса
Хост = "fglrr-site.its";

// Создание HTTP-соединения с использованием защищенного соединения
HTTPСоединение = Новый HTTPСоединение(Хост, , , , , ,ССЛ,);

// Пример HTTP-запроса
HTTPЗапрос = Новый HTTPЗапрос("/XAdES-TesT/Sign?WSDL");

// Формирование тела SOAP-запроса
СтрокаXML = "
        |<s:Envelope xmlns:s=""http://schemas.xmlsoap.org/soap/envelope/"">
        |    <s:Body xmlns:xsi=""http://www.w3.org/2001/XMLSchema-instance"" xmlns:xsd=""http://www.w3.001/XMLSchema"">
        |        <requestSignXAdES xmlns=""http://sign.server.procertum.unizeto/"">
        |            <certificate xmlns="">%СертБасе64%</certificate>
        |            <contentType xmlns="">application/octet-stream</contentType>
        |            <data xmlns="">%данные%</data>
        |            <signatureType xmlns="">ENVELOPING</signatureType>
        |            <validationDataFormat xmlns="">XADES</validationDataFormat>
        |        </requestSignXAdES>
        |    </s:Body>
        |</s:Envelope>";

// Пример получения сертификата в Base64 для включения в тело SOAP
// Это действие НЕ связано с SSL-аутентификацией на уровне транспортного протокола.
// Оно требуется, если сам сервис ожидает сертификат в теле запроса для своих нужд.
СертДвоичнДанные = Новый ДвоичныеДанные("e:\Install\testq.cer");
СертБасе64 = ПолучитьСертификатBase64Строку(СертДвоичнДанные);

Функция ПолучитьСертификатBase64Строку(ДвоичнДанные) Экспорт
    СтрокаBase64 = Base64Строка(ДвоичнДанные);
    Значение = СтрЗаменить(СтрокаBase64, Символы.ВК, "");
    Значение = СтрЗаменить(Значение, Символы.ПС, "");
    Возврат Значение;
КонецФункции

СтрокаXML = СтрЗаменить(СтрокаXML,"%СертБасе64%",СертБасе64);

HTTPЗапрос.УстановитьТелоИзСтроки(СтрокаXML);

// Отправка запроса
Результат = HTTPСоединение.ОтправитьДляОбработки(HTTPЗапрос);
Ответ = Результат.ПолучитьТелоКакСтроку();
Сообщить(Ответ);

В данном случае, мы сталкиваемся с ошибкой "400 The SSL certificate error". Эта ошибка, как правило, указывает на то, что серверу не понравился клиентский сертификат, который был отправлен (или не отправлен) на этапе SSL-рукопожатия. Важно понимать, что встраивание сертификата в Base64 в тело SOAP-запроса (как показано выше) – это требование самого SOAP-сервиса на прикладном уровне, а не механизм SSL-аутентификации, который происходит на транспортном уровне, до того, как тело запроса будет обработано сервером.

Проверка серверного сертификата: Зачем нужен cacert.pem?

Помимо клиентского сертификата, при установке защищенного соединения необходимо обеспечить проверку подлинности сертификата самого HTTP-сервера, к которому мы подключаемся. Для этого в конструкторе ЗащищенноеСоединениеOpenSSL можно использовать второй параметр – объект СертификатыУдостоверяющихЦентровФайл, который указывает на файл cacert.pem. Этот файл должен содержать корневые и промежуточные сертификаты удостоверяющих центров (УЦ), которым доверяет наша клиентская сторона 1С для проверки подлинности серверного сертификата.

Пример использования:


// "e:/!work/pki.p12.pem" - файл, содержащий корневые и промежуточные сертификаты УЦ,
// которым вы доверяете для проверки серверного сертификата.
// Важно: это НЕ тот же сертификат, что и клиентский .p12.
// Он должен включать сертификаты УЦ, выпустивших СЕРВЕРНЫЙ сертификат.
ССЛ = Новый ЗащищенноеСоединениеOpenSSL(Новый СертификатКлиентаФайл("e:/!work/pki.p12", "123"), Новый СертификатыУдостоверяющихЦентровФайл("e:/!work/pki.p12.pem"));

Если файл cacert.pem отсутствует, некорректен или не содержит нужных сертификатов УЦ, мы получим ошибку "Ошибка работы с Интернет: Удаленный узел не прошел проверку". Это означает, что 1С не смогла подтвердить подлинность сертификата сервера. Если мы получили именно эту ошибку, нам необходимо выполнить следующие шаги:

  1. Получим цепочку сертификатов для сервера. Обычно это можно сделать, открыв сайт в браузере, просмотрев сведения о сертификате и экспортировав корневой и все промежуточные сертификаты.
  2. Добавим эти сертификаты в файл cacert.pem. Это обычно делается путем конкатенации содержимого файлов .cer (в формате Base64) или .pem в один общий текстовый файл cacert.pem.

Даже после правильной настройки cacert.pem и использования клиентского .p12, проблема "400 The SSL certificate error" может сохраняться, указывая на то, что сервер всё ещё отклоняет наш клиентский сертификат. Давайте выясним наиболее вероятные причины этой ошибки:

Альтернативное решение 1: Использование stunnel

В качестве обходного пути, мы можем рассмотреть использование утилиты stunnel. stunnel — это кроссплатформенный прокси-сервер, который может устанавливать SSL/TLS-соединения от вашего имени. Мы можем настроить stunnel на вашей локальной машине или на сервере 1С следующим образом:

  1. Настроим stunnel для подключения к внешнему HTTP-сервису, используя наш клиентский SSL-сертификат и выполняя все необходимые SSL-рукопожатия. В конфигурации stunnel мы укажем путь к клиентскому сертификату и ключу, а также к файлам УЦ.
  2. В 1С будем подключаться к stunnel по обычному HTTP (без SSL), указав в HTTPСоединение адрес и порт, на котором прослушивает stunnel. stunnel будет прозрачно перенаправлять трафик через защищенное соединение.

Этот подход может быть полезен, если мы столкнулись с непреодолимыми трудностями при настройке SSL напрямую в 1С. Для организации других видов обмена данными, например, через WebSTOMP для RabbitMQ, могут потребоваться иные инструменты и протоколы.

Рабочее решение: Использование COM-объекта WinHttp.WinHttpRequest.5.1

Если встроенные средства 1С и OpenSSL не дают желаемого результата, наиболее эффективным и проверенным решением является использование системного COM-объекта WinHttp.WinHttpRequest.5.1. Этот объект предоставляет функциональность HTTP-клиента, которая использует встроенные механизмы Windows для работы с SSL/TLS, включая доступ к системным хранилищам сертификатов Windows.

Преимущество этого подхода в том, что если наш клиентский сертификат корректно установлен в хранилище сертификатов Windows, то WinHttp.WinHttpRequest.5.1 может автоматически его использовать. Похожие системные механизмы авторизации задействуются, когда выполняется интеграция 1С с Google Таблицами через сервисный аккаунт.

Для использования этого метода, выполним следующие шаги:

  1. Установим клиентский сертификат в личное хранилище сертификатов пользователя, от имени которого запускается сервер 1С (для серверного вызова) или клиент 1С (для клиентского вызова). Убедимся, что закрытый ключ доступен и корректно импортирован вместе с сертификатом.
  2. Создадим COM-объект WinHttp.WinHttpRequest.5.1 в 1С.
  3. Настроим параметры запроса, включая URL, заголовки и тело запроса.
  4. Укажем клиентский сертификат, если он не выбран автоматически. Для этого часто используется метод SetClientCertificate или свойство Option (с параметром WinHttpRequestOption_ClientCertificate), позволяющее выбрать сертификат по отпечатку или другим характеристикам.

Давайте посмотрим на концептуальный пример кода для WinHttp.WinHttpRequest.5.1 (точный выбор сертификата по отпечатку может отличаться в зависимости от контекста и версии Windows):


// Создаем COM-объект WinHttpRequest
Попытка
    WinHttp = Новый COMОбъект("WinHttp.WinHttpRequest.5.1");
Исключение
    Сообщить("Ошибка при создании COM-объекта WinHttp.WinHttpRequest.5.1: " + ОписаниеОшибки());
    Возврат;
КонецПопытки;

// Устанавливаем метод HTTP (POST, GET и т.д.), URL и асинхронность (Ложь - синхронный)
WinHttp.Open("POST", "https://fglrr-site.its/XAdES-TesT/Sign", Ложь);

// Устанавливаем необходимые заголовки запроса
WinHttp.SetRequestHeader("Content-Type", "text/xml; charset=utf-8");
// Для SOAP-сервисов часто требуется заголовок SOAPAction
// WinHttp.SetRequestHeader("SOAPAction", """http://sign.server.procertum.unizeto/requestSignXAdES""");

// Указываем клиентский сертификат.
// Если сертификат установлен в хранилище "Личные", WinHttp может автоматически
// выбрать подходящий сертификат, если он является единственным или настроен для домена.
// Для явного указания сертификата по отпечатку (Thumbprint) можно использовать
// метод SetClientCertificate. Отпечаток сертификата можно найти в свойствах сертификата
// в хранилище Windows.
// Пример:
// ИмяСертификата = "CN=My Client Certificate, O=My Org"; // Или отпечаток
// WinHttp.SetClientCertificate("CURRENT_USER\My\" + ИмяСертификата);
//
// Более гибкий способ - через свойства:
// Const WinHttpRequestOption_ClientCertificate = 13; // Код опции для клиентского сертификата
// Const CERT_SYSTEM_STORE_CURRENT_USER = 1;
// Const CERT_SYSTEM_STORE_LOCAL_MACHINE = 2;
//
// Мы можем перечислить сертификаты в хранилище и выбрать нужный по отпечатку
// или другим параметрам. Например, получим отпечаток заранее.
// ОтпечатокСертификата = "ВАШ_ОТПЕЧАТОК_СЕРТИФИКАТА_БЕЗ_ПРОБЕЛОВ"; // Пример: "‎4f11a43a011030e2381f5c6f0d1487d21d7b049b"
//
// Для получения отпечатка можно использовать:
// WinHttp.Option(WinHttpRequestOption_ClientCertificate) = "LOCAL_MACHINE\My\" + ОтпечатокСертификата; // Если сертификат в хранилище компьютера
// WinHttp.Option(WinHttpRequestOption_ClientCertificate) = "CURRENT_USER\My\" + ОтпечатокСертификата; // Если сертификат в хранилище текущего пользователя
//
// В простейшем случае, если сертификат единственный и подходящий, WinHttp может найти его сам.
// Для данной темы, поскольку автор смог решить проблему, вероятно, использовался
// либо автоматический выбор, либо отпечаток.

// Формируем тело запроса (как и для HTTPЗапроса)
СтрокаXML = "
        |<s:Envelope xmlns:s=""http://schemas.xmlsoap.org/soap/envelope/"">
        |    <s:Body xmlns:xsi=""http://www.w3.org/2001/XMLSchema-instance"" xmlns:xsd=""http://www.w3.001/XMLSchema"">
        |        <requestSignXAdES xmlns=""http://sign.server.procertum.unizeto/"">
        |            <certificate xmlns="">%СертБасе64%</certificate>
        |            <contentType xmlns="">application/octet-stream</contentType>
        |            <data xmlns="">%данные%</data>
        |            <signatureType xmlns="">ENVELOPING</signatureType>
        |            <validationDataFormat xmlns="">XADES</validationDataFormat>
        |        </requestSignXAdES>
        |    </s:Body>
        |</s:Envelope>";

// Предполагаем, что СертБасе64 уже сформирован, как в предыдущем примере
// СертДвоичнДанные = Новый ДвоичныеДанные("e:\Install\testq.cer");
// СертБасе64 = ПолучитьСертификатBase64Строку(СертДвоичнДанные);
// СтрокаXML = СтрЗаменить(СтрокаXML,"%СертБасе64%",СертБасе64);

// Отправляем запрос
WinHttp.Send(СтрокаXML);

// Получаем ответ и проверяем статус
Если WinHttp.Status = 200 Тогда
    Сообщить("Успешно: " + WinHttp.ResponseText);
Иначе
    Сообщить("Ошибка " + WinHttp.Status + ": " + WinHttp.StatusText);
    Сообщить("Ответ сервера: " + WinHttp.ResponseText);
КонецЕсли;

Этот подход позволил автору темы успешно решить проблему с подключением, что подчеркивает его надежность для сложных сценариев SSL-аутентификации в 1С, особенно когда возникают несовместимости или ограничения с встроенными механизмами платформы.

Важные моменты и рекомендации

Для успешной работы с клиентскими SSL-сертификатами в 1С, мы рекомендуем учитывать следующие аспекты:

  1. Формат клиентского сертификата: Для встроенных средств 1С (ЗащищенноеСоединениеOpenSSL) клиентский сертификат должен быть в формате PKCS#12 (файлы .p12 или .pfx), содержащий закрытый ключ. Если у вас есть только публичный сертификат (.cer), его необходимо конвертировать вместе с закрытым ключом в этот формат.
  2. Цепочка доверия сервера (файл cacert.pem): Всегда удостоверяйтесь, что ваш файл cacert.pem содержит сертификаты удостоверяющих центров, которым вы доверяете для проверки подлинности серверного сертификата. Если этот файл отсутствует или некорректен, вы получите ошибку "Удаленный узел не прошел проверку".
  3. Проверка на стороне сервера: Если вы получаете ошибку "400 The SSL certificate error", это часто означает, что сервер не принял ваш клиентский сертификат. Возможно, сервер не доверяет УЦ, выдавшему ваш сертификат, или ожидает другой конкретный сертификат. В таких случаях необходимо обратиться к администраторам целевого сервиса для уточнения требований и проверки настроек на их стороне.
  4. Различие SSL-аутентификации и данных в XML: Мы должны четко понимать, что передача сертификата в Base64 внутри XML-тела HTTP-запроса — это требование прикладного уровня сервиса (например, для электронной подписи), а не часть SSL/TLS-аутентификации, которая происходит на транспортном уровне, до обработки содержимого запроса. Эти два механизма независимы.
  5. Отладка сетевого трафика: Для более детального анализа проблем с SSL/TLS, мы рекомендуем использовать такие инструменты, как Wireshark или Fiddler. Они позволяют перехватывать и анализировать сетевой трафик, просматривать процесс SSL-рукопожатия, обмениваемые сертификаты и ошибки, возникающие на низком уровне, что существенно облегчает диагностику.

Подводя итог, подключение к HTTP-сервисам с клиентскими SSL-сертификатами в 1С требует тщательной настройки и понимания принципов работы SSL/TLS. Если встроенные механизмы 1С вызывают трудности или требуют слишком много усилий для настройки, использование COMОбъект("WinHttp.WinHttpRequest.5.1") является проверенным и эффективным решением, использующим системные возможности Windows.

← На главную