httpClient.GetAsync throws exception; certificate

2019-07-23 16:47发布

TL;DR version (somewhat)

I've got two servers (X and Y) that are trying to communicate, but failing to do so over TLS1.2 (TLS1.1, and TLS1.0 both behave correctly).

I've got a server X that is failing to get a response from localhost.

The call that is throwing an exception is this line of code

HttpResponseMessage response = await httpClient.GetAsync(address, cancel);

where address is https://localhost/XXXXX/Identity/.well-known/jwks and cancel is IsCancellationRequested = false

I noticed that the server certificate being presented for https://localhost/XXXXX/Identity/.well-known/jwks is one that is valid, signed with RSA256, not on a revocation list, etc etc, everything looks good, but the subject is *.testing.corp, which doesn't match the URL I am trying to navigate to. This causes a certificate warning when navigating to https://localhost/XXXXX/Identity/.well-known/jwks from Chrome on server X, but after manually clicking through it I get the results I expect.

So my thought is that if I can override the cert validation of HttpClient.GetAsync, I should be able to make this work, so I add the following code around my client:

var handler = new WebRequestHandler();
httpClient = new HttpClient(handler);
handler.ServerCertificateValidationCallback = new RemoteCertificateValidationCallback(dumbvalidationdeletemeishouldnotbeversioned);

public static bool dumbvalidationdeletemeishouldnotbeversioned(object sender, X509Certificate cert, X509Chain chain, SslPolicyErrors err)
{
    return true;
}

The interesting bit is that this never hits, when I step to the GetAsync line of code, a step throws the exception without ever hitting the dummy validation.

What is going on? Why else could this method fail?


Even more context:

EDIT: Related, X is utilizing Identity Server to attempt to authenticate. The failure is occuring when trying to access https://localhost/XXXXX/Identity/.well-known/jwks

I am using IISCrypto to configure the protocols on these machines. (Taking @Oleg's advice in the comments did not change this behavior). I have configured them by clicking the "Best Practices" button and then un-checking TLS1.0 and TLS1.1, so that both servers look like this:

enter image description here

EDIT: Adding some stack trace information

Before I added my dummy handler, this is the thrown exception's stack trace:

System.Net.Http.HttpRequestException: An error occurred while sending the request. ---> System.Net.WebException: The underlying connection was closed: An unexpected error occurred on a send. ---> System.IO.IOException: Unable to read data from the transport connection: An existing connection was forcibly closed by the remote host. ---> System.Net.Sockets.SocketException: An existing connection was forcibly closed by the remote host
   at System.Net.Sockets.NetworkStream.EndRead(IAsyncResult asyncResult)
   --- End of inner exception stack trace ---
   at System.Net.TlsStream.EndWrite(IAsyncResult asyncResult)
   at System.Net.ConnectStream.WriteHeadersCallback(IAsyncResult ar)
   --- End of inner exception stack trace ---
   at System.Net.HttpWebRequest.EndGetResponse(IAsyncResult asyncResult)
   at System.Net.Http.HttpClientHandler.GetResponseCallback(IAsyncResult ar)
   --- End of inner exception stack trace ---
   at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
   at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
   at System.Runtime.CompilerServices.TaskAwaiter`1.GetResult()
   at XXXXXXXX.Authentication.Extensions.XXXXXXOpenIDConnectAuthentication.<ReadJWTKeys>d__8.MoveNext() 
   --- End of stack trace from previous location where exception was thrown ---         
   at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)         
   at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)         
   at XXXXXXX.Authentication.Extensions.XXXXXXOpenIDConnectAuthentication.<MessageRecieved>d__6.MoveNext()  
   --- End of stack trace from previous location where exception was thrown ---    
   at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)     
   at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)     
   at Microsoft.Owin.Security.OpenIdConnect.OpenIdConnectAuthenticationHandler.<AuthenticateCoreAsync>d__1a.MoveNext()

After adding the dummy handler, the exception is completely null <-- see the comments. The exception is exactly the same, but the return statement in my dummy handler is never hit when I debug the process, so the crux of my question changes to "What else is there to check?"

标签: c# tls1.2
1条回答
兄弟一词,经得起流年.
2楼-- · 2019-07-23 17:15

One should add

ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls12;

(from System.Net namespace) before call of

await httpClient.GetAsync(address, cancel);

to force the usage of correct protocol. See the blog with detailed experiments with HttpClient and ServicePointManager.SecurityProtocol.

查看更多
登录 后发表回答