我在努力让我的Windows 8应用程序通过SSL与我的测试Web API通信。
似乎HttpClient / HttpClientHandler没有提供忽略不受信任证书的选项,就像WebRequest允许您做的那样(虽然需要使用ServerCertificateValidationCallback
的“hacky”方式)。
非常感谢您的帮助!
我在努力让我的Windows 8应用程序通过SSL与我的测试Web API通信。
似乎HttpClient / HttpClientHandler没有提供忽略不受信任证书的选项,就像WebRequest允许您做的那样(虽然需要使用ServerCertificateValidationCallback
的“hacky”方式)。
非常感谢您的帮助!
ServicePointManager.ServerCertificateValidationCallback
委托。这允许您提供自己的证书验证。该验证在整个应用程序域全局应用。ServicePointManager.ServerCertificateValidationCallback +=
(sender, cert, chain, sslPolicyErrors) => true;
我主要用于单元测试,当我想要在进程中托管终结点并尝试使用WCF客户端或HttpClient
访问时,我会使用它。
对于生产代码,您可能需要更精细的控制,并最好使用WebRequestHandler
及其ServerCertificateValidationCallback
委托属性(请参见下面的dtb的答案)。或者使用ctacke的答案和HttpClientHandler
。现在,即使在我的集成测试中,除非我找不到其他的钩子,否则我都更喜欢使用这两个中的任何一个。
ServicePointManager.GetServicePoint(Uri)
(参见文档)获取仅适用于该URI调用的服务点。然后,你可以基于该子集设置属性和处理事件。 - oatsoda如果你想在.NET Standard库中尝试进行此操作,这里有一个简单的解决方案,并且存在仅仅返回true
的所有风险。安全问题由你自行决定。
var handler = new HttpClientHandler();
handler.ClientCertificateOptions = ClientCertificateOption.Manual;
handler.ServerCertificateCustomValidationCallback =
(httpRequestMessage, cert, cetChain, policyErrors) =>
{
return true;
};
var client = new HttpClient(handler);
ClientCertificateOptions
属性并不是必要的。该属性涉及使用客户端证书,以便服务器可以识别客户端。从文档中可以看到:“获取或设置一个值,该值指示证书是否自动从证书存储区中选择,还是允许调用者传递特定的客户端证书。”(https://learn.microsoft.com/en-us/dotnet/api/system.net.http.httpclienthandler.clientcertificateoptions?view=net-7.0) - Paul Wheeler请查看 WebRequestHandler类 及其 ServerCertificateValidationCallback属性:
using (var handler = new WebRequestHandler())
{
handler.ServerCertificateValidationCallback = ...
using (var client = new HttpClient(handler))
{
...
}
}
HttpClientHandler
? - Kiquenet如果您正在使用 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接口的存在。 请不要这样做!
微软告诉你原因:
在这里,您可以找到详细分析背后正在发生的事情: 您正在错误地使用HttpClient,它正在破坏您的软件
官方微软链接:HttpClient
HttpClient旨在实例化一次并在应用程序的整个生命周期中重复使用。在每个请求中实例化HttpClient类会在重载下耗尽可用的套接字数量。这将导致SocketException错误。
关于您的SSL问题,基于错误实例化反模式 # 如何解决问题
这是您的模式:
class HttpInterface
{
// https://learn.microsoft.com/en-us/azure/architecture/antipatterns/improper-instantiation/#how-to-fix-the-problem
// https://learn.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;
}
}
}
您也可以使用位于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)) {
...
}
System.Net.Http
、System.Web
和 Windows.Web.Http
吗? - Kiquenet在 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;
});
}
在Windows 8.1中,您现在可以信任无效的SSL证书。您需要使用Windows.Web.HttpClient或者如果想要使用System.Net.Http.HttpClient,则可以使用我编写的消息处理程序适配器: http://www.nuget.org/packages/WinRtHttpClientHandler
文档在GitHub上: https://github.com/onovotny/WinRtHttpClientHandler
我在这个 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>();
}
}