How to trust a CA only for a specific domain
Does your company use their own CA for internal websites in their domain and you want to trust it, but not trust it for any other domains?
Do you want your browser to trust your development website signed by the Let’s Encrypt staging CA, but not trust it for any other websites?
Then this post might help you.
Some trusted root CAs have misbehaved in the past. In 2011 a fraudulent SSL certificate issued by DigiNotar was used for a man-in-the-middle attack. In 2013 a French CA, ANSSI, had the same problem and in 2014 NIC of India. This might be a good reason to trust fewer CAs or trust them for fewer domains.
My solution
x509 CA certificates can contain the name constraint extension. Here you can specify which domains a CA is allowed to issue certificates for.
If a CA already has name constraints with a sane list of allowed domains you do not have to do anything. Unfortunately most do not and can issue certificates for any domain.
A solution to this problem, based on an answer on serverfault.com, is to create your own CA root certificate and add it to your trust store. Your CA can then cross-sign any CA certificate that you want to constrain while adding a name constraint to it. This is what I will be doing in this post.
Creating your own CA
Creating your own CA root certificate that you trust can be dangerous. Make sure to keep your private key secure - if you trust it and someone else gains access to it they can man-in-the-middle your TLS connections.
That said, let’s create a CA root certificate based on this mozilla guide. I have created a script to help with this:
Run it with ./genCrossSignCa.sh.
Cross Signing Certificates
Next you need to download the CA certificate that you want to cross-sign. For this example I will use the Let’s Encrypt staging environment root certificate and trust it to sign certificates for example.org and *.example.org.
Download the certificate from Let’s Encrypt’s website and call it lets-encrypt-staging-ca.pem.cer.
To cross sign it with name constraints I have created this script:
Run it with ./crossSignCert.sh example.org lets-encrypt-staging-ca.pem.cer.
The new cross signed certificate with name constraints can be found under ./cross-signed-certs/cross-signed-lets-encrypt-staging-ca.pem.cer.
To trust the Let’s Encrypt staging CA to issue certificates for example.org you need to import cross-sign-ca.pem.cer and cross-signed-lets-encrypt-staging-ca.cer into your trust store to trust it.
Example Results
When you go to a site signed by the Let’s Encrypt staging CA your certificate path will look something like this:
After you cross signed the CA with your root CA you will see:
If the site your are visiting does not match your name constraints you will see: