Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

HTTPS request fails using HttpClient

Tags:

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.

like image 635
hcp Avatar asked Mar 12 '17 10:03

hcp


2 Answers

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.

EDIT 16 Sep 2020

I changed from the = assignment operator to the |= operator so that requests to any other legacy sites which still require SSL will continue to work.

like image 118
Neil Moss Avatar answered Sep 27 '22 20:09

Neil Moss


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;         }     } } 
like image 26
Debro012 Avatar answered Sep 27 '22 22:09

Debro012