Разрешение ненадежных SSL-сертификатов с помощью HttpClient
Я изо всех сил пытаюсь заставить мое приложение Windows 8 связываться с моим тестовым веб-API по SSL.
Кажется, что HttpClient/HttpClientHandler не предоставляет, а опция игнорировать недоверенные сертификаты, такие как WebRequest, позволяет вам (хотя и "хакерским" способом ServerCertificateValidationCallback
).
Любая помощь приветствуется!
13 ответов
В Windows 8.1 теперь вы можете доверять недействительным сертификатам SSL. Вы должны либо использовать Windows.Web.HttpClient, либо, если вы хотите использовать System.Net.Http.HttpClient, вы можете использовать адаптер обработчика сообщений, который я написал: http://www.nuget.org/packages/WinRtHttpClientHandler
Документы находятся на GitHub: https://github.com/onovotny/WinRtHttpClientHandler
Быстрое и грязное решение заключается в использовании ServicePointManager.ServerCertificateValidationCallback
делегировать. Это позволяет вам предоставить собственную проверку сертификата. Проверка применяется глобально по всему домену приложения.
ServicePointManager.ServerCertificateValidationCallback +=
(sender, cert, chain, sslPolicyErrors) => true;
Я использую это главным образом для модульного тестирования в ситуациях, когда я хочу запустить конечную точку, которую я размещаю в процессе, и пытаюсь выполнить ее с клиентом WCF или HttpClient
,
Для производственного кода вам может потребоваться более детальный контроль, и было бы лучше использовать WebRequestHandler
И его ServerCertificateValidationCallback
делегировать свойство (см . ответ dtb ниже). Или ответьте, используя HttpClientHandler
, Сейчас я предпочитаю любой из этих двух, даже с моими интеграционными тестами, вместо того, как я это делал, если я не могу найти какой-либо другой хук.
Если вы пытаетесь сделать это в стандартной библиотеке.NET, вот простое решение со всеми рисками простого возврата true
в вашем обработчике. Я оставляю безопасность на ваше усмотрение.
var handler = new HttpClientHandler();
handler.ClientCertificateOptions = ClientCertificateOption.Manual;
handler.ServerCertificateCustomValidationCallback =
(httpRequestMessage, cert, cetChain, policyErrors) =>
{
return true;
};
var client = new HttpClient(handler);
Взгляните на класс WebRequestHandler и его свойство ServerCertificateValidationCallback:
using (var handler = new WebRequestHandler())
{
handler.ServerCertificateValidationCallback = ...
using (var client = new HttpClient(handler))
{
...
}
}
Если вы используете System.Net.Http.HttpClient
Я считаю, что правильный образец
var handler = new HttpClientHandler()
{
ServerCertificateCustomValidationCallback = HttpClientHandler.DangerousAcceptAnyServerCertificateValidator
};
var http = new HttpClient(handler);
var res = http.GetAsync(url);
Большинство ответов здесь предлагают использовать типичный шаблон:
using (var httpClient = new HttpClient())
{
// do something
}
из-за IDisposable интерфейса. Пожалуйста, не надо!
Microsoft говорит вам, почему:
- https://docs.microsoft.com/en-us/azure/architecture/antipatterns/improper-instantiation
- https://blogs.msdn.microsoft.com/henrikn/2012/08/07/httpclient-httpclienthandler-and-webrequesthandler-explained/
И здесь вы можете найти подробный анализ того, что происходит за кулисами: https://aspnetmonsters.com/2016/08/2016-08-27-httpclientwrong/
Относительно вашего вопроса SSL и на основе https://docs.microsoft.com/en-us/azure/architecture/antipatterns/improper-instantiation/
Вот ваш шаблон:
class HttpInterface
{
// https://docs.microsoft.com/en-us/azure/architecture/antipatterns/improper-instantiation/#how-to-fix-the-problem
// https://docs.microsoft.com/en-us/dotnet/api/system.net.http.httpclient#remarks
private static readonly HttpClient client;
// static initialize
static HttpInterface()
{
// choose one of these depending on your framework
// HttpClientHandler is an HttpMessageHandler with a common set of properties
var handler = new HttpClientHandler();
{
ServerCertificateCustomValidationCallback = delegate { return true; },
};
// derives from HttpClientHandler but adds properties that generally only are available on full .NET
var handler = new WebRequestHandler()
{
ServerCertificateValidationCallback = delegate { return true; },
ServerCertificateCustomValidationCallback = delegate { return true; },
};
client = new HttpClient(handler);
}
.....
// in your code use the static client to do your stuff
var jsonEncoded = new StringContent(someJsonString, Encoding.UTF8, "application/json");
// here in sync
using (HttpResponseMessage resultMsg = client.PostAsync(someRequestUrl, jsonEncoded).Result)
{
using (HttpContent respContent = resultMsg.Content)
{
return respContent.ReadAsStringAsync().Result;
}
}
}
Или вы можете использовать для HttpClient в Windows.Web.Http
Пространство имен:
var filter = new HttpBaseProtocolFilter();
#if DEBUG
filter.IgnorableServerCertificateErrors.Add(ChainValidationResult.Expired);
filter.IgnorableServerCertificateErrors.Add(ChainValidationResult.Untrusted);
filter.IgnorableServerCertificateErrors.Add(ChainValidationResult.InvalidName);
#endif
using (var httpClient = new HttpClient(filter)) {
...
}
Используйте это в Startup.cs для проекта ASP.NET Core:
public void ConfigureServices(IServiceCollection services)
{
// other code
services
.AddHttpClient<IMyService, MyService>(client =>
{
client.BaseAddress = new Uri(myConfiguration.BaseUrl);
})
.ConfigurePrimaryHttpMessageHandler(() =>
{
// Allowing Untrusted SSL Certificates
var handler = new HttpClientHandler();
handler.ClientCertificateOptions = ClientCertificateOption.Manual;
handler.ServerCertificateCustomValidationCallback =
(httpRequestMessage, cert, cetChain, policyErrors) => true;
return handler;
});
}
Я нашел пример в этом клиенте Kubernetes, где они использовали X509VerificationFlags.AllowUnknownCertificateAuthority для доверия самозаверяющим самозаверяющим корневым сертификатам. Я немного переработал их пример для работы с нашими собственными корневыми сертификатами в кодировке PEM. Надеюсь, это кому-то поможет.
namespace Utils
{
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net.Security;
using System.Security.Cryptography.X509Certificates;
/// <summary>
/// Verifies that specific self signed root certificates are trusted.
/// </summary>
public class HttpClientHandler : System.Net.Http.HttpClientHandler
{
/// <summary>
/// Initializes a new instance of the <see cref="HttpClientHandler"/> class.
/// </summary>
/// <param name="pemRootCerts">The PEM encoded root certificates to trust.</param>
public HttpClientHandler(IEnumerable<string> pemRootCerts)
{
foreach (var pemRootCert in pemRootCerts)
{
var text = pemRootCert.Trim();
text = text.Replace("-----BEGIN CERTIFICATE-----", string.Empty);
text = text.Replace("-----END CERTIFICATE-----", string.Empty);
this.rootCerts.Add(new X509Certificate2(Convert.FromBase64String(text)));
}
this.ServerCertificateCustomValidationCallback = this.VerifyServerCertificate;
}
private bool VerifyServerCertificate(
object sender,
X509Certificate certificate,
X509Chain chain,
SslPolicyErrors sslPolicyErrors)
{
// If the certificate is a valid, signed certificate, return true.
if (sslPolicyErrors == SslPolicyErrors.None)
{
return true;
}
// If there are errors in the certificate chain, look at each error to determine the cause.
if ((sslPolicyErrors & SslPolicyErrors.RemoteCertificateChainErrors) != 0)
{
chain.ChainPolicy.RevocationMode = X509RevocationMode.NoCheck;
// add all your extra certificate chain
foreach (var rootCert in this.rootCerts)
{
chain.ChainPolicy.ExtraStore.Add(rootCert);
}
chain.ChainPolicy.VerificationFlags = X509VerificationFlags.AllowUnknownCertificateAuthority;
var isValid = chain.Build((X509Certificate2)certificate);
var rootCertActual = chain.ChainElements[chain.ChainElements.Count - 1].Certificate;
var rootCertExpected = this.rootCerts[this.rootCerts.Count - 1];
isValid = isValid && rootCertActual.RawData.SequenceEqual(rootCertExpected.RawData);
return isValid;
}
// In all other cases, return false.
return false;
}
private readonly IList<X509Certificate2> rootCerts = new List<X509Certificate2>();
}
}
Если это для приложения времени выполнения Windows, то вы должны добавить самоподписанный сертификат в проект и сослаться на него в appxmanifest.
Документы находятся здесь: http://msdn.microsoft.com/en-us/library/windows/apps/hh465031.aspx
То же самое, если он принадлежит ЦС, которому не доверяют (например, частному ЦС, которому сама машина не доверяет) - вам необходимо получить публичный сертификат ЦС, добавить его в качестве содержимого в приложение, а затем добавить его в манифест.
Как только это будет сделано, приложение увидит его как правильно подписанный сертификат.
У меня нет ответа, но у меня есть альтернатива.
Если вы используете Fiddler2 для мониторинга трафика и включения расшифровки HTTPS, ваша среда разработки не будет жаловаться. Это не будет работать на устройствах WinRT, таких как Microsoft Surface, потому что вы не можете устанавливать на них стандартные приложения. Но ваш компьютер для разработки Win8 будет в порядке.
Чтобы включить шифрование HTTPS в Fiddler2, перейдите в Инструменты> Параметры Fiddler> HTTPS (вкладка) > Установите флажок "Дешифровать трафик HTTPS".
Я буду следить за этой темой, надеясь, что кто-то найдет элегантное решение.
Я нашел пример онлайн, который, кажется, работает хорошо:
Сначала вы создаете новый ICertificatePolicy
using System.Security.Cryptography.X509Certificates;
using System.Net;
public class MyPolicy : ICertificatePolicy
{
public bool CheckValidationResult(ServicePoint srvPoint, X509Certificate certificate, WebRequest request,
int certificateProblem)
{
//Return True to force the certificate to be accepted.
return true;
}
}
Затем просто используйте это перед отправкой вашего http запроса следующим образом:
System.Net.ServicePointManager.CertificatePolicy = new MyPolicy();
http://www.terminally-incoherent.com/blog/2008/05/05/send-a-https-post-request-with-c/
Для Xamarin Android это было единственное решение, которое сработало для меня: еще один пост о переполнении стека
Если вы используете, вам необходимо предоставить
SSLSocketFactory
и кастомная реализация
HostnameVerifier
со всеми отключенными проверками. Для этого вам нужно создать подкласс
AndroidClientHandler
и переопределите соответствующие методы.
internal class BypassHostnameVerifier : Java.Lang.Object, IHostnameVerifier
{
public bool Verify(string hostname, ISSLSession session)
{
return true;
}
}
internal class InsecureAndroidClientHandler : AndroidClientHandler
{
protected override SSLSocketFactory ConfigureCustomSSLSocketFactory(HttpsURLConnection connection)
{
return SSLCertificateSocketFactory.GetInsecure(1000, null);
}
protected override IHostnameVerifier GetSSLHostnameVerifier(HttpsURLConnection connection)
{
return new BypassHostnameVerifier();
}
}
А потом
var httpClient = new System.Net.Http.HttpClient(new InsecureAndroidClientHandler());