C# 使用 HttpClient 允许不受信任的 SSL 证书
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/12553277/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Allowing Untrusted SSL Certificates with HttpClient
提问by Jamie
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 使您能够(尽管以“hacky”方式使用ServerCertificateValidationCallback)。
Any help would be much appreciated!
任何帮助将非常感激!
采纳答案by Claire Novotny
With Windows 8.1, you can now trust invalid SSL certs. 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 8.1,您现在可以信任无效的 SSL 证书。您必须使用 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
回答by dtb
Have a look at the WebRequestHandler Classand its ServerCertificateValidationCallback Property:
看看WebRequestHandler 类和它的ServerCertificateValidationCallback 属性:
using (var handler = new WebRequestHandler())
{
handler.ServerCertificateValidationCallback = ...
using (var client = new HttpClient(handler))
{
...
}
}
回答by Claire Novotny
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.
完成后,应用程序会将其视为正确签名的证书。
回答by Laith
I don't have an answer, but I do have an alternative.
我没有答案,但我有一个选择。
If you use Fiddler2to monitor traffic AND enable HTTPS Decryption, your development environment will not complain. This will not work on WinRT devices, such as Microsoft Surface, because you cannot install standard apps on them. But your development Win8 computer will be fine.
如果您使用Fiddler2监控流量并启用 HTTPS 解密,您的开发环境将不会抱怨。这不适用于 WinRT 设备,例如 Microsoft Surface,因为您无法在这些设备上安装标准应用程序。但是你开发的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.
我将密切关注这个线程,希望有人有一个优雅的解决方案。
回答by Bronumski
A quick and dirty solution is to use the ServicePointManager.ServerCertificateValidationCallbackdelegate. This allows you to provide your own certificate validation. The validation is applied globally across the whole App Domain.
一个快速而肮脏的解决方案是使用ServicePointManager.ServerCertificateValidationCallback委托。这允许您提供自己的证书验证。验证在整个应用程序域中全局应用。
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 clientor the HttpClient.
我主要使用它来进行单元测试,在这种情况下,我想针对正在托管的端点运行并尝试使用WCF 客户端或HttpClient.
For production code you may want more fine grained control and would be better off using the WebRequestHandlerand its ServerCertificateValidationCallbackdelegate property (See dtb's answer below). Or ctacke answerusing the 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.
对于生产代码,您可能需要更细粒度的控制,最好使用WebRequestHandler及其ServerCertificateValidationCallback委托属性(请参阅下面的 dtb 答案)。或ctacke答案使用HttpClientHandler。除非我找不到任何其他钩子,否则我现在更喜欢这两个中的任何一个,即使是我的集成测试,而不是我以前的做法。
回答by dschüs?
Or you can use for the HttpClientin the Windows.Web.Httpnamespace:
或者你可以使用的的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)) {
...
}
回答by TombMedia
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/
回答by ctacke
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 truein your handler. I leave safety up to you.
如果您尝试在 .NET Standard 库中执行此操作,这里有一个简单的解决方案,但会承担true在处理程序中返回的所有风险。我把安全留给你。
var handler = new HttpClientHandler();
handler.ClientCertificateOptions = ClientCertificateOption.Manual;
handler.ServerCertificateCustomValidationCallback =
(httpRequestMessage, cert, cetChain, policyErrors) =>
{
return true;
};
var client = new HttpClient(handler);
回答by Bernhard
Most answers here suggest to use the typical pattern:
这里的大多数答案都建议使用典型模式:
using (var httpClient = new HttpClient())
{
// do something
}
because of the IDisposable interface. Please don't!
因为 IDisposable 接口。请不要!
Microsoft tells you why:
微软告诉你原因:
- 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://docs.microsoft.com/en-us/azure/architecture/antipatterns/improper-instantiation
- https://blogs.msdn.microsoft.com/henrikn/2012/08/07/httpclient-httpclienthandler-and-webrequesthandler-explained/
And here you can find a detailed analysis whats going on behind the scenes: https://aspnetmonsters.com/2016/08/2016-08-27-httpclientwrong/
在这里你可以找到幕后发生的详细分析:https: //aspnetmonsters.com/2016/08/2016-08-27-httpclientwrong/
Regarding your SSL question and based on https://docs.microsoft.com/en-us/azure/architecture/antipatterns/improper-instantiation/#how-to-fix-the-problem
关于您的 SSL 问题并基于https://docs.microsoft.com/en-us/azure/architecture/antipatterns/improper-instantiation/#how-to-fix-the-problem
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;
}
}
}
回答by PaulB
I found an example in this Kubernetes clientwhere they were using X509VerificationFlags.AllowUnknownCertificateAuthorityto trust self-signed self-signed root certificates. I slightly reworked their example to work with our own PEM encoded root certificates. Hopefully this helps someone.
我在这个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>();
}
}

