title: .NET dropping client certificate over TLS 1.2
body: <p>Hello it seems some form of this question has been asked a million different ways but many don't have answers, or the answers don't apply to me.</p>
  
  <p>We have a trivial little .NET service that calls out to a 3rd party API that is only supporting TLS 1.2 now.</p>
  
  <pre><code>        var requestHandler = new WebRequestHandler();
          var clientCert = GetClientCert("THUMBPRINT");
  
          requestHandler.ClientCertificates.Add(clientCert);
          var encodedHeader = "FOO";
          var httpClient = new HttpClient(requestHandler) { BaseAddress = new Uri("https://foo.bar.com/rest/api/") };
          httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Basic", encodedHeader);
  
          var httpResponse = httpClient.GetAsync("").Result;
          var responseContent = httpResponse.Content.ReadAsStringAsync().Result;
  </code></pre>
  
  <p>When I explicitly set the security protocol to 1.2, or target a framework that defaults to 1.2, the client certificate is not part of the packet that goes to the server.  I've examined it in Wireshark and the certs are never present over 1.2.  At that point the server sends the Fatal Alert Handshake Failure (40) which results in the following .Net exception:  </p>
  
  <pre><code>The request was aborted: Could not create SSL/TLS secure channel.
  </code></pre>
  
  <p>I can make the same call through Chrome or Postman (native) and it works fine.  If I set the protocol down to 1.1 it works fine.  But if I run it as 1.2 through .NET it fails every time.  I get the same exact error and same traffic in Wireshark even if I purposely do not add my client certificate at all.</p>
  
  <p>I ran the 3rd party endpoint through <a href="https://www.ssllabs.com/" rel="nofollow noreferrer">https://www.ssllabs.com/</a> and it grades out fine.  There are no MD5 signatures anywhere that I can see on either side.  Everything is signed with sha1 or sha256 with RSA encryption.</p>
  
  <p>I have tracing enabled.  The following output seems to indicate there is no issue finding the certificate or its private key:</p>
  
  <pre><code>System.Net Information: 0 : [19512] SecureChannel#41622463 - Certificate is of type X509Certificate2 and contains the private key.
  System.Net Information: 0 : [19512] SecureChannel#41622463::.AcquireClientCredentials, new SecureCredential() (flags=(ValidateManual, NoDefaultCred, SendAuxRecord, UseStrongCrypto), m_ProtocolFlags=(Zero), m_EncryptionPolicy=RequireEncryption)
  System.Net Information: 0 : [19512] AcquireCredentialsHandle(package = Microsoft Unified Security Protocol Provider, intent  = Outbound, scc     = System.Net.SecureCredential)
  System.Net Information: 0 : [19512] InitializeSecurityContext(credential = 
  System.Net.SafeFreeCredential_SECURITY, context = 1ed7465db80:1f1d854c910, targetName = foo.bar.com, inFlags = ReplayDetect, SequenceDetect, Confidentiality, AllocateMemory, InitManualCredValidation)
  System.Net Information: 0 : [19512] InitializeSecurityContext(In-Buffers count=2, Out-Buffer length=133, returned code=ContinueNeeded).
  </code></pre>
  
  <p>I also have SCHANNEL event logging turned on.  There I can see it picking up the client cert with the private key.</p>
  
  <pre><code>The TLS client credential's private key has the following properties:
  
     CSP name: Microsoft Enhanced Cryptographic Provider v1.0
     CSP type: 1
     Key name: {some hex here}
     Key Type: key exchange
     Key Flags: 0x0
  
   The attached data contains the certificate.
  </code></pre>
  
  <p>Followed by the error that corresponds to the Alert packet in Wireshark:</p>
  
  <pre><code>A fatal alert was received from the remote endpoint. The TLS protocol defined fatal alert code is 40.
  </code></pre>
  
  <p>I am completely out of ideas.  It feels like a bug in .Net.  We thought for a moment that it was an incomplete trust chain with our client certificate, because our cert package only had the client cert and an intermediate CA.  But we got the Root CA from the vendor and now Windows says the cert is legit.</p>
  
  <p>One interesting thing I notice in Wireshark is that we send the client cert AND the intermediate CA on successful calls.  I don't know how it's picking up the intermediate CA because I only pull in the client cert to attach to the request.  Why wouldn't it also submit the root CA once we had it?  My gut tells me the problem may still lie in here somewhere but I can't prove it.  There is nothing in any logs anywhere that indicate a trust chain failure.</p>
  
  <p>I've even hooked up ProcMon as well to see if I could see anything amiss but nothing jumped out.  Man I could have rewritten the whole thing in Java in the amount of time I've spent on this.</p>
  
  <p>Some other things I have tried that didn't work: </p>
  
  <p>targeting .Net 4.8</p>
  
  <p>machine store vs user store</p>
  
  <p>loading directly from the pfx instead</p>
  
  <p>HttpWebRequest instead of HttpClient</p>
  
  <p>TcpClient with SslStream</p>
  
  <p>newer Windows 10 release</p>
  
  <p><strong>EDIT</strong>
  I just read something that says SHA-1 signatures were also removed from TLS 1.2 along with MD5.  Our client cert is signed in SHA-1.  I think this might be it and we are contacting our vendor.</p>