HttpClient使用客户端证书时出错
我的问题基本上与这里描述的一致 这里。
简而言之:对需要客户端证书的服务器发起的网络请求,在我使用PowerShell或 Postman时可以工作,或者在把BurpSuite作为代理并配置了客户端证书时也可以工作。当我使用 HttpClient 搭配一个 HttpClientHandler 时则无法工作。错误信息是:
System.Net.Http.HttpRequestException: An error occurred while sending the request.
System.Net.Http.HttpIOException: The response ended prematurely. (ResponseEnded)at System.Net.Http.HttpConnection.SendAsync(HttpRequestMessage request, Boolean async, CancellationToken cancellationToken)
--- End of inner exception stack trace ---
at System.Net.Http.HttpConnection.SendAsync(HttpRequestMessage request, Boolean async, CancellationToken cancellationToken)
at System.Net.Http.HttpConnectionPool.SendWithVersionDetectionAndRetryAsync(HttpRequestMessage request, Boolean async, Boolean doRequestAuth, CancellationToken cancellationToken)
at System.Net.Http.DiagnosticsHandler.SendAsyncCore(HttpRequestMessage request, Boolean async, CancellationToken cancellationToken)
at System.Net.Http.RedirectHandler.SendAsync(HttpRequestMessage request, Boolean async, CancellationToken cancellationToken)
at System.Net.Http.SocketsHttpHandler.g__CreateHandlerAndSendAsync|115_0(HttpRequestMessage request, CancellationToken cancellationToken)
at System.Net.Http.HttpClient.g__Core|83_0(HttpRequestMessage request, HttpCompletionOption completionOption, CancellationTokenSource cts, Boolean disposeCts, CancellationTokenSource pendingRequestsCts, CancellationToken originalCancellationToken)
at Program.Main(String[] args) in D:\repos\ConsoleApp1\Program.cs:line 39
链接的帖子中的答案是用 HttpClientHandler 替换为 SocketsHttpHandler,在一个简单的控制台应用程序中这对我有效。
唯一的问题是,实际上我正在处理一个使用RestSharp的代码库,而RestSharp在内部使用 HttpClientHandler。
我的问题是:
- 我能否绕过RestSharp使用的
HttpMessageHandler? - 有没有办法让它与
HttpClientHandler一起工作?
FWIW这是我用于测试的代码:
using var store = new X509Store(StoreLocation.LocalMachine);
store.Open(OpenFlags.ReadOnly);
var certs = store.Certificates.Find(X509FindType.FindBySubjectDistinguishedName, "CN=example.com", true);
var cert = certs[0];
// I also tried this, makes no difference
// var cert = X509CertificateLoader.LoadPkcs12FromFile(@"C:\data\cert.pfx", "password");
using var handler = new HttpClientHandler();
handler.ClientCertificates.Add(cert);
// The following 2 lines make it work
// using var handler = new SocketsHttpHandler();
// handler.SslOptions.ClientCertificates = [cert];
using var client = new HttpClient(handler);
var response = await client.GetAsync("https://example.com/path"); // <== exception is raised here
解决方案
也许你应该试试这个:
var options = new RestClientOptions("https://example.com/endpoint")
{
ConfigureMessageHandler = _ => new SocketsHttpHandler()
{
SslOptions = new SslClientAuthenticationOptions()
{
ClientCertificates = cert
}
// more config options for SocketsHttpHandler can be added, if any
}
};
var client = new RestClient(options); // originally "https://example.com/endpoint"
说明:
- RestSharp的
RestClient构造函数可以接受一个RestClientOptions对象,而不是它的常规用法,即端点URL。这样的RestClientOptions对象本身具有一个ConfigureMessageHandler属性,允许你更改处理程序,它应该是一个返回HttpMessageHandler类对象的lambda,或者它的派生类对象。而SocketsHttpHandler恰好是它的派生类,所以我们有办法对其进行挂钩。 - 我自己没有亲自试过,看看它是否能解决你所说的“RespondEnded”错误,因为我确实无法访问你的系统。如果确实使用
SocketsHttpHandler能像你在直接用它的简单控制台程序中测试时那样解决问题,那么值得你去尝试。