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.