39

I may have been under the wrong impression on how servers should be setup and what certificates actually get sent over during the server hello certificate message. I came across this today from Symantec/VeriSign:

Root installed on the server. For best practices, remove the self-signed root from the server. The certificate bundle should only include the certificate's public key, and the public key of any intermediate certificate authorities. Browsers will only trust certificates that resolve to roots that are already in their trust store, they will ignore a root certificate sent in the certificate bundle (otherwise, anyone could send any root).

If this is true, and root cert does not need to be installed on the server, well, there goes what I thought I knew about proper server setup and how the chain gets validated back to the root. Then again, when I look back at this question, under Certificates and Authentication section of Thomas Pornin's answer it says:

So the client is supposed to do the following:

  • Get a certificate chain ending with the server's certificate. The Certificate message from the server is supposed to contain, precisely, such a chain.

This says pretty much the opposite of the Symantec/VeriSign message above, unless I am misunderstanding something. So:

  1. Does a server need the complete chain installed, including the root? If not, what does a browser use to compare against for validation since the server won't be supplying its root cert during the handshake? Does it simply look at the identity cert and get it from there? (Like opening up an identity cert on your local machine, and seeing the full chain in the certification path?)

  2. Again if this is true what about stand-alone client apps that use SSL libraries? Will this depend on the application since it may have different path building methods to a trusted root vs a browser?

user53029
  • 2,687
  • 5
  • 24
  • 35

5 Answers5

43

The server always sends a chain. As per the TLS standard, the chain may or may not include the root certificate itself; the client does not need that root since it already has it. And, indeed, if the client does not already have the root, then receiving it from the server would not help since a root can be trusted only by virtue of being already there.

What Symantec says is that they recommend not sending the root, only the rest of the chain. This makes sense: since the root is useless for validation purposes, you may as well avoid sending it and save the 1 kB or so of data bandwith per connection.

Anyway:

  • The server's certificate, with its chain, is not for the server. The server has no use for its own certificate. Certificates are always for other people (here, the client). What is used by the server is its private key (that corresponds to the public key in its certificate). In particular, the server does not need to trust its own certificate or any CA which issued it.

  • In TLS, the server is supposed to send a chain; and the client is supposed to somehow use the server's public key for the handshake. The client is free to "know" that public key in any way that it wishes to, although of course it is expected that the client will obtain the server's public key from the certificate chain that the server just sent. Browsers will primarily try to use the chain sent by the server (by trying to link it below one of the roots already trusted by the browser); in case of failure, they will try to build other chains based on intermediate CA certificates that they already know or can download on-the-fly.

    In stand-alone applications, application writers are free to configure or bypass that step in arbitrary ways, which can be useful if the server's public key can be hardcoded in the application code (in which case the chain sent by the server is just completely ignored). Unfortunately, freedom to implement a custom certificate validation step is also freedom to wallop security, stab it to death and then throw its corpse in a ditch. It happens way too often.

Thomas Pornin
  • 322,884
  • 58
  • 787
  • 955
  • 2
    Small note to this excellent answer: caching of intermediate certificates may mean that a chain validates even if it is not sent in full (minus root). This is a pitfall, as an empty cache may mean that the chain *suddenly* doesn't validate anymore when e.g. only the leaf / server certificate is sent. Doublecheck the chain and make sure every certificate - minus root - is there and in the right order (leaf first). – Maarten Bodewes Nov 12 '20 at 13:12
9

what does a browser use to compare against for validation since the server won't be supplying its root cert during the handshake

The whole idea of certificate checking is that the clients has some root certificates it trusts (shipped with the browser or OS) and that it validates the certificate the browser sends against this local trust anchor.

This means, that the server does not need to (and should not) send the root certificate to the client, because this root certificate is supposed to be at the clients end already. If it still sends the root certificate the client will simply discard it, i.e. it will not trust any root certificate send by the server.

Again if this is true what about stand-alone client apps that use SSL libraries? Will this depend on the application since it may have different path building methods to a trusted root vs a browser?

The basic verification process between browsers and SSL libraries is the same. There are some differences in path building in edge cases between openssl and browsers (see http://rt.openssl.org/Ticket/Display.html?id=2732&user=guest&pass=guest) and the verification of the hostname of the target against the name(s) in the servers certificate is often buggy or even non-existing outside the common browsers.

Steffen Ullrich
  • 190,458
  • 29
  • 381
  • 434
6

I send the root when it is convenient to do so.

If the client trusts the root, then it makes no difference whether you send it or not.

When the client does not recognize the root, in my experience the MS Windows client produces much less confusing diagnostic messages if the untrusted root was provided in the chain.

As for how it knows what root to use if one is not provided, the next-to-root (which has to be in the provided chain) contains the identifying information for the root, which the client uses to look up the root in the client's store.

jjanes
  • 307
  • 1
  • 6
  • First answer, which points out an advantage for including the root certificate... – I’m not advocating it, because it means the root is “downloaded” on each handshake. But it also enables the user to extract and install the root certificate from the chain (which, without comparing fingerprints, is not enhancing security very much). – Robert Siemer Mar 21 '17 at 11:14
  • 1
    I'd go as far as saying that this would be detrimental to security. In the worst case, the application at the other side decides to accept the chain without trust, or doesn't trust the chain because an identical but different root is used than the one in the trust store (although I must admit that this would be extremely stupid programming, but yeah, I've seen a lot of that) – Maarten Bodewes Nov 12 '20 at 13:16
3

Take into account that there are server software (like IBM HTTP Server) that will not start at all if the root CA is not included in the keystore.

NuTTyX
  • 693
  • 4
  • 9
  • Interesting. does it also send the full chain within the handshake though? – Maarten Bodewes Nov 12 '20 at 13:17
  • 1
    The default behaviour is to send the whole chain. I think they finally made some option to avoid sending the root CA, but since it does not affect any known client, I just leave it like that. It sends an extra cert (around 1kb I think) but it also helps the client to make sure they have the correct root installed on their side. – NuTTyX Nov 12 '20 at 16:03
0

As pointed out in the other answers, there is no security benefit to be gained from the server including the root certificate with the rest of the certificates in the certificate chain.

However, it is not uncommon for a server to include the root certificate in the chain. For example, www.amazon.com does this. This can be seen using the following openssl command:

openssl s_client -showcerts -connect www.amazon.com:443

This produces:

depth=2 C = US, O = DigiCert Inc, OU = www.digicert.com, CN = DigiCert Global Root G2
verify return:1
depth=1 C = US, O = DigiCert Inc, CN = DigiCert Global CA G2
verify return:1
depth=0 CN = www.amazon.com
verify return:1

Note that the certificate at depth=2 (DigiCert Global Root G2) is a widely-trusted root certificate. On many Linux systems, this certificate ships with the distribution and can found at /etc/ssl/certs/DigiCert_Global_Root_G2.pem.

mti2935
  • 21,098
  • 2
  • 47
  • 66
  • 1
    At least for the server I get (amazon has many) it actually sends a DigicertG2-to-VerisgnG5 _bridge_ cert (not the root) as you can see in the detailed 'showcerts' output. But if you are using an up-to-date truststore derived from Mozilla, as many Linuxes and some other systems do, that [recently removed Verisign G5](https://bugzilla.mozilla.org/show_bug.cgi?id=1670769) and (non-ancient) OpenSSL when the supplied chain points to an untrusted root will look for a trusted root for a CA earlier in the chain and if found use it instead for the verify calback (which is what you quoted). – dave_thompson_085 May 04 '21 at 11:57