#c# #https #webclient #tls1.2
#c# #https #webclient #tls1.2
Вопрос:
Это не один из вопросов «почему мой код не загружается по протоколу HTTPS». Я видел и читал их все много лет назад, и с тех пор я успешно реализовал этот же подход несколько раз.
Мой очень простой код на C #, предназначенный для платформы 4.7.1 и работающий на платформе 4.7.2, пытается загрузить ресурсы через HTTPS. Он следует всем общеизвестным рекомендациям в отношении настройки ServicePointManager
, WebClient
, и WebRequest
. Первоначально я протестировал его с экземпляром Apache 2.4.20 в нашей локальной сети, и он работал нормально. Затем я протестировал его на нескольких других серверах в общедоступном Интернете, и там я столкнулся с ошибкой, которая возникает примерно через 5 секунд после выполнения запроса, хотя страница открывается в браузере мгновенно:
{"The request was aborted: Could not create SSL/TLS secure channel."}
Я проверил сертификаты сервера. Им доверяют Mozilla и Windows, и они открываются в любом известном мне браузере в полностью доверенном режиме. Сайты отвечают по протоколу TLS 1.2. С серверами или их сертификатами нет ничего явно неправильного. Для проверки работоспособности я взял адрес домашней страницы Google https://www.google.ca/?gws_rd=ssl , и она открылась просто отлично. Некоторые сайты открываются, другие сайты нет. Я попытался включить одну версию TLS за раз, и каждая из них работала одинаково хорошо с каждым из рабочих сайтов, тогда как ни один из них не работал с нерабочими сайтами.
Вот мой код. Он написан для использования с базовой аутентификацией, но также протестирован без нее. Это не проблема с аутентификацией.
ServicePointManager.Expect100Continue = true;
ServicePointManager.DefaultConnectionLimit = 9999;
ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls12 | SecurityProtocolType.Tls11; // | SecurityProtocolType.Tls | SecurityProtocolType.Ssl3; // tested by enabling only 1 protocol at a time locally. Each worked.
if (ServicePointManager.ServerCertificateValidationCallback == null) // for testing only
{
ServicePointManager.ServerCertificateValidationCallback = (se, cert, chain, sslerror) => { return true; };
}
using (var client = new WebClientEx()) // Ex adds Timeout and CookieContainer
{
client.CachePolicy = new System.Net.Cache.RequestCachePolicy(System.Net.Cache.RequestCacheLevel.BypassCache);
client.Headers.Add("Cache-Control", "no-cache");
client.Encoding = Encoding.UTF8;
var cc = new CredentialCache();
// Using one or the other, depending on the situation, or none
//cc.Add(url, "Basic", new NetworkCredential(user, pass));
//cc.Add(new Uri(url.GetLeftPart(UriPartial.Authority)), "Digest", new NetworkCredential(user, pass));
client.Credentials = cc;
return client.DownloadData(url);
}
Прежде чем вернуться WebClient
, я попробовал использовать plain HttpRequest
ради его надежного тайм-аута, что важно для моей реализации:
var req = (HttpWebRequest)WebRequest.Create(url);
req.Timeout = (int)timeout.TotalMilliseconds;
req.AutomaticDecompression = DecompressionMethods.GZip | DecompressionMethods.Deflate; // tried with or w/o
var encoded = Convert.ToBase64String(Encoding.GetEncoding("ISO-8859-1").GetBytes($"{user}:{pass}"));
req.Headers.Add("Authorization", "Basic " encoded); // tried with or w/o
var credentialCache = new CredentialCache();
credentialCache.Add(url, "Basic", new NetworkCredential(user, pass)); // tried with or w/o
req.Credentials = credentialCache;
req.PreAuthenticate = true; // tried with or w/o
using (var response = (HttpWebResponse)req.GetResponse())
{
using (var ms = new MemoryStream())
{
response.GetResponseStream().CopyTo(ms);
return ms.ToArray();
}
}
Оба варианта моего кода одинаково работали с моим локальным Apache и Google, но не работали с некоторыми другими сайтами. Я не могу найти никаких ошибок в своем коде. Один из сайтов, который не работал https://jigsaw.w3.org/HTTP/Basic / который отлично открывается в любом браузере. Это сайт, который большинство нашего программного обеспечения использует для модульного тестирования, поэтому я должен создать что-то, что работает с ним на этапе тестирования и с любым произвольным веб-сервером в рабочей среде.
Я включил System.Net
трассировку и сравнил успешные и неудачные попытки загрузки, и разница в том, что одна выполняется с рукопожатием, а другая сразу завершается неудачей. В трассировке нет полезной информации.
Есть идеи, что не так?
Все сервисы и технологии, упомянутые в этом вопросе, находятся вне моего контроля и должны оставаться такими, какими они являются в настоящее время. Я не в силах изменить какие-либо версии или установить какие-либо обновления. Он должен работать так, как сейчас. Если вас это не устраивает, пожалуйста, перейдите к другим вопросам.
Комментарии:
1. Используйте такой инструмент, как Wireshark, для анализа пакетов подтверждения связи, и причина должна быть ясна при сравнении успешных / неудачных сценариев.
Ответ №1:
Ответ: «С кодом все в порядке». Что-то на самом деле не так с теми избранными серверами, с которыми мне приходилось тестировать. Они старые или нишевые, и они не соответствуют требованиям в такой степени, что .NET framework отказывается взаимодействовать с ними. Как только я протестировал с современными совместимыми серверами, все работало как ожидалось, как по HTTP, так и по HTTPS, а также с базовой или дайджест-аутентификацией.