Сделайте вызов Https с помощью HttpClient

157

Я использую HttpClientдля звонков WebApi с использованием C #. Кажется аккуратным и быстрым способом по сравнению с WebClient. Однако я застрял во время Httpsзвонков.

Как я могу сделать код ниже, чтобы совершать Httpsзвонки?

HttpClient httpClient = new HttpClient();
httpClient.BaseAddress = new Uri("https://foobar.com/");
httpClient.DefaultRequestHeaders.Accept.Clear();
httpClient.DefaultRequestHeaders.Accept.Add(
                new MediaTypeWithQualityHeaderValue("application/xml"));

var task = httpClient.PostAsXmlAsync<DeviceRequest>(
                "api/SaveData", request);

РЕДАКТИРОВАТЬ 1: приведенный выше код отлично работает для http-звонков. Но когда я меняю схему на https, она не работает. Вот полученная ошибка:

Базовое соединение было закрыто: не удалось установить доверительные отношения для безопасного канала SSL / TLS.

РЕДАКТИРОВАТЬ 2: Изменение схемы на https: шаг первый.

Как мне предоставить сертификат и открытый / закрытый ключ вместе с C # запросом.

Abhijeet
источник
2
вы делаете https звонки, просто указавnew Uri("https://foobar.com/");
felickz
2
Я запутался. Это уже не работает? Вы получаете ошибку? (Изменить: опубликовано до того, как OP изменил URI с https на http)
Тим

Ответы:

215

Если сервер поддерживает только более высокую версию TLS, например, только TLS 1.2, он все равно не будет работать, если ваш клиентский ПК не настроен на использование более высокой версии TLS по умолчанию. Чтобы преодолеть эту проблему, добавьте следующее в ваш код.

System.Net.ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls12 | SecurityProtocolType.Tls11 | SecurityProtocolType.Tls;

Модифицируя ваш пример кода, это будет

HttpClient httpClient = new HttpClient();   

//specify to use TLS 1.2 as default connection
System.Net.ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls12 | SecurityProtocolType.Tls11 | SecurityProtocolType.Tls;

httpClient.BaseAddress = new Uri("https://foobar.com/");
httpClient.DefaultRequestHeaders.Accept.Clear();
httpClient.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/xml"));

var task = httpClient.PostAsXmlAsync<DeviceRequest>("api/SaveData", request);
Рональд Рамос
источник
@DerekS Я рад, и пожалуйста. Если по какой-либо причине вы не можете изменить код в производственном параметре, но можете настроить администратора на сервере, я использую эту утилиту для настройки TLS 1.2 по умолчанию. nartac.com/Products/IISCrypto
Рональд Рамос
SecurityProtocolType.Tls12не удалось найти перечисленные вами значения перечислений
JobaDiniz
1
@JobaDiniz использует .NET 4.5 или выше и включает пространство имен System.Net.
Рональд Рамос,
Нужно ли устанавливать SecurityProtocol только один раз? Как при запуске приложения?
CamHart
Это прекрасно работает. Спасибо. Я также обнаружил, что мне не нужно очищать какие-либо заголовки, использующие этот клиент HttpClient = new HttpClient (); ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls12 | SecurityProtocolType.Tls11 | SecurityProtocolType.Tls; HttpResponseMessage response = await client.GetAsync ("https: // ...");
AtLeastThereToast
127

Просто укажите HTTPS в URI.

new Uri("https://foobar.com/");

Foobar.com должен иметь доверенный сертификат SSL, иначе ваши звонки не будут выполнены из-за ненадежной ошибки.

РЕДАКТИРОВАТЬ Ответ: ClientCertificates с HttpClient

WebRequestHandler handler = new WebRequestHandler();
X509Certificate2 certificate = GetMyX509Certificate();
handler.ClientCertificates.Add(certificate);
HttpClient client = new HttpClient(handler);

