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

Я использую 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# запросом.

14 ответов

Решение

Если сервер поддерживает только более высокую версию 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);

Просто укажите 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 (см. Ответ Рональда Рамоса)

Существует неглобальная настройка на уровне 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).

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

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();

Когда подключиться к 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():

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

У меня была такая же проблема при подключении к 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");

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

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

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

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

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

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

Базовое соединение было закрыто: не удалось установить доверительные отношения для безопасного канала 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+"

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

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

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;

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

Я согласен с 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

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

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


        handler.ClientCertificateOptions = ClientCertificateOption.Automatic;
        handler.PreAuthenticate = true;
        HttpClient client = new HttpClient(handler);

Вот рабочий код, который работает и при HTTPS-вызове

      UriBuilder builder = new UriBuilder("https://yourdomain.com/");
builder.Query = "id=10";                 
//Create a query
HttpClient client = new HttpClient();
System.Net.ServicePointManager.SecurityProtocol = 
SecurityProtocolType.Tls12 | SecurityProtocolType.Tls11 | SecurityProtocolType.Tls;                  
client.DefaultRequestHeaders.Add("Authorization", userprofile.Token);                
var result = client.GetAsync(builder.Uri).Result;                                       
using (StreamReader sr = new 
StreamReader(result.Content.ReadAsStreamAsync().Result))
{                       
   responseres = sr.ReadToEnd();
}

Для ошибки:

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

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

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

Как написал Oppositional в своем ответе на вопрос .NET клиент подключается к ssl Web API.

Другие вопросы по тегам