1

I have a p12 file that contains three certificates (and a private key)

  1. Client certificate
  2. Intermediate certificate
  3. Root certificate

Using openssl s_client the connection is successful, however, using HTTP client the connection isn't.

Inspecting the payload in Wireshark I can see that only two certificates are sent (1,2) and the root (3) is missing.

enter image description here

I've installed the certificates in Current User and Local Machine in My and Root Certificates but nothing changes the result. Where should the certificates be installed?

Fun fact, using var chain = new X509Chain(); chain.Build(certificate) all intermediate certificates are correctly found.

Update: I tried adding all certificates resolved from the chain but the result is the same.

Code

using System.Security.Authentication;
using System.Security.Cryptography.X509Certificates;

const string thumbprint = "";

using var store = new X509Store(StoreLocation.LocalMachine);
store.Open(OpenFlags.ReadOnly);
var certificate = store.Certificates.Find(X509FindType.FindByThumbprint, thumbprint, false)[0];

var clientHandler = new HttpClientHandler
{
    ClientCertificateOptions = ClientCertificateOption.Manual,
    SslProtocols = SslProtocols.Tls12,
    ClientCertificates = { certificate }
};

var client = new HttpClient(clientHandler)
{
    BaseAddress = new Uri("url")
};

var response = await client.GetAsync(string.Empty);
// Exception:
//  The SSL connection could not be established, see inner exception.' -> 
//  AuthenticationException: Authentication failed because the remote party sent a TLS alert: 'HandshakeFailure'.

I've been follow this SO-post but it doesn't work for me.

Update I removed the root certificate from the crt file used when exercising OpenSSL s_client and more carefully read all of the output. It appears it has never worked...

139645152049040:error:14094410:SSL routines:ssl3_read_bytes:sslv3 alert handshake failure:s3_pkt.c:1493:SSL alert number 40
139645152049040:error:140790E5:SSL routines:ssl23_write:ssl handshake failure:s23_lib.c:177:

I was paying attention to the last part of the output which read

SSL handshake has read 5917 bytes and written 2674 bytes
---
New, TLSv1/SSLv3, Cipher is ECDHE-RSA-AES256-GCM-SHA384
Server public key is 2048 bit
Secure Renegotiation IS supported
Compression: NONE
Expansion: NONE
No ALPN negotiated
SSL-Session:
    Protocol  : TLSv1.2
    Cipher    : ECDHE-RSA-AES256-GCM-SHA384
    Session-ID:
    Session-ID-ctx:
    Master-Key: 13838C2676F91215679A69B491D9117198CAD86B24CDBBFE6357A0D34B58317DD6F9C57FAFE99264CB73A94204280300
    Key-Arg   : None
    Krb5 Principal: None
    PSK identity: None
    PSK identity hint: None
    Start Time: 1654810361
    Timeout   : 300 (sec)
    Verify return code: 0 (ok)
---

That gave me the impression that everything was ok. I'll reach back to the provider of the certificate and service since I suspect that the client certificate CA isn't in the allowed list of the server (can't find it in openssl output) which @Oliver pointed out in a comment.

Sorry for wasting your time and thanks for your dito!

Update: @crypt32 They have now successfully installed the necessary certificates on the server and it works using openssl with

  1. All three certificates
  2. Intermediate and client
  3. Only client (private key is included in all above as part of the pem file)

The .NET (from Windows) still doesn't work.

Update: It works from .NET as well! Case closed

joacar
  • 891
  • 11
  • 28
  • 2
    AFAIK the root certificates are never send. The machine (or application) has its own storage of root certificates and uses these to check the chain of certificates. That's why root certificates are special, because they need to be provided by a different way to the user (e.g. OS or application installation / update). Most applications use the certificate storage of the underlying OS, one counter example would be Firefox that has its own storage. – Oliver Jun 09 '22 at 12:17
  • Sending the intermediate certificate is useful though. Yes, you need to create a `Chain`, what is wrong with that? – Charlieface Jun 09 '22 at 12:24
  • Nothing wrong. I forgot to write in the question that I tried adding all certificates (intermediate and root) to the `HttpClientHandler` but no difference in result. – joacar Jun 09 '22 at 12:33
  • `it works from .NET as well! Case closed` how are able to send root cert? – Imran Qadir Baksh - Baloch Aug 11 '22 at 09:06
  • @ImranQadirBaksh-Baloch Is this a new question? Do you want to know how to send a root certificate? If yes, open a new question and don't ask in the comments. – Oliver Aug 12 '22 at 05:45

2 Answers2

4

Where should the certificates be installed?

on a server you are connecting to.

The idea behind this is that you send leaf and all intermediate CA certificates to server. Server builds the certificate chain on receipt. And if server is not able to locate the root certificate, then client certificate is not trusted by the server and validation check will fail. Presence of root certificate in request makes little sense, because server will not automatically trust your root just because you sent it. Root certificate must be explicitly installed on a server in trusted root store.

An excerpt from RFC 5246 §7.4.2 which applies to both, server and client certificates:

certificate_list
      This is a sequence (chain) of certificates.  The sender's
      certificate MUST come first in the list.  Each following
      certificate MUST directly certify the one preceding it.  Because
      certificate validation requires that root keys be distributed
      independently, the self-signed certificate that specifies the root
      certificate authority MAY be omitted from the chain, under the
      assumption that the remote end must already possess it in order to
      validate it in any case.

In other words, there is nothing you can or should do on your end.

Crypt32
  • 12,850
  • 2
  • 41
  • 70
  • But using `openssl s_client -connect :443 -cert cert.cer` works and then all three certs are passed (since they are all in the file). I can try a call with removing the root from the certificate file. – joacar Jun 09 '22 at 20:59
  • This is just OpenSSL implementation. Microsoft SChannel do not send root cert. – Crypt32 Jun 09 '22 at 21:01
  • I just tested without the root certificate and it succeeds. This make me even more curios why it doesn't work in .NET when all required certificates are sent. – joacar Jun 09 '22 at 21:34
  • @joacar you should examine server logs. No one can tell you what's wrong without additional details. BTW, make sure if your application has permissions to access private keys in system store. Either, the application must run with admin rights or have explicit permissions. – Crypt32 Jun 09 '22 at 21:38
  • Thanks @Crypt32. I updated my post with additional info and I think that the server indeed lacks the root certificate. – joacar Jun 10 '22 at 06:33
  • Then this is the answer. Though installing root CA on a server is required, but often not sufficient. Server should map client certificate to identity database in order to authenticate and authorize the client. Otherwise client authentication is misused. – Crypt32 Jun 10 '22 at 06:56
-4

You can add this to your client handler initialization:

clientHandler.ServerCertificateCustomValidationCallback = (sender, cert, chain, sslPolicyErrors) => { return true; };
IKomarovLeonid
  • 352
  • 1
  • 9
  • Downvoting, because the topic has nothing to do with server certificates, and this is simply unsecure. Accepting arbitrary certificate never was a good idea. – Crypt32 Jun 09 '22 at 19:27