[英]Allowing Untrusted SSL Certificates with HttpClient
I'm struggling to get my Windows 8 application to communicate with my test web API over SSL.我正在努力让我的 Windows 8 应用程序通过 SSL 与我的测试 Web API 进行通信。
It seems that HttpClient/HttpClientHandler does not provide and option to ignore untrusted certificates like WebRequest enables you to (albeit in a "hacky" way with ServerCertificateValidationCallback
).似乎 HttpClient/HttpClientHandler 没有提供和忽略 WebRequest 等不受信任的证书的选项使您能够(尽管使用
ServerCertificateValidationCallback
以“hacky”方式)。
Any help would be much appreciated!任何帮助将非常感激!
A quick and dirty solution is to use the ServicePointManager.ServerCertificateValidationCallback
delegate.一个快速而肮脏的解决方案是使用
ServicePointManager.ServerCertificateValidationCallback
委托。 This allows you to provide your own certificate validation.这允许您提供自己的证书验证。 The validation is applied globally across the whole App Domain.
验证在整个应用程序域中全局应用。
ServicePointManager.ServerCertificateValidationCallback +=
(sender, cert, chain, sslPolicyErrors) => true;
I use this mainly for unit testing in situations where I want to run against an endpoint that I am hosting in process and am trying to hit it with a WCF client or the HttpClient
.在我想针对正在托管的端点运行并尝试使用WCF 客户端或
HttpClient
命中它的情况下,我主要将其用于单元测试。
For production code you may want more fine grained control and would be better off using the WebRequestHandler
and its ServerCertificateValidationCallback
delegate property (See dtb's answer below ).对于生产代码,您可能需要更细粒度的控制,最好使用
WebRequestHandler
及其ServerCertificateValidationCallback
委托属性(请参阅下面的 dtb 答案)。 Or ctacke answer using the HttpClientHandler
.或 ctacke 使用
HttpClientHandler
回答。 I am preferring either of these two now even with my integration tests over how I used to do it unless I cannot find any other hook.我现在更喜欢这两个中的任何一个,即使我的集成测试胜过我过去的做法,除非我找不到任何其他钩子。
If you're attempting to do this in a .NET Standard library, here's a simple solution, with all of the risks of just returning true
in your handler.如果您尝试在 .NET Standard 库中执行此操作,这里有一个简单的解决方案,其中存在仅在处理程序中返回
true
的所有风险。 I leave safety up to you.我把安全留给你。
var handler = new HttpClientHandler();
handler.ClientCertificateOptions = ClientCertificateOption.Manual;
handler.ServerCertificateCustomValidationCallback =
(httpRequestMessage, cert, cetChain, policyErrors) =>
{
return true;
};
var client = new HttpClient(handler);
Have a look at the WebRequestHandler Class and its ServerCertificateValidationCallback Property :查看WebRequestHandler 类及其ServerCertificateValidationCallback 属性:
using (var handler = new WebRequestHandler())
{
handler.ServerCertificateValidationCallback = ...
using (var client = new HttpClient(handler))
{
...
}
}
If you are using System.Net.Http.HttpClient
I believe correct pattern is如果您使用
System.Net.Http.HttpClient
我相信正确的模式是
var handler = new HttpClientHandler()
{
ServerCertificateCustomValidationCallback = HttpClientHandler.DangerousAcceptAnyServerCertificateValidator
};
var http = new HttpClient(handler);
var res = http.GetAsync(url);
Or you can use for the HttpClient in the Windows.Web.Http
namespace:或者您可以使用
Windows.Web.Http
命名空间中的HttpClient :
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)) {
...
}
Most answers here suggest to use the typical pattern:这里的大多数答案都建议使用典型模式:
using (var httpClient = new HttpClient())
{
// do something
}
because of the IDisposable interface.因为 IDisposable 接口。 Please don't!
请不要!
Microsoft tells you why:微软告诉你原因:
And here you can find a detailed analysis whats going on behind the scenes: You're using HttpClient wrong and it is destabilizing your software在这里您可以找到幕后发生的详细分析: 您使用 HttpClient 错误,它正在破坏您的软件
Official Microsoft link: HttpClient微软官方链接: HttpClient
HttpClient is intended to be instantiated once and re-used throughout the life of an application.
HttpClient 旨在被实例化一次并在应用程序的整个生命周期中重复使用。 Instantiating an HttpClient class for every request will exhaust the number of sockets available under heavy loads.
为每个请求实例化一个 HttpClient 类将耗尽重负载下可用的套接字数量。 This will result in SocketException errors.
这将导致 SocketException 错误。
Regarding your SSL question and based on Improper Instantiation antipattern # How to fix the problem关于您的 SSL 问题并基于不正确的实例化反模式 # 如何解决问题
Here is your pattern:这是你的模式:
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;
}
}
}
With Windows 8.1, you can now trust invalid SSL certs.在 Windows 8.1 中,您现在可以信任无效的 SSL 证书。 You have to either use the Windows.Web.HttpClient or if you want to use the System.Net.Http.HttpClient, you can use the message handler adapter I wrote: http://www.nuget.org/packages/WinRtHttpClientHandler
您必须使用 Windows.Web.HttpClient,或者如果您想使用 System.Net.Http.HttpClient,您可以使用我写的消息处理程序适配器: http ://www.nuget.org/packages/WinRtHttpClientHandler
Docs are on the GitHub: https://github.com/onovotny/WinRtHttpClientHandler文档在 GitHub 上: https ://github.com/onovotny/WinRtHttpClientHandler
If this is for a Windows Runtime application, then you have to add the self-signed certificate to the project and reference it in the appxmanifest.如果这是针对 Windows 运行时应用程序,则必须将自签名证书添加到项目并在 appxmanifest 中引用它。
The docs are here: http://msdn.microsoft.com/en-us/library/windows/apps/hh465031.aspx文档在这里:http: //msdn.microsoft.com/en-us/library/windows/apps/hh465031.aspx
Same thing if it's from a CA that's not trusted (like a private CA that the machine itself doesn't trust) -- you need to get the CA's public cert, add it as content to the app then add it to the manifest.如果它来自不受信任的 CA(例如机器本身不信任的私有 CA),则同样如此——您需要获取 CA 的公共证书,将其作为内容添加到应用程序中,然后将其添加到清单中。
Once that's done, the app will see it as a correctly signed cert.完成后,应用程序会将其视为正确签名的证书。
I found an example in this Kubernetes client where they were using X509VerificationFlags.AllowUnknownCertificateAuthority to trust self-signed root certificates.我在这个Kubernetes 客户端中找到了一个示例,他们使用X509VerificationFlags.AllowUnknownCertificateAuthority来信任自签名根证书。 I slightly reworked their example to work with our own PEM encoded root certificates.
我稍微修改了他们的示例以使用我们自己的 PEM 编码根证书。 Hopefully this helps someone.
希望这可以帮助某人。
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>();
}
}
I don't have an answer, but I do have an alternative.我没有答案,但我有一个选择。
If you use Fiddler2 to monitor traffic AND enable HTTPS Decryption, your development environment will not complain.如果您使用Fiddler2监控流量并启用 HTTPS 解密,您的开发环境不会抱怨。 This will not work on WinRT devices, such as Microsoft Surface, because you cannot install standard apps on them.
这不适用于 WinRT 设备,例如 Microsoft Surface,因为您无法在它们上安装标准应用程序。 But your development Win8 computer will be fine.
但是你开发的Win8电脑就可以了。
To enable HTTPS encryption in Fiddler2, go to Tools > Fiddler Options > HTTPS (Tab) > Check "Decrypt HTTPS Traffic" .要在 Fiddler2 中启用 HTTPS 加密,请转到Tools > Fiddler Options > HTTPS (Tab) > Check "Decrypt HTTPS Traffic" 。
I'm going to keep my eye on this thread hoping for someone to have an elegant solution.我将密切关注这个线程,希望有人能有一个优雅的解决方案。
I found an example online which seems to work well:我在网上找到了一个似乎效果很好的例子:
First you create a new ICertificatePolicy首先你创建一个新的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;
}
}
Then just use this prior to sending your http request like so:然后在发送您的 http 请求之前使用它,如下所示:
System.Net.ServicePointManager.CertificatePolicy = new MyPolicy();
http://www.terminally-incoherent.com/blog/2008/05/05/send-a-https-post-request-with-c/ http://www.terminally-incoherent.com/blog/2008/05/05/send-a-https-post-request-with-c/
Use this in Startup.cs for ASP.NET Core project:在 ASP.NET Core 项目的Startup.cs中使用它:
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;
});
}
For Xamarin Android this was the only solution that worked for me: another stack overflow post对于 Xamarin Android,这是唯一对我有用的解决方案:另一个堆栈溢出帖子
If you are using AndroidClientHandler
, you need to supply a SSLSocketFactory
and a custom implementation of HostnameVerifier
with all checks disabled.如果您使用的是
AndroidClientHandler
,则需要提供SSLSocketFactory
和HostnameVerifier
的自定义实现,并禁用所有检查。 To do this, you'll need to subclass AndroidClientHandler
and override the appropriate methods.为此,您需要
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();
}
}
And then接着
var httpClient = new System.Net.Http.HttpClient(new InsecureAndroidClientHandler());
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.