14

we're in the process of replacing certificates with SHA1 hash due to Google's move to let them appear less secure in Chrome. The replacement certificates use a different intermediate CA than the ones we're currently using, but the same root CA. During testing we noticed that our SSL clients would fail to validate the chain using the new certificates we don't quite understand why.

Here's the specifics:

Currently the chain the server sends looks like this:

  • Root CA, signed by Server CA X from the same authority (came with Server Certificate)
  • Intermediate CA 1 (came with Server Certificate)
  • Server Certificate

in our SSL clients we trust:

  • Root CA, self signed, but same CN and same key (same skid)! (Downloaded from authority; same as Mozilla incorporates in its products)
  • Intermediate CA 1 (identical with the one the server sends)

This works with the current certificate chain but only because we trust Intermediate CA 1. If we remove this it fails because Server CA X is not trusted. New certificates obviously don't work then, because they are signed by a different intermediate CA.

Now, from reading RFC 5280, section 6.1, my impression is that our SSL client does not adhere to standards, because from what I've read my understanding of a certificate path validation is this (simplified):

  1. start with the trust anchor; validate if it's still currently valid (time) etc.
  2. for the following certificates: check if the CN and key of the previous certificate match with the issuers CN and key of the current certificate (and check pathlen constrains etc...)
  3. the last certificate must not be a CA certificate

The point is: Our SSL client fails to validate the chain because the root certificate it's configured to use as a trust anchor has a different issuer than the root certificate presented by the server. However, CN and key are identical for both. From my understanding of what I've read in RFC 5280 the SSL client should not care for the issuer of its trust anchor. There are also no policies defined in the certificates inside the chain that would enforce anything like that. However, I don't know if our SSL client may enforce additional policies that would lead to this (not my system).

So my question is: Does our SSL client behave correctly in this scenario or not? And of course: Why?

Thanks in advance

Edit: I know that the server should only send the intermediate CA cert and not the root cert, however, we're using what the authority provided as a cabundle alongside the certificate, which included the root certificate. Moreover, from what I know, the client should disregard the root certificate sent by the server in that case.

Also what's weird is that the client behaves as if it validates the chain from the server certificate down to something it can trust and then stops, whereas the specs say it should validate the chain from the trust anchor up to the server certificate.

Edit2: The different "versions" of the Root CA cert all have the same SKID - they only differ in issuer and serial number (of course).

Also, just came to mind, from the first edit's information it seems the SSL client either needs the root certificate in trust and chain to be identical in its issuer and possibly serial number to accept it.

luxifer
  • 143
  • 1
  • 1
  • 6

2 Answers2

19

There is X.509, and there is SSL/TLS. TLS expects the server to send an X.509 certificate chain, from which the client will extract the server's public key. Then things diverge:

  • In pure X.509, the server should send its certificate for the client to validate; possibly, the server may add an unordered bunch of extra certificates that could prove useful to the client, basically some possibly relevant intermediate CA. The client would still be free to validate the certificate in any way as it sees fit, in particular through arbitrary chain building. This method is demonstrated in, for instance, in CMS (formerly known as "PKCS#7"), in which a signed document (SignedData) references the signer's certificate and optionally includes an unordered set (ASN.1 "SET") of extra certificates.

  • In SSL/TLS, things don't go that way. Instead, the server should send the exact chain that is to be used; the server is explicitly allowed to omit the root CA, but that's all. Any client is entitled (at least historically, if not de jure) to reject the server's certificate if the chain does not match this pattern. Of course, any client is allowed to make an effort and try to validate the server's certificate in the X.509 way, with its own path building; but there are SSL client out there that do not make such an effort.

A relevant excerpt of RFC 5246 is in appendix F.1.1:

If the server is authenticated, its certificate message must provide a valid certificate chain leading to an acceptable certificate authority.

This is the passage that gives SSL clients the "right" to unceremoniously reject server chains that cannot be validated "as is".

In your case, the chain sent by the server is:

    Root-X -> CA1 -> Server

