20

I have the following C# code, constructing an https call with a custom certificate. When using Tls 1.1, the call works fine. When using Tls 1.2 the call breaks. I using curl, using tls 1.2 works fine as well.

C# Code:

X509Certificate2Collection collection = new X509Certificate2Collection();
collection.Import("C:\\SomePath\\MyCertificate.pfx", "MyPassword", X509KeyStorageFlags.PersistKeySet);
var cert = collection[0];

ServicePointManager.SecurityProtocol = ...;

ServicePointManager.ServerCertificateValidationCallback += (sender, certificate, chain, errors) => true;
HttpClientHandler handler = new HttpClientHandler();
handler.ServerCertificateCustomValidationCallback = (message, certificate2, arg3, arg4) => true;
handler.ClientCertificates.Add(cert);

var content = new ByteArrayContent(Encoding.GetEncoding("latin1").GetBytes("Hello world"));
HttpClient client = new HttpClient(handler);
var resp = client.PostAsync(requestUri: url, content: content).Result.Content.ReadAsStringAsync().Result;

Works with:

ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls11;

Error with:

ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls12;

.Net error message: SocketException: An existing connection was forcibly closed by the remote host

.Net version : 4.7.1

OS: Windows 10 version 1703 (supported cipher list: https://msdn.microsoft.com/en-us/library/windows/desktop/mt808163(v=vs.85).aspx) - and the server specifies TLS_RSA_WITH_AES_256_GCM_SHA384 to be used, which is among the supported ciphers.

In wireshark I can see that with the working calls (C#/Tls 1.1 and Curl Tls 1.2) the certificate is being sent to the server. Here is the wireshark dump for the C# tls 1.1 call:

Wireshark dump - Csharp tls 1.1

However, also in wireshark, I can see that with C#/Tls 1.2 there is no certificate being sent from the client to the server. Here is the wireshark dump for the C# tls 1.2 call:

enter image description here

Can anyone see what I am missing here?

UPDATE

It seems the certificate has an md5 signature which is not supported by Schannel in windows in combination with tls 1.2. Our vendor has created another certificate to us as a solution.

I came across this random thread that discusses the issue: https://community.qualys.com/thread/15498

Stephan Møller
  • 1,023
  • 17
  • 34
  • I had similar issue with .Net Core 2.0 client/server application. I solved by forcing TLS 1.2 on server. This way, client negotiate protocol and certificate properly. You might try to connect to a server which is forced to TLS 1.2 only and see if you have same behaviour with .NET Framework. – Ghigo Dec 20 '17 at 14:02
  • well changing this on the server is not really an option here as the server belongs to a vendor. And it doesn’t matter if I add tls 1.1 along. As long as tls 1.2 is just among the allowed protocols, this issue happens. I need to know why .net behaves this way when curl works just fine. And I need tls 1.2 enabled because this is a static setting and other connections depend on tls 1.2 in the same application. If I could turn of tls 1.3 for this single call, it would do. But setting the protocols on the handler object throws an exception telling that this is not possible in the current env. – Stephan Møller Dec 20 '17 at 18:31
  • 1
    Looks a lot like a duplicate of this question https://stackoverflow.com/questions/44751179/tls-1-2-not-negotiated-in-net-4-7-without-explicit-servicepointmanager-security – Robbert Draaisma Jan 02 '18 at 10:31
  • 1
    Well I dont think it is a duplicate. First of all, your link shows a thread describing an issue where tls 1.2 is not working unless explicitly set - my issue is kind the opposite: https not working WHEN tls 1.2 enabled. Second, the fix described using app.config did not help anything. – Stephan Møller Jan 02 '18 at 14:20
  • Check if you have any restrictions on cipher suites at the protocol level, like so: https://technet.microsoft.com/en-us/library/dn786418%28v=ws.11%29.aspx?f=255&MSPPError=-2147217396#BKMK_SchannelTR_CipherSuites – zaitsman Jan 02 '18 at 23:24
  • what are your curl parameters? further have to used wireshark against curl...are you sure its passing as tls 1.2 – Ctznkane525 Jan 07 '18 at 18:31
  • We are having this exact same problem, but I think we can rule out MD5 signatures or cipher suites. Everything looks good on Wireshark except the missing client cert. I'm so stuck. – grinder22 Jan 16 '20 at 17:53
  • Are you sure md5 is not used when the Certificate was created? It is dotnet specifically that wont accept tls 1.2 with md5 based certificates. – Stephan Møller Jan 17 '20 at 18:07

3 Answers3

2

You are right on the root cause of this problem: By default, schannel-based clients offer SHA1, SHA256, SHA384 and SHA512 (on Win10/Server 2016). So TLS 1.2 servers are not supposed to send their MD5 certs to these clients.

The client (HttpClient) does not list MD5 in the signature_algorithms extension, so the TLS 1.2 handshake fails. The fix is to use a secure server cert.

0

I believe this code is masking some type of certificate error by always blindly returning true:

handler.ServerCertificateCustomValidationCallback = (message, certificate2, arg3, arg4) => true;

I recommend you have a function to truly analyze the results of arg4. That is your SSL policy errors. Log them and you will get your answer. In my example, I write to the console, but you can write to the trace, or a file. You'll get a number which will be associated a value for the SslPolicyErrors enumeration. Based on the results you might need to check your arg3, which is your chain.

ServicePointManager.ServerCertificateValidationCallback += (sender, cert, chain, sslPolicyErrors) => {

SslPolicyErrors errs = sslPolicyErrors;
Console.WriteLine("Policy Errors " + sslPolicyErrors.ToString());           
return true;};
Ctznkane525
  • 6,748
  • 2
  • 13
  • 36
  • First of all, thank you for commenting! However, the callback function is never called. I just added a body to the function with a breakpoint and a console write call and none of this gets hit. – Stephan Møller Jan 06 '18 at 19:07
  • Ctznkane actually, even though it is not called when using tls 1.2, it is actually called when using tls 1.1 and specifies error type "System.Net.Security.SslPolicyErrors.RemoteCertificateChainErrors". The method returns true, allowing the call in tls 1.1 but maybe tls 1.2 wont allow this at all due to higher security standards? My best bet is to find out why the chain errors occurs with tls 1.1 now and then fix the error and try with tls 1.2 again. I will be back with the result. – Stephan Møller Jan 08 '18 at 10:22
  • After digging a little in the objects proviced for the callback method, I found this message deep down: "A certificate chain processed, but terminated in a root certificate which is not trusted by the trust provider." - I will go google for this error specifically. – Stephan Møller Jan 08 '18 at 10:29
  • Did you buy a cert from a valid company or make one of your own...making one is ok for internal stuff only – Ctznkane525 Jan 08 '18 at 10:33
  • And this might help with second error https://stackoverflow.com/questions/11230091/whys-my-root-certificate-not-trusted – Ctznkane525 Jan 08 '18 at 10:36
  • I was handed out the custom certificate by a vendor. Should this rely on a trusted root then? – Stephan Møller Jan 08 '18 at 12:45
  • Who is responsible for the server you are connecting to? – Ctznkane525 Jan 08 '18 at 12:50
0

Shouldn't you specified the handler's SslProtocols property?

Try adding this line after hander definition:

handler.SslProtocols = SslProtocols.Tls12;
Majid ALSarra
  • 315
  • 2
  • 7