Здійснюйте 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);

EDIT 1: Код, що знаходиться вище, чудово працює для здійснення дзвінків http. Але коли я міняю схему на https, вона не працює. Ось отримана помилка:

Базове з'єднання було закрито: Не вдалося встановити довірчі відносини для захищеного каналу SSL / TLS.

EDIT 2: Зміна схеми на https - це: перший крок.

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


2
Ви здійснюєте https-дзвінки лише уточнюючиnew Uri("https://foobar.com/");
felickz

2
Я збентежений. Це вже не працює? Ви отримуєте помилку? (Редагувати: Опубліковано до того, як ОП змінила 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 = новий HttpClient (); ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls12 | SecurityProtocolType.Tls11 | SecurityProtocolType.Tls; HttpResponseMessage відповідь = очікуємо client.GetAsync ("https: // ...");
AtLeastTheresToast

127

Просто вкажіть HTTPS в URI.

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

Foobar.com повинен мати надійний сервер SSL, або ваші дзвінки не вдасться із недовірливою помилкою.

Відповідь EDIT: ClientCertificate з HttpClient

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

EDIT Answer2: Якщо сервер, до якого ви підключаєтесь, відключив SSL, TLS 1.0 та 1.1 і ви все ще працюєте .NET Framework 4.5 (або нижче), вам потрібно зробити вибір

  1. Оновлення до .Net 4.6+ ( підтримує TLS 1.2 за замовчуванням )
  2. Додайте зміни в реєстрі, щоб доручити 4.5 підключитися через TLS1.2 (Див.: Реєстрація у продавців для компати та ключі, щоб змінити АБО замовлення IISCrypto, див. Відповіді Рональда Рамоса на коментарі )
  3. Додайте код додатка вручну налаштувати .NET для підключення через TLS1.2 (див Ronald Ramos відповідь )

@felickz чудова відповідь. чи є еквівалент бібліотеці WebRequestHandler для Windows Phone 8?
Billatron

@Billatron різні бібліотеки для WP / Win8 .. дивіться
felickz

6
Розробляючи або маючи справу з самопідписаними сертами, ви можете ігнорувати недовірені помилки cert з наступним: ServicePointManager.ServerCertificateValidationCallback += (sender, cert, chain, sslPolicyErrors) => true;
розробник

6
Що таке GetMyX509Certificate?
Фанді Сусанто

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

Попередньо хитрість полягає у надсиланні сертифікатів разом із запитом. як це зробити?
Abhijeet

Де взяти "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()буде працювати чи ні.

EDIT: Деякі посилання:

Створіть самопідписаний серверний сертифікат у IIS 7

Імпорт та експорт сертифікатів SSL в IIS 7

Перетворити .pfx у .cer

Кращі практики використання ServerCertificateValidationCallback

Я вважаю, що значення Thumbprint дорівнює 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).


Дякую за відповідь. Маленька примітка: відповідно до цієї документації: docs.microsoft.com/en-us/dotnet/api/… Для цього потрібен принаймні фреймворк 4.7.1.
GELR

@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.


: У мене є аналогічна проблема: я зробив запит у браузері, і сертифікат дійсний (дзвінок проходить через, дозволений як рівень обслуговування і все працює як шарм), але програмно не працює. Що ще може бути? якщо я використовую такий самий сертифікат від браузера, який "вручну викликає сервіс", ніж веб-додаток, що викликає сервіс програмно?
Карлос

2

Я також отримував помилку:

Базове з'єднання було закрито: Не вдалося встановити довірчі відносини для захищеного каналу SSL / TLS.

... за допомогою програми Xamarin Forms для Android, яка намагається запросити ресурси у постачальника API, для якого потрібен TLS 1.3.

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

Це просте перемикання проектів у візуальній студії. Дивіться скріншот нижче.

  • Властивості проекту
  • Параметри Android
  • Розширений
  • Елемент списку
  • Змініть "Реалізацію HttpClient" на "Android"
  • Змініть реалізацію SSL / TLS на "Native TLS 1.2+"

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


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 з правами адміністратора. Я спробував усі вищеперелічені рішення, і це не спрацювало, поки я цього не зробив. Сподіваюся, це економить комусь дорогоцінний час.


Оскільки ви новачок на сайті, ви, можливо, не знаєте, що якщо у відповіді є зелена перевірка поруч, це означає, що вона була прийнята ОП як правильне рішення його / її проблеми. Якщо ви не зможете дати кращу, більш повну відповідь, можливо, краще не подавати іншої відповіді, особливо на таке старе питання.
Сем

4
Ну, у мене теж було питання, і зелена перевірена відповідь зовсім не допомогла. Думав, що я даю альтернативне рішення, яке допомогло мені, але ей, я цього не зроблю наступного разу. Дякую за мінусові бали;) чудовий день.
Лукіан Сатмарейський

0

Ви можете спробувати використовувати пакунок ModernHttpClient Nuget: Завантаживши пакет, ви можете реалізувати його так:

 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-клієнта. Дивіться мою відповідь нижче.
MSC

0

Я погоджуюся з felickz, але також хочу додати приклад для уточнення використання в #. Я використовую 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
Це минає валідацію cert. Ніколи цього не роби у виробництві.
Джон Корнес

Ну, а @JohnKorsnes, якщо Абхієет отримує доступ до якогось загальнодоступного сервера WebAPI, я не думаю, що він може багато зробити на цьому. Це був мій випадок.
Неманья Симович
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.