Fully Verifying Cross-Signed SSL Certificates
There seems to have been a rash of Root and Intermediate CA certificate expirations recently, and while I was busy laughing at the people unprepared for this I got the results of a scheduled security scan telling me that on of the intermediates on our primary site had expired a week ago.
“Woops. Wait… a week ago?” I had a hard time believing that a busy e-commerce site could go a week with broken SSL and zero complaints.
After digging into it I found that when we renewed our certs with Sectigo just a couple months ago they provided us with a cert bundle that included their signing CA, and the expired intermediate. [gee, thanks Sectigo] The reason that nothing broke in an obvious manner was that the Sectigo CA was also cross-signed by a root, and another intermediate CA. So, unless someone was running a 5+ year old device that had never updated, there was at least one valid certification path.
That said:
- I can’t abide an invalid cert hanging out there, and the security scan sure doesn’t either.
- I need to test both cert paths before pushing changes to a live site.
The trouble is that an openssl verify
will always choose the shortest path, eg: me > Sectigo > root, and I want to test me > Sectigo > intermediate > root.
In order to test a specific path like this you need to manually construct it.
- Find the root certificate you want to use.
In my case [CentOS] the certificate was bundled into/etc/pki/ca-trust/extracted/pem/tls-ca-bundle.pem
, and from prior testing I knew it was named “AAA Certificate Services”. Super-trustworthy name, I know. But it’s actually Comodo’s root, and Comodo is now Sectigo, and I don’t know why they’ve done any of this.
Anyhow, if your cert is stuck in a bundle, copy/paste it into it’s own file somewhere, eg:aaa_root.crt
If you’re using a Debian-based distro you’ll likely find the individual cert files symlinked in:/etc/ssl/certs
- Break out your intermediates into their own files.
This ensures that you can build out the chain exactly as you’d like.
eg:usertrust_intermediate.crt
andsectigo_intermediate.crt
- Construct the
openssl verify
command:
openssl verify \
-show_chain \ # make sure it's following the chain you've laid out
-CAfile aaa_root.crt \ # use ONLY this root cert
-untrusted usertrust_intermediate.crt \ # untrusted intermediate 1
-untrusted sectigo_intermediate.crt \ # untrusted intermediate 2
my_site.crt
Note: The comments after the escape are invalid syntax, make sure you remove them if you copy/paste this.
You should get output like:
my_site.cert: OK
Chain:
depth=0: CN = example.com (untrusted)
depth=1: C = GB, ST = Greater Manchester, L = Salford, O = Sectigo Limited, CN = Sectigo RSA Domain Validation Secure Server CA (untrusted)
depth=2: C = US, ST = New Jersey, L = Jersey City, O = The USERTRUST Network, CN = USERTrust RSA Certification Authority (untrusted)
depth=3: C = GB, ST = Greater Manchester, L = Salford, O = Comodo CA Limited, CN = AAA Certificate Services
4. Repeat as necessary for any other cross-signed paths you’d like to validate.