Name constraints are a little-known gem of a feature in X.509 certificates, which are used for TLS. A name constraint is a certificate extension, applied to a CA’s certificate, that contains a whitelist and/or blacklist of names (e.g. domain names) that the CA can issue certs for. There are 3 main reasons why a CA might want to have a name constraint:
- A commercial public CA might sell you a CA for your personal use, with a name constraint that whitelists only domains that you’ve demonstrated that you control. This works similarly to selling you an end-entity certificate, except that now you can renew your end-entity cert (perhaps rotating keys) without needing to interact with the commercial CA, since you now have your own CA you can use. This use case isn’t really used in practice, mostly because of regulatory capture (commercial CA’s have successfully made sure that the CA/Browser Forum regulations prohibit issuing name-constrained CA’s to members of the general public).
- A private CA, perhaps belonging to a corporate intranet or other internal network, wants to limit the damage it can do if it is later compromised, so it includes a name constraint that whitelists a TLD belonging to its intranet. Thus, if the private CA is later compromised, it can’t be used to attack websites on the public Internet. This is the stated motivation for Netflix’s BetterTLS test suite.
- A public CA wants to prevent itself from issuing certificates for specific TLD’s that have unique regulatory requirements that the CA isn’t able to comply with, so it includes a name constraint that blacklists those TLD’s. Thus, users of those TLD’s don’t have to worry about the public CA affecting their environment. The best-known example of this is that the Let’s Encrypt CA has sometimes used a name constraint that blacklisted
.mil, presumably because the U.S. military PKI has regulatory requirements that Let’s Encrypt didn’t want to mess with.
All of these use cases are quite valid and legitimate, but they assume one thing: that the CA wanted to be subjected to a name constraint. What do you do if you’ve been given a CA certificate that doesn’t have a name constraint, and you want to only trust it for a subset of names?
One approach is to cross-sign the CA – in other words, you create your own CA with the name constraint you want, and then produce a cert that’s identical to the CA you want to constrain, except with an issuer and signature from your constrained CA. This is not too hard; I actually wrote a tool called
crosssignnameconstraint that does this for you. It works pretty well, but it does produce two potentially unwanted side effects:
- It transforms a root CA into an intermediate CA.
- It changes the fingerprint of the CA.
Both of these can result in unintended behavior from poorly designed TLS implementations, e.g. certificate pinning and EV certificates may behave differently when you cross-sign a certificate.
So, is there another approach we can use? If you’re on Windows, then the answer is yes!
Windows CryptoAPI stores certificates in a kind of funky way: certificates are stored not just in DER-encoded form, but in a custom data structure called a “certificate blob” that allows “properties” to be attached to the certificates. One example of such a property is a constraint on which extended key usages a CA can be used for. For example, you might want to import a CA that’s allowed to issue TLS client certificates but not allowed to issue TLS server certificates or sign code. When I was looking at the list of properties, most looked pretty mundane, but one jumped out: what the heck is this
CERT_ROOT_PROGRAM_NAME_CONSTRAINTS_PROP_ID thing? Curiously, the documentation doesn’t say what it does; it’s simply marked as “Reserved”. However, looking at other properties’ documentation, it became clear that Microsoft has a habit of encoding extensions in ASN.1 format and stuffing the resulting binary data into a property. I also noticed that the Windows utility
certutil actually has a way to edit arbitrary properties of certificates; it’s the (confusingly named)
So, I cooked up a certificate that had a name constraint blacklisting
.github.io, extracted the ASN.1 binary data for the name constraint extension into a hex blob, and instructed
certutil to set it on the built-in root CA that GitHub Pages uses. I then tried to visit GitHub Pages in Chromium, and boom: a TLS error. Inspecting the CryptoAPI logs confirmed that the failure was due to a name constraint.
So, why is this feature there? Is it being used anywhere? As far as I can tell, it’s not used by anyone. The name of the property suggests that Microsoft intended to ship name constraints with its 3rd-party root CA’s, but I briefly checked the entire list of 3rd-party root CA’s, and it appears that none of them have this property set. The property was added to Windows over a decade ago (if Wine commit dates are to be believed), so perhaps Microsoft used it in the past and then decided to stop. Or perhaps they planned to use it, abandoned the plan, and left the code in place. Maybe it was intended for private PKI purposes, but later got shelved. Microsoft doesn’t document what this property does anywhere, and in fact I was unable to find even a single mention of this property on the public Internet besides the source code to Microsoft’s header files and the Microsoft documentation that marks it as “Reserved”. Amusingly,
certutil knows exactly what this property is, and if you enable verbose output, it will even happily deserialize the name constraint ASN.1 data into a nice human-readable representation of the name constraint you’re applying.
Reserved or not, it definitely works, and it seems like a useful addition to a PKI toolbox. NSS, GnuTLS, and p11-kit do support external name constraints as well, but NSS requires a recompile if you want to change them, and the support in GnuTLS and p11-kit only works on a few distros (and no mainstream browsers). By supporting external name constraints that are actually enforced in arbitrary applications (including mainstream browsers) and can be edited by the user, Windows is leading the way on an excellent feature (Windows support for this long predates GnuTLS and p11-kit support) – it’s too bad that Microsoft didn’t bother to advertise this as a selling point.
More details on how we’re going to use this functionality in Namecoin will be in a future post.