where "Root-X" is "the root signed by Server CA X". The lazy client tries to validate that chain twice:

  1. First attempt is based on the assumption that the chain includes the root CA. The client does not find "Root-X" in the list of its own root CA, so this fails.

  2. Second attempt is based on the assumption that the chain does not include the root CA. The client scans its root CA for an issuer for Root-X (and that's the critical point). It does not find any, hence failure.

In order to validate the chain sent by your server, the client must be slightly less lazy, and try to find an issuer for CA1 within its own set of trusted CA. This would require a third assumption, namely that the chain sent by the server does include a root CA, but not "the right one".

I suppose that your failing SSL client is a "lazy client" as enunciated above. To make sure, try to connect with Internet Explorer as client; IE, for all its shortcomings, is not lazy when it comes to certificate validation. IE will try to rebuild a chain from the certificates sent by the server, the certificates it has in its own stores, and the certificates it can download. Notably, any certificate may contain a URL pointing to the certificate for its issuer (that's part of the Authority Information Access extension); IE (and, in fact, Windows in general) will gleefully follow such URL and gather intermediate CA (HTTP-only, not HTTPS, to avoid a nasty loop).

If you can thus confirm that the problem is indeed a lazy client, then possible workarounds include the following:

  • Change the chain sent by the server so that it includes the self-signed root, or simply ends at CA1.
  • Install the "Server CA X" certificate as trusted CA in the clients.
  • Modify/configure the client so as not to be lazy.

Of course it is quite possible that your chain is incorrect in some other way, which is difficult to assess without actually seeing the certificates. The path validation algorithm is complex, so there might be another extension that does not please the client (e.g. one certificate might be using a Name Constraints extension on which the SSL client implementation just chokes). Possibly, the client is not lazy, but insists on ascertaining revocation status of all certificates in the path, and some CRL or OCSP responder is out-of-date or unreachable. There are hundreds of ways by which certificate validation may fail.

Thomas Pornin
  • 320,799
  • 57
  • 780
  • 949
  • The question is how the SSL client is supposed to determine whether or not it has the appropriate root cert in its trust store. Which certificate properties should it consider for comparison? My expectation would be CN and SKID. But obviously this client considers more than that (suspecting issuer field and serial number). Would that even be a legal thing to do for an SSL client? – luxifer Nov 01 '14 at 11:11
  • 1
    My point is that the client is only considering CN and public key; _but_ it is not doing the chain surgery that you expect. The client looks whether the top element of the transmitted chain is exactly one of its known trusted CA, or whether a valid chain could be obtained by putting one of its trusted CA _on top of_ the chain transmitted by the server. The client does not try what you would like, i.e. _replacing_ the top certificate in the server chain with one of its trusted CA. – Thomas Pornin Nov 01 '14 at 12:32
  • I understand your point. However my question is how should the client decide if the top element of the chain is exactly one of its known trusted CAs? Matching CN and SKID? Binary match? Or is this completely undefined and up to an actual implementation to decide? – luxifer Nov 01 '14 at 12:55
  • 2
    It is not really defined, but I expect many implementations to make a binary match with a locally known self-signed certificate. In "true X.509", self-signed roots don't exist; a _trust anchor_ is a DN+key. However, a widespread tradition is to encode trust anchors as certificates, usually self-signed (mostly because a certificate has a non-optional field for a signature), and lazy implementations just do a bit-by-bit comparison. – Thomas Pornin Nov 01 '14 at 15:39
  • I'm going to accept your answer as it was very concise and you follow-ups added valuable insight. Turns out the client is indeed lazy. We were also in contact with the client's vendor and indeed, when using one of their updated crypto libs it would have verified correctly in this scenario instead of failing. Thanks again for your insights! – luxifer Nov 04 '14 at 10:51
2

The provided chain should not include the root, if it does the client must disregard it. A key feature of X.509 trust is that it requires pre-known roots (or trust anchors). This isn't the cause of your problem though. The order is inverted, the server cert should be first, followed by its signer (and its signer etc. if need be), this might cause problems on some platforms, but read on.

Contemporary clients no longer rely on chasing up the chain by DN (i.e. issuer's certificate name). Starting with the AKID of a certificate (Authority key identifier), the parent is the certificate with the matching SKID. The SKID (Subject key identifier) is intended to be a (statistically) unique identifier, typically it's derived from a hash of the public key, but need not be. For the purpose of of verification it's simply an opaque identifier (i.e. verification doesn't recompute it, it only compares the values). It seems most likely the identifiers have changed, though the human readable names have not.

The checking of the chain must start at the server cert, and work toward the root at the "top", this is conventionally "up". This is the same order the chain is presented in. Each cert "knows" only its signer (CN and SKID), nothing else about the hierarchy.

This expands on the verification process a little: Certificate Chain checking, and this gives an example using OpenSSL to reproduce the typical verification process: https://serverfault.com/questions/541262/checking-the-issued-and-expiry-dates-for-the-certificates-involved-a-certificate

Another possible problem is what constitutes the "top", or "trust anchor". Some CAs have a self-signed root certificate (AKID==SKID), some use cross-signed roots (the signing cert belongs to another CA). Where exactly to stop following the chain is a property of the client keystore, typically by virtue of a CA certificate in a specific classification.

mr.spuratic
  • 7,937
  • 25
  • 37
  • Thanks for pointing this out, I didn't know that. After checking for this, however, the situation is still the same. I'll edit my question to add this information. – luxifer Oct 31 '14 at 16:41
  • I've updated, but lacking details of the certificates it's hard to be precise... Also, you are certain that your implementation can [correctly handle SHA-256](https://support.globalsign.com/customer/portal/articles/1499561-sha-256-compatibility?__hstc=263099475.d01eedc0c0e0646e49876b6ddd8a8cc3.1414776517079.1414776517079.1414776517079.1&__hssc=263099475.1.1414776517079&__hsfp=1844977452)? – mr.spuratic Oct 31 '14 at 17:31
  • I don't know about other implementations, but 5280 certainly says and openssl (through 1.0.1) does chain by Issuer to Subject *DN* (as you say first time, not just *CN* as you say second time) but *also* checks AKID==SKID *if* they are present (which they should). Are you maybe mixing this with the fact that 6125 prefers *server* (not CA) certs to use SubjAltNames and not Subject? – dave_thompson_085 Nov 01 '14 at 12:26