问题
I am using the following code and get HttpRequestException
exception:
using (var handler = new HttpClientHandler())
{
handler.ClientCertificateOptions = ClientCertificateOption.Manual;
handler.SslProtocols = SslProtocols.Tls12;
handler.ClientCertificates.Add(new X509Certificate2(@"C:\certificates\cert.pfx"));
// I also tried to add another certificates that was provided to https access
// by administrators of the site, but it still doesn't work.
//handler.ClientCertificates.Add(new X509Certificate2(@"C:\certificates\cert.crt"));
//handler.ClientCertificates.Add(new X509Certificate2(@"C:\certificates\cert_ca.crt"));
using (var client = new HttpClient(handler))
{
var response = client.GetAsync("https://someurl.com/api.php?arg1=some&arg2=test").GetAwaiter().GetResult();
// ^ HttpRequestException: An error occurred while sending the request.
}
}
The exception:
WinHttpException: A security error occurred
System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
System.Runtime.CompilerServices.ConfiguredTaskAwaitable+ConfiguredTaskAwaiter.GetResult()
System.Net.Http.WinHttpHandler+<StartRequest>d__105.MoveNext()
HttpRequestException: An error occurred while sending the request.
System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
System.Runtime.CompilerServices.ConfiguredTaskAwaitable+ConfiguredTaskAwaiter.GetResult()
System.Net.Http.HttpClient+<FinishSendAsync>d__58.MoveNext()
System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
System.Runtime.CompilerServices.TaskAwaiter.GetResult()
MyApp.Web.Controllers.HomeController.Test() in HomeController.cs
var response = client.GetAsync("https://someurl.com/api.php?arg1=some&arg2=test").GetAwaiter().GetResult();
lambda_method(Closure , object , Object[] )
Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker+<InvokeActionMethodAsync>d__27.MoveNext()
I also tried to export the same certificates to Windows Certificate Store and use it via Google Chrome and it works fine (browser asked me to confirm installed certificate and then loaded the resource).
Why it's not working in my code?
UPDATED
I also tried to add callback to validate the certificate:
handler.ServerCertificateCustomValidationCallback = (message, certificate2, arg3, arg4) =>
{
// I set a breakpoint to this point but it is not catched.
return true;
};
UPDATED2
The certificate is used SHA-1. Neil Moss is mentioned in the comments that support for SHA1 certs is being withdrawn. If it is the real reason why it is not working, is there workaround for it?
SOLUTION
Thank you Neil Moss for solution. He proposed to use Tls flag for SSL protocol.
handler.SslProtocols = SslProtocols.Tls12 | SslProtocols.Tls11 | SslProtocols.Tls;
But it also required the following:
handler.ServerCertificateCustomValidationCallback = (message, cert, chain, errors) => true;
After I added this one it works fine.
回答1:
According to this SO post, you must enable TLS1.2 with ServicePointManager.
System.Net.ServicePointManager.SecurityProtocol =
SecurityProtocolType.Tls12 |
SecurityProtocolType.Tls11 |
SecurityProtocolType.Tls; // comparable to modern browsers
Also noteworthy, the MSDN documentation for ServicePointManager.SecurityProtocols property makes this statement:
The .NET Framework 4.6 includes a new security feature that blocks insecure cipher and hashing algorithms for connections.
which suggests that some form of SHA1 block might be in place.
回答2:
This was a very helpful document. For ASP.NET Core 2.0, the answer was applied as follows (the result was successful):
using (var handler = new HttpClientHandler())
{
handler.ServerCertificateCustomValidationCallback = (sender, certificate, chain, sslPolicyErrors) => true;
handler.SslProtocols = SslProtocols.Tls12 | SslProtocols.Tls11 | SslProtocols.Tls;
using (HttpClient client = new HttpClient(handler))
{
string requestObjJson = requestObj.ToJson();
var address = new Uri($"https://yourcompany.com/");
string token = GetToken();
client.BaseAddress = address;
client.DefaultRequestHeaders.Accept.Clear();
client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", token);
var contentData = new StringContent(requestObjJson, System.Text.Encoding.UTF8, "application/json");
using (var response = await client.PostAsync("yourcompany/new-employee", contentData))
{
var content = response.Content.ReadAsStringAsync();
var taskResult = content.Result;
JObject resultObj = JObject.Parse(taskResult);
return resultObj;
}
}
}
来源:https://stackoverflow.com/questions/42746190/https-request-fails-using-httpclient