Those following me on various Intarweb Media may have noticed I've spent half the week staring at openssl source code and weeping. Here's one of the results of that.
OpenSSL has two somewhat different mechanisms for deciding what uses a certificate is good for: trust and purpose. This is quite subtle and not terribly well documented, so I thought I'd write it up here.
Here's a tl;dr approximation: purpose is about what the entity that issued the certificate has to say about what it should be used for. trust is about the degree to which the entity validating the certificate trusts the entity that issued it.
purpose checking is more commonly encountered and understood. The reason we have it should be fairly obvious. Say I'm a CA, and you ask me to issue you a certificate for your web server, awesomesauce.com. What I, as the CA, want to say to the world at large is "I affirm that the entity that holds the other half of this here key pair is the legitimate operator of awesomesauce.com". Notably, I do not necessarily want to say to the world at large "I affirm that the holder of the other half of this here key pair can tell you who else to trust, on my behalf" (i.e. to act as an intermediate CA), or even "I affirm that the holder of the other half of this here key pair is the legitimate owner of the email address firstname.lastname@example.org" (S/MIME email stuff).
So we don't just need a mechanism for entities to sign each other's keys; we need to indicate what purpose they're affirming trust in the other entity for.
If you have a certificate for your web server or whatever, take a look at it, with
openssl x509 -in certfile -noout -text. If you don't, here's an excerpt from the output you get by running that command on the happyassassin.net certificate:
Subject: C=CA, ST=British Columbia, L=Vancouver, O=Adam Williamson, CN=www.happyassassin.net/emailAddress=postmaster AT happyassassin.net ... X509v3 Subject Alternative Name: DNS:www.happyassassin.net, DNS:happyassassin.net, DNS:mail.happyassassin.net ... X509v3 Key Usage: Digital Signature, Key Encipherment, Key Agreement X509v3 Extended Key Usage: TLS Web Client Authentication, TLS Web Server Authentication
That information is a part of the certificate as issued by the CA. What it means is that the CA trusts me (as the holder of the other half of the key pair associated with the certificate) for the purposes of "Digital Signature, Key Encipherment, Key Agreement" and "TLS Web Client Authentication, TLS Web Server", as they relate to the subject and subject alternative name that also form a part of the certificate metadata. The various (extended) key usages are defined in RFC5280 and RFC3280, mostly (I think a few crop up in other RFCs).
When openssl does purpose checking, what it does is check all the certificates in the chain being verified which don't come from the store of trusted certificates to see if their key usage extensions (and a few similar bits of metadata) match the purpose for which the chain's validity is being tested. The intermediate certificates are required to have the appropriate key usage extensions for a CA issuing certificates for the purpose, and the leaf certificate (the server certificate or whatever) is required to have the appropriate key usage extensions for an entity performing the purpose.
trust checking is a bit less commonly encountered and, probably, understood.
We mentioned the trusted certificate store just now. This is the list of certificates considered 'trusted'. Commonly, this is kind of a binary operation. If you have a regular certificate in OpenSSL's trust store it is considered that you trust it for all purposes (corrections welcome on this, but my reading of the source is OpenSSL does not run purpose checks on certificates from the trust store, even though they can express key usages - OpenSSL's trust store is pretty much explicit a store of trusted CA certificates, you shouldn't ever put server certificates in it). The trust store usually contains root CA certificates - the basis of the CA trust system, your Verisigns and StartComs and GoDaddys and so on. When you verify a typical site certificate, it provides its own certificate and all the intermediate certificates between itself and the root CA; you have the root CA certificate in your trust store; and OpenSSL does purpose checking on the server certificate and all the intermediates (as these are untrusted certificates, not from the trust store) as well as validating them in other ways. If you're checking a web server's certificate, it will make sure that certificate and all intermediate certificates have a key usage consistent with the web server purpose (or have no key usage extensions at all, for compatibility with very old certificates).
OpenSSL does, however, provide a mechanism by which you can limit the extent to which you (the entity doing the validation) trust a certificate in the trusted store. This is what the trust mechanism is all about.
OpenSSL understands an extended certificate format which I call
BEGIN TRUSTED, after the text that identifies it when written to PEM format. It's sometimes referred to in the documentation as the 'trusted certificate' format, but I don't like that term because they sometimes use the same term to refer to any certificate in the trust store, which just gets confusing.
It's a bad name, because what it actually does is limit the trust placed in the certificate. A regular certificate in the trust store, as I said, is trusted for all purposes. The
BEGIN TRUSTED certificate format lets you say 'I trust this certificate, but only for certain purposes'.
You can take an existing certificate and run it through
openssl x509 with the
-addreject parameters to explicitly state that you trust or distrust it for certain purposes. So if I take a CA cert and run this on it:
openssl x509 -in ca.pem -addtrust serverAuth -addreject clientAuth -out ca-partial-trust.pem`
I get out a
BEGIN TRUSTED style certificate file which says "this CA is explicitly trusted to issue certificates for SSL servers and explicitly distrusted to issue certificates for SSL clients", and doesn't express an opinion about its trustworthiness for other purposes. Again, this is actually a less trusted certificate, because OpenSSL treats certificates in the trust store which have absolutely no trust extensions as trusted for all purposes.
When you validate a certificate chain, OpenSSL 1.0.1k runs trust checks against the first certificate in the chain (the root certificate). OpenSSL git master runs trust checks against all certificates in the chain that came from the trusted certificate store. The way the trust check is written is that if there are any trust extensions in the certificate, then it must be trusted for the trust type for which the chain is being validated (that is, so long as any trust extensions are present, a certificate which isn't explicitly trusted for the trust type being checked will be rejected). The trust type can be specified by the code running the validation process, and they differ from but are similar to the 'purpose' types. For our web server validation case, the trust type is likely to be "SSL Server" (
X509_TRUST_SSL_SERVER in the code). If the certificates that have trust checks run on them aren't trusted for that purpose, the validation fails.
This mechanism has been around for a long time (since 1999), but isn't terribly commonly used with OpenSSL. The only distribution I've found that uses the extended
BEGIN TRUSTED format for its system certificate store is Arch, and I rather suspect they're actually doing it by accident (see here and here for some boring details on that). Most distributions keep their OpenSSL trust stores in basic format, with no trust extensions, and hence the certificates in them are trusted for all purposes. But if you do encounter it, that's what it's all about - remember the difference between trust and purpose.
There is a decent reason to have such a mechanism. Interested parties like browser and OS vendors are making something of a concerted effort to improve overall internet security by dropping old and potentially insecure CA certificates from their trust stores, but this can be difficult when certificates they issued are still being used in the real world. Sometimes we run into a situation where we know, say, that certificates signed with a bad old CA certificate are still being used for S/MIME purposes, but no servers should be using certificates issued by them any more. In this case, it is good to have a mechanism by which distributions can say 'this CA should be trusted only for mail certificates, not for server certificates'. This is the function served by the
TRUSTED CERTIFICATE feature. NSS has its own implementation of this, and Firefox and many vendor NSS distributions already include certain CA certificates in their trust stores with this kind of partial trust; as I mentioned above, its use with OpenSSL is less common, but is at least possible.
openssl verify to check certificates and chains manually, you can pass
-purpose to specify the purpose to be checked. If you don't pass
-purpose, no purpose checks will be run.
openssl verify, at present, cannot be made to run trust checks, which is a notable difference from the behaviour of most OpenSSL-based applications. Applications which use OpenSSL's SSL functions, unless they explicitly set a trust type, will use the SSL Server trust type unless they identify themselves as server applications, which will use the SSL Client trust type. I sent a patch for this, which just hooks up a few bits that already exist so that when you pass
-purpose, a trust type is chosen accordingly and trust checks are run against that trust type.