Sun Sep 4, 2016
Many intranets setup a certificate authority (CA) in order to have a good internal SSL/TLS infrastructure. As computers inside the network are managed, to automatically install the CA certificate on all computers is not a problem. Things are different when you have people bringing external computers to work on the internal network, network administrators can request that people install the CA certificate, if they do, they are trusting a lot more than access to encrypted sites inside the internal network, by default a trusted CA can be used to generate certificates for any site, and browsers will accept them. I wanted a way to be able to trust the CA certificate without exposing my encrypted sessions to, for example, a rogue intranet administrator generating certificates for *.google.com.
There is a certificate extension (options that can be embedded on a certificate) that tell encryption libraries for which domains is the CA restricted, it is called Name Constraints.
If you control the CA, you can think about adding the name constraints, but what to do when you are given a CA certificate and you want to restrict it? Create a new CA that cross-certify the original one.
Let’s do one example using OpenSSL tools. First create a new directory to work
on, we will do it on
/dev/shm/crossca in order to be sure all files are
generated on RAM and not stored on disk.
TOPDIR=/dev/shm/crossca mkdir $TOPDIR chmod 700 $TOPDIR cd $TOPDIR
We will need the source certificate in PEM format, let call it ca.crt. Copy it to the work directory
cp /path_to_ca.crt $TOPDIR/ca.crt
Now create a new CA
mkdir -p $TOPDIR/CA/private $TOPDIR/CA/newcerts touch $TOPDIR/CA/index.txt openssl req \ -new -x509 \ -days 730 \ -newkey rsa:2048 \ -sha256 \ -out ./CA/cacert.pem \ -keyout CA/private/cakey.pem \ -subj "/O=Example Organization/CN=Example Root CA"
OpenSSL will ask you for a password, set anything you want and take note of it.
You must set a good Organization name (O) and Common name (CN). Play with the
amount of days your CA will be valid. I try to set it to a value that generate
the same expiration day of our source ca.crt. You can get information about
ca.crt with the command:
openssl x509 -in ca.crt -text -noout | less
... Validity Not Before: Jul 22 16:09:17 2016 GMT Not After : Jul 22 16:09:17 2036 GMT ... X509v3 extensions: X509v3 Authority Key Identifier: keyid: xx:xx:xx:xx:xx:xx:xx:xx:xx:xx:xx:xx:xx:xx:xx:xx:xx:xx:xx:xx X509v3 Basic Constraints: critical CA:TRUE X509v3 Key Usage: critical Digital Signature, Non Repudiation, Certificate Sign, CRL Sign X509v3 Subject Key Identifier: yy:yy:yy:yy:yy:yy:yy:yy:yy:yy:yy:yy:yy:yy:yy:yy:yy:yy:yy:yy Authority Information Access: OCSP - URI:http://ca.example.com:80/ca/ocsp
It is important to see which extensions are used on the source ca.crt because OpenSSL do not automate copying these extensions to the new certificate.
Now, copy your OpenSSL configuration file, we are going to make changes to it in
order to not touch the original file and use directories a non root user can
openssl version -d can help you locate it:
cp $OPENSSLDIR/openssl.cnf $TOPDIR
Edit the file and replace the lines:
[ CA_default ] dir = /etc/pki/CA # Where everything is kept
[ CA_default ] dir = ./CA # Where everything is kept
Create a file named
$TOPDIR/ossl_domain_com.cfg with the following content:
[ req ] prompt=no distinguished_name=req_distinguished_name req_extensions=domain_ca [ req_distinguished_name ] O=Example Organization CN=Original Certificate CN [ domain_ca ] authorityKeyIdentifier=keyid basicConstraints=critical,CA:true keyUsage=critical,digitalSignature,nonRepudiation,keyCertSign,cRLSign subjectKeyIdentifier=hash authorityInfoAccess=OCSP;URI:http://ca.example.com:80/ca/ocsp nameConstraints=critical,permitted;DNS:.example.com
Adapt the file to your needs, try to copy the information of the source ca.crt
[ req_distinguished_name ] and set all the extensions found of ca.crt on
[ domain_ca ]. Read OpenSSL documentation if you need help. The important new
nameConstraints=critical,permitted;DNS:.example.com that restrict
the CA to the domain name
Now create the cross-certification:
openssl ca \ -config openssl.cnf \ -create_serial \ -preserveDN \ -extensions domain_ca \ -extfile ossl_domain_com.cfg \ -ss_cert ca.crt \ -startdate 160722160917Z \ -enddate 360722160917Z \ -out crosscertified_ca.crt
Again, play with the start and end date to your liking.
The new cross-certified CA is now in the file
crosscertified_ca.crt. Import it
to your browser and it should be able to be used to trust servers only from the
Note: Firefox support name constraints on all platforms, Chrome support it on Linux and Windows but not on macOS because it uses the platform SSL libraries and as always macOS is behind the times.