1

I'm attempting to call a third-party's HTTP-based API from an ASP.NET Core (2.0.2) Web API (within a Controller).

It succeeds when running in a non-Azure environment (Windows 7 Boxes). It fails when running in an App Service within Azure with the error "The client certificate credentials were not recognized". The code and certificate are identical between the environments. Both are running in IIS.

I've whittled the code down to isolate my issue as follows. And I've changed the passwords and URL details here for obvious reasons.

[HttpGet] public async Task<IActionResult> Get() { string response = null; using (HttpClientHandler handler = new HttpClientHandler()) { handler.ClientCertificateOptions = ClientCertificateOption.Manual; handler.SslProtocols = SslProtocols.Tls12; handler.ClientCertificates.Add(new X509Certificate2("Certificate/Cert.pfx", "<password-removed>")); using (HttpClient client = new HttpClient(handler)) { using (HttpResponseMessage responseMessage = await client.PostAsync("https://some-domain.com/action.asp?sourceid=123&key=10101", null)) { response = await responseMessage.Content.ReadAsStringAsync(); } } } return Ok(response); } 

The resulting Exception and callstack are as follows:

System.Net.Http.HttpRequestException: An error occurred while sending the request. ---> System.Net.Http.WinHttpException: The client certificate credentials were not recognized at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw() at System.Threading.Tasks.RendezvousAwaitable1.GetResult() at System.Net.Http.WinHttpHandler.d__105.MoveNext() --- End of inner exception stack trace --- at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw() at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task) at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task) at System.Runtime.CompilerServices.ConfiguredTaskAwaitable1.ConfiguredTaskAwaiter.GetResult() at System.Net.Http.HttpClient.d__58.MoveNext() --- End of stack trace from previous location where exception was thrown ---\TestController.cs:line 32

The other interesting thing is that I can successfully perform this POST in the Azure App Service Console using CURL as follows. The .pem file used here was generated from the .pfx file used in the C# code.

curl -s --data "" --cert .\Cert.pem:<password-removed> "https://some-domain.com/action.asp?sourceid=123&key=10101" 

Update

I've refactored the code base to simplify the duplication of the problem. I'm now using a console application which I'm uploading to the App Service in Azure via Kudu and running in the Kudu Console. It fails with the same exception as above.

static void Main(string[] args) { Console.WriteLine("Starting v3..."); string response = null; try { using (HttpClientHandler handler = new HttpClientHandler()) { handler.ClientCertificateOptions = ClientCertificateOption.Manual; handler.SslProtocols = SslProtocols.Tls12; Console.WriteLine("Loading Cert..."); var cert = new X509Certificate2(certFilePath, certPassword); Console.WriteLine("Verifying Cert..."); bool result = cert.Verify(); Console.WriteLine($"Verified Cert: {result}"); // Variable result is 'True'here. handler.ClientCertificates.Add(cert); using (HttpClient client = new HttpClient(handler)) { HttpContent content = new System.Net.Http.StringContent(""); Console.WriteLine("Posting..."); using (HttpResponseMessage responseMessage = client.PostAsync(url, content).Result) // FAILS HERE! { Console.WriteLine("Posted..."); Console.WriteLine("Reading Response..."); response = responseMessage.Content.ReadAsStringAsync().Result; Console.WriteLine($"Response: {response}"); } } } } catch (Exception exc) { Console.WriteLine("BOOM!!!"); exc = LogException(exc); } Console.WriteLine("Done!"); Console.ReadLine(); } 

Please note: I'm aware that this is unusual because I'm performing a POST with query parameters and no actual body but this part is not in my control - it's dictated by the third-party. I haven't suspected this as a factor in my issue but I could be very wrong here.

I've spent a great deal of time on this and have hit a wall.

2 Answers 2

1

The solution was far easier than expected. I needed the WEBSITE_LOAD_CERTIFICATES key with the thumbprint for the value in the Azure App Service Application Settings but this will only be picked up when running in the context of the web server. So my Console application that I used to try to isolate the problem only caused me more pain since it doesn't see those settings.

This article is what brought me to this fix.

https://learn.microsoft.com/en-us/azure/app-service/app-service-web-ssl-cert-load

Sign up to request clarification or add additional context in comments.

Comments

0

Have you added their server cert to your cert store? Client cert is used for mutual auth situations where you are sending them a cert to represent you. If their cert passes the "Well Known" test, it won't have a trust issues but if they have a self signed cert you have to capture their cert and import it into your cert store.

2 Comments

We are currently loading the certificate from the file system in Azure. It is successfully loading the certificate in code. In the same environment via the Azure Console with the exact same certificate I can perform the CURL to the third-party and get back the expected response without an error. This code, as is, runs outside of Azure. In fact, I've downloaded everything directly from Azure Kudu for the application without a single change and plopped it in IIS on a different box and it works.
Tools like curl,soapUI and postman are not good in troubleshooting cert issues as they typically are set to trust all certs.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.