РЕДАКТИРОВАТЬ Ответ 2: Если сервер, к которому вы подключаетесь, отключил SSL, TLS 1.0 и 1.1, и вы все еще используете .NET Framework 4.5 (или ниже), вам нужно сделать выбор

  1. Обновление до .Net 4.6+ ( по умолчанию поддерживает TLS 1.2 )
  2. Добавьте изменения в реестр, чтобы указать 4.5 для подключения через TLS1.2 (См .: Salesforce writeup для compat и ключи для изменения ИЛИ извлечение IISCryp, чтобы увидеть комментарии Рональда Рамоса )
  3. Добавьте код приложения для ручной настройки .NET для подключения через TLS1.2 (см. Ответ Рональда Рамоса )
felickz
источник
@felickz отличный ответ. Есть ли эквивалент библиотеки WebRequestHandler для Windows Phone 8?
Биллатрон
@Billatron разные библиотеки для WP / Win8 .. см.
felickz
6
При разработке или работе с самоподписанными сертификатами вы можете игнорировать ненадежные ошибки сертификатов с помощью следующего: ServicePointManager.ServerCertificateValidationCallback += (sender, cert, chain, sslPolicyErrors) => true;
разработчик
6
Что такое GetMyX509Certificate?
Fandi Susanto
6
@developer позволяет надеяться, что код не попадет в вашу производственную сборку :)
felickz
15

Ваш код должен быть изменен следующим образом:

httpClient.BaseAddress = new Uri("https://foobar.com/");

Вы просто должны использовать https: схему URI. Там есть полезная страница здесь на MSDN о защищенных соединениях HTTP. На самом деле:

Используйте схему https: URI

Протокол HTTP определяет две схемы URI:

http: используется для незашифрованных соединений.

https: используется для безопасных соединений, которые должны быть зашифрованы. Эта опция также использует цифровые сертификаты и центры сертификации для проверки того, кем является сервер.

Кроме того, учтите, что соединения HTTPS используют сертификат SSL. Убедитесь, что ваше безопасное соединение имеет этот сертификат, иначе запросы не будут выполнены.

РЕДАКТИРОВАТЬ:

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

Что это значит не работает? Сбой запросов? Исключение брошено? Уточни свой вопрос.

Если запросы терпят неудачу, то проблема должна быть в сертификате SSL.

Чтобы решить эту проблему, вы можете использовать класс, HttpWebRequestа затем его свойство ClientCertificate. Кроме того, вы можете найти здесь полезный пример того, как сделать HTTPS-запрос с использованием сертификата.

Примером является следующий (как показано на странице MSDN, связанной ранее):

//You must change the path to point to your .cer file location. 
X509Certificate Cert = X509Certificate.CreateFromCertFile("C:\\mycert.cer");
// Handle any certificate errors on the certificate from the server.
ServicePointManager.CertificatePolicy = new CertPolicy();
// You must change the URL to point to your Web server.
HttpWebRequest Request = (HttpWebRequest)WebRequest.Create("https://YourServer/sample.asp");
Request.ClientCertificates.Add(Cert);
Request.UserAgent = "Client Cert Sample";
Request.Method = "GET";
HttpWebResponse Response = (HttpWebResponse)Request.GetResponse();
Альберто Солано
источник
Трюк заключается в отправке сертификатов вместе с запросом. как это сделать?
Абхиджит
Где взять "C: \\ mycert.cer"? Как создать файл mycert.cer?
Masiboo
@masiboo То, что я написал, отвечает на вопросы о том, как совершать HTTPS-звонки. Спросите Google о том, как создать файл * .cer, и вы сами найдете ответ.
Альберто Солано
9

При подключении httpsя тоже получаю эту ошибку, я добавляю эту строку раньше HttpClient httpClient = new HttpClient();и успешно подключаюсь:

ServicePointManager.ServerCertificateValidationCallback = delegate { return true; };

Я знаю это из этого ответа и другого подобного ответа, а комментарий упоминает:

Это хак, полезный в разработке, поэтому добавление в него оператора #if DEBUG #endif - это минимум, что вы должны сделать, чтобы сделать это более безопасным и остановить его в конечном итоге в производстве.

Кроме того, я не пробовал метод в другом ответе, который использует new X509Certificate()или new X509Certificate2()для создания сертификата, я не уверен, что просто создать new()будет работать или нет.

РЕДАКТИРОВАТЬ: Некоторые ссылки:

Создайте самоподписанный сертификат сервера в IIS 7

Импорт и экспорт SSL-сертификатов в IIS 7

Конвертировать .pfx в .cer

Лучшие практики для использования ServerCertificateValidationCallback

Я считаю, что значение отпечатка равно x509certificate.GetCertHashString():

Получить отпечаток сертификата

ю ян цзянь
источник
6

У меня была такая же проблема при подключении к GitHub, которая требует агента пользователя. Таким образом, достаточно предоставить это, а не генерировать сертификат

var client = new HttpClient();

client.BaseAddress = new Uri("https://api.github.com");
client.DefaultRequestHeaders.Add(
    "Authorization",
    "token 123456789307d8c1d138ddb0848ede028ed30567");
client.DefaultRequestHeaders.Accept.Add(
    new MediaTypeWithQualityHeaderValue("application/json"));
client.DefaultRequestHeaders.Add(
    "User-Agent",
    "Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/41.0.2228.0 Safari/537.36");
Карло В. Данго
источник
2
Вы должны отозвать тот токен, которым вы поделились с интернетом, я думаю, что GitHub, возможно, сделал это автоматически.
Амиас
21
ты действительно думаешь, что мой токен начинается с 123456789??
Карло В. Данго
5

Существует неглобальная настройка на уровне HttpClientHandler:

var handler = new HttpClientHandler()
{
    SslProtocols = SslProtocols.Tls12 | SslProtocols.Tls11 | SslProtocols.Tls
};

var client = new HttpClient(handler);

Таким образом, один включает последние версии TLS.

Обратите внимание, что значение по умолчанию SslProtocols.Defaultфактически SslProtocols.Ssl3 | SslProtocols.Tls(проверено для .Net Core 2.1 и .Net Framework 4.7.1).

стоп-Cran
источник
Спасибо за ответ. Небольшое примечание: согласно этой документации: docs.microsoft.com/en-us/dotnet/api/… Требуется как минимум фреймворк 4.7.1.
GELR
@ ГЕЛР да, ты прав. Приведенный выше код приводит к ошибке времени выполнения в .Net 4.6.1. Исправлен ответ.
Стоп-кран
В предыдущих версиях .NET Framework это свойство было частным
JotaBe
В Net Core 3.1 это все, что у меня работало. Установка глобального System.Net.ServicePointManager.SecurityProtocol = xxx - имела абсолютно нулевой эффект в трассировке пакета.
Роуэн Смит
3

Простое указание HTTPS в URI должно помочь.

httpClient.BaseAddress = new Uri("https://foobar.com/");

Если запрос работает с HTTP, но не работает с HTTPS, то это, безусловно, проблема с сертификатом . Убедитесь, что вызывающий абонент доверяет издателю сертификата и что срок действия сертификата не истек. Быстрый и простой способ проверить это - попытаться выполнить запрос в браузере.

Вы также можете захотеть проверить на сервере (если он ваш и / или если вы можете), чтобы он был настроен для правильного обслуживания запросов HTTPS.

Хроно
источник
: У меня похожая проблема: я сделал запрос в браузере, и сертификат действителен (вызов проходит, авторизован как уровень обслуживания и все работает как чудо), но программно не работает. Что еще может быть? если я использую точно такой же сертификат из браузера, "вызывающего службу вручную", чем из веб-приложения, вызывающего srvice программно?
Карлос
2

Я также получил ошибку:

Базовое соединение было закрыто: не удалось установить доверительные отношения для безопасного канала SSL / TLS.

... с приложением-мишенью для Android Xamarin Forms, пытающимся запросить ресурсы у поставщика API, для которого требуется TLS 1.3.

Решение состояло в том, чтобы обновить конфигурацию проекта для замены «управляемого» (.NET) http-клиента Xamarin (который не поддерживает TLS 1.3 с Xamarin Forms v2.5) и вместо этого использовать собственный клиент Android.

Это простой проект переключения в визуальной студии. Смотрите скриншот ниже.

  • Свойства проекта
  • Настройки Android
  • продвинутый
  • Элемент списка
  • Измените «Реализация HttpClient» на «Android»
  • Измените реализацию SSL / TLS на «Собственный TLS 1.2+»

введите описание изображения здесь

MSC
источник
1

Добавьте следующие объявления в ваш класс:

public const SslProtocols _Tls12 = (SslProtocols)0x00000C00;
public const SecurityProtocolType Tls12 = (SecurityProtocolType)_Tls12;

После:

var client = new HttpClient();

И:

ServicePointManager.SecurityProtocol = Tls12;
System.Net.ServicePointManager.SecurityProtocol = SecurityProtocolType.Ssl3 /*| SecurityProtocolType.Tls */| Tls12;

Счастливый? :)

Грег Ставски
источник
1

У меня была эта проблема, и в моем случае решение было глупо простым: открыть Visual Studio с правами администратора. Я попробовал все вышеупомянутые решения, и это не сработало, пока я не сделал это. Надеюсь, это сэкономит кому-то драгоценное время.

Люсьен Сатмарский
источник
Поскольку вы новичок на сайте, вы можете не знать, что если у ответа есть зеленая галочка рядом с ним, это означает, что OP принял его как правильное решение для его / ее проблемы. Если вы не можете дать лучший, более полный ответ, вероятно, лучше не давать другого ответа, особенно на такой старый вопрос.
Сэм
4
Ну, у меня тоже была проблема, и зеленый проверенный ответ совсем не помог. Думаю, я даю альтернативное решение, которое мне помогло, но, эй, я не буду делать это в следующий раз. Спасибо за минусовые баллы;) Удачного дня.
Лукиан Сатмарян
0

Вы можете попробовать использовать пакет Nuget ModernHttpClient: после загрузки пакета вы можете реализовать его следующим образом:

 var handler = new ModernHttpClient.NativeMessageHandler()
 {
     UseProxy = true,
 };


 handler.ClientCertificateOptions = ClientCertificateOption.Automatic;
 handler.PreAuthenticate = true;
 HttpClient client = new HttpClient(handler);
Ученна Ннодим
источник
К сожалению, это не сработало от меня. Решение состояло в том, чтобы включить TLS 1.3 путем переключения с управляемого http-клиента xamarin на собственный http-клиент android. Смотрите мой ответ ниже.
MSC
0

Я согласен с felickz, но также хочу добавить пример для пояснения использования в c #. Я использую SSL в Windows службы следующим образом.

    var certificatePath = System.IO.Path.Combine(System.AppDomain.CurrentDomain.BaseDirectory, "bin");
    gateway = new GatewayService();
    gateway.PreAuthenticate = true;


    X509Certificate2 cert = new X509Certificate2(certificatePath + @"\Attached\my_certificate.pfx","certificate_password");
    gateway.ClientCertificates.Add(cert);

    ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls12;
    gateway.UserAgent = Guid.NewGuid().ToString();
    gateway.Timeout = int.MaxValue;

Если я собираюсь использовать его в веб-приложении, я просто изменяю реализацию на стороне прокси следующим образом:

public partial class GatewayService : System.Web.Services.Protocols.SoapHttpClientProtocol // to => Microsoft.Web.Services2.WebServicesClientProtocol
Хамит ЙИЛДИРИМ
источник
-4

Для ошибки:

Базовое соединение было закрыто: не удалось установить доверительные отношения для безопасного канала SSL / TLS.

Я думаю, что вам нужно безоговорочно принять сертификат со следующим кодом

ServicePointManager.ServerCertificateValidationCallback += 
    (sender, cert, chain, sslPolicyErrors) => true;

Как написал Оппозиционный в своем ответе на вопрос .NET клиент подключается к ssl Web API .

Неманья Симович
источник
4
Это обход проверки сертификата. Никогда не делай этого в производстве.
Джон Корснес
Хорошо @JohnKorsnes, если Abhijeet получает доступ к какому-либо общедоступному серверу WebAPI, я не думаю, что он может много с этим поделать. Это был мой случай.
Неманья Симович