Reputation: 2363
I'm building a own certificate chain with following componenents:
Root Certificate - Intermediate Certificate - User Certificate
Root Cert is a self signed certificate, Intermediate Certificate is signed by Root and User by Intermediate.
Now I want to verify if a User Certificate has its anchor by Root Certificate.
With
openssl verify -verbose -CAfile RootCert.pem Intermediate.pem
the validation is ok. In the next step I validate the User Cert with
openssl verify -verbose -CAfile Intermediate.pem UserCert.pem
and the validation shows
error 20 at 0 depth lookup:unable to get local issuer certificate
What is wrong?
Upvotes: 236
Views: 906883
Reputation: 24806
If using Let's Encrypt certificates generated with certbot, you will end up with the following files in /etc/letsencrypt/live/xxx.yyy.zzz/
cert.pem
chain.pem
openssl x509 -noout -text chain.pem
C = US, O = Let's Encrypt, CN = R11
in my casefullchain.pem
server.pem
and chain.pem
concatenatedSo to verify that cert.pem
is valid
cd /etc/letsencrypt/live/xxx.yyy.zzz/
openssl verify -untrusted chain.pem cert.pem
cert.pem: OK
The above tell to verify cert.pem
using chain.pem
as an intermediate untrusted CA, and using the root ca bundle in the linux distro implicitly (in my case /etc/ssl/certs/ca-bundle.crt
.). Although the chain.pem
is "untrusted" it will pass the verify since that "untrusted" intermediate CA it's signed by one of the CA in the ca-bundle.crt
.
So if we explicit specify the -CAfile
it would be:
openssl verify -CAfile /etc/ssl/certs/ca-bundle.crt -untrusted chain.pem cert.pem
cert.pem: OK
Upvotes: 0
Reputation: 10868
1. If you only want to verify that issuer of UserCert.pem
is actually Intermediate.pem
do the following (example uses: OpenSSL 1.1.1
):
openssl verify -no-CAfile -no-CApath -partial_chain -trusted Intermediate.pem UserCert.pem
and you will get:
UserCert.pem: OK
or
UserCert.pem: verification failed
2. Same thing to verify that the issuer of Intermediate.pem
is RootCert.pem
openssl verify -no-CAfile -no-CApath -partial_chain -trusted RootCert.pem Intermediate.pem
If both of the above verifications succeed then the certificate chain is verified.
Upvotes: 14
Reputation: 1021
If you have e.g. cachain.pem
containing the whole CA chain starting with the root certificate and e.g. mycert.pem
containing the certificate to check then
openssl verify -CAfile cachain.pem -untrusted cachain.pem mycert.pem
equivalent to (as openssl will read only the first certificate from CAfile
)
openssl verify -CAfile root.pem -untrusted cachain.pem mycert.pem
will do the job. Some sources mention that openssl verify
accepts several -untrusted
options, but that didn't work for me with some version of openssl
.
Upvotes: 1
Reputation: 3169
From verify
documentation:
If a certificate is found which is its own issuer it is assumed to be the root CA.
In other words, root CA needs to be self signed for verify to work. This is why your second command didn't work. Try this instead:
openssl verify -CAfile RootCert.pem -untrusted Intermediate.pem UserCert.pem
It will verify your entire chain in a single command.
Upvotes: 301
Reputation: 161
openssl verify
doesn't handle certificate chains the way SSL clients do. You can replicate what they do with a three step process:
(cat cert.pem chain.pem | diff -q fullchain.pem -) && \
openssl verify chain.pem && \
openssl verify -CAfile chain.pem cert.pem
This will confirm that fullchain.pem
== cert.pem
+ chain.pem
and that it is legitimate according to the CAs installed on your system (usually in /etc/ssl/certs
from your ca-certificates
package).
If you are trying to validate a letsencrypt/ACME, be aware that letsencrypt gives you four files per domain:
cert.pem
chain.pem
fullchain.pem
privkey.pem
Of these, your cert pair is (fullchain.pem
, privkey.pem
), not (cert.pem
, privkey.pem
) nor (chain.pem
, privkey.pem
).
For example, in nginx.conf, you would put:
ssl_certificate /etc/letsencrypt/domain.example.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/domain.example.com/privkey.pem;
Upvotes: 5
Reputation: 460
I've had to do a verification of a letsencrypt certificate and I did it like this:
Issue this command:
$ openssl verify -CAfile letsencrypt-root-cert/isrgrootx1.pem.txt -untrusted letsencrypt-intermediate-cert/letsencryptauthorityx3.pem.txt /etc/letsencrypt/live/sitename.tld/cert.pem
/etc/letsencrypt/live/sitename.tld/cert.pem: OK
Upvotes: 8
Reputation: 3088
You can easily verify a certificate chain with openssl. The fullchain will include the CA cert so you should see details about the CA and the certificate itself.
openssl x509 -in fullchain.pem -text -noout
Upvotes: -5
Reputation: 31751
That's one of the few legitimate jobs for cat
:
openssl verify -verbose -CAfile <(cat Intermediate.pem RootCert.pem) UserCert.pem
Update:
As Greg Smethells points out in the comments, this command implicitly trusts Intermediate.pem. I recommend reading the first part of the post Greg references (the second part is specifically about pyOpenSSL and not relevant to this question).
In case the post goes away I'll quote the important paragraphs:
Unfortunately, an "intermediate" cert that is actually a root / self-signed will be treated as a trusted CA when using the recommended command given above:
$ openssl verify -CAfile <(cat geotrust_global_ca.pem rogue_ca.pem) fake_sometechcompany_from_rogue_ca.com.pem fake_sometechcompany_from_rogue_ca.com.pem: OK
It seems openssl will stop verifying the chain as soon as a root certificate is encountered, which may also be Intermediate.pem if it is self-signed. In that case RootCert.pem is not considered. So make sure that Intermediate.pem is coming from a trusted source before relying on the command above.
Upvotes: 79
Reputation: 496
After breaking an entire day on the exact same issue , with no prior knowledge on SSL certificates, i downloaded the CERTivity Keystores Manager and imported my keystore to it, and got a clear-cut visualisation of the certificate chain.
Screenshot :
Upvotes: 4
Reputation: 10489
The problem is, that openssl -verify
does not do the job.
As Priyadi mentioned, openssl -verify
stops at the first self signed certificate, hence you do not really verify the chain, as often the intermediate cert is self-signed.
I assume that you want to be 101% sure, that the certificate files are correct before you try to install them in the productive web service. This recipe here performs exactly this pre-flight-check.
Please note that the answer of Peter is correct, however the output of openssl -verify
is no clue that everything really works afterwards. Yes, it might find some problems, but quite not all.
Here is a script which does the job to verify a certificate chain before you install it into Apache. Perhaps this can be enhanced with some of the more mystic OpenSSL magic, but I am no OpenSSL guru and following works:
#!/bin/bash
# This Works is placed under the terms of the Copyright Less License,
# see file COPYRIGHT.CLL. USE AT OWN RISK, ABSOLUTELY NO WARRANTY.
#
# COPYRIGHT.CLL can be found at http://permalink.de/tino/cll
# (CLL is CC0 as long as not covered by any Copyright)
OOPS() { echo "OOPS: $*" >&2; exit 23; }
PID=
kick() { [ -n "$PID" ] && kill "$PID" && sleep .2; PID=; }
trap 'kick' 0
serve()
{
kick
PID=
openssl s_server -key "$KEY" -cert "$CRT" "$@" -www &
PID=$!
sleep .5 # give it time to startup
}
check()
{
while read -r line
do
case "$line" in
'Verify return code: 0 (ok)') return 0;;
'Verify return code: '*) return 1;;
# *) echo "::: $line :::";;
esac
done < <(echo | openssl s_client -verify 8 -CApath /etc/ssl/certs/)
OOPS "Something failed, verification output not found!"
return 2
}
ARG="${1%.}"
KEY="$ARG.key"
CRT="$ARG.crt"
BND="$ARG.bundle"
for a in "$KEY" "$CRT" "$BND"
do
[ -s "$a" ] || OOPS "missing $a"
done
serve
check && echo "!!! =========> CA-Bundle is not needed! <========"
echo
serve -CAfile "$BND"
check
ret=$?
kick
echo
case $ret in
0) echo "EVERYTHING OK"
echo "SSLCertificateKeyFile $KEY"
echo "SSLCertificateFile $CRT"
echo "SSLCACertificateFile $BND"
;;
*) echo "!!! =========> something is wrong, verification failed! <======== ($ret)";;
esac
exit $ret
Note that the output after
EVERYTHING OK
is the Apache setting, because people usingNginX
orhaproxy
usually can read and understand this perfectly, too ;)
There is a GitHub Gist of this which might have some updates
Prerequisites of this script:
/etc/ssl/certs
as usual for example on UbuntuDIR
where you store 3 files:
DIR/certificate.crt
which contains the certificateDIR/certificate.key
which contains the secret key for your webservice (without passphrase)DIR/certificate.bundle
which contains the CA-Bundle. On how to prepare the bundle, see below../check DIR/certificate
(this assumes that the script is named check
in the current directory)CA-Bundle is not needed
. This means, that you (read: /etc/ssl/certs/
) already trusts the signing certificate. But this is highly unlikely in the WWW.How to create the certificate.bundle
file?
In the WWW the trust chain usually looks like this:
/etc/ssl/certs
certificate.crt
)Now, the evaluation takes place from bottom to top, this means, first, your certificate is read, then the unknown intermediate certificate is needed, then perhaps the cross-signing-certificate and then /etc/ssl/certs
is consulted to find the proper trusted certificate.
The ca-bundle must be made up in excactly the right processing order, this means, the first needed certificate (the intermediate certificate which signs your certificate) comes first in the bundle. Then the cross-signing-cert is needed.
Usually your CA (the authority who signed your certificate) will provide such a proper ca-bundle-file already. If not, you need to pick all the needed intermediate certificates and cat
them together into a single file (on Unix). On Windows you can just open a text editor (like notepad.exe
) and paste the certificates into the file, the first needed on top and following the others.
There is another thing. The files need to be in PEM format. Some CAs issue DER (a binary) format. PEM is easy to spot: It is ASCII readable. For mor on how to convert something into PEM, see How to convert .crt to .pem and follow the yellow brick road.
Example:
You have:
intermediate2.crt
the intermediate cert which signed your certificate.crt
intermediate1.crt
another intermediate cert, which singed intermediate2.crt
crossigned.crt
which is a cross signing certificate from another CA, which signed intermediate1.crt
crossintermediate.crt
which is another intermediate from the other CA which signed crossigned.crt
(you probably will never ever see such a thing)Then the proper cat
would look like this:
cat intermediate2.crt intermediate1.crt crossigned.crt crossintermediate.crt > certificate.bundle
And how can you find out which files are needed or not and in which sequence?
Well, experiment, until the check
tells you everything is OK. It is like a computer puzzle game to solve the riddle. Every. Single. Time. Even for pros. But you will get better each time you need to do this. So you are definitively not alone with all that pain. It's SSL, ya' know? SSL is probably one of the worst designs I ever saw in over 30 years of professional system administration. Ever wondered why crypto has not become mainstream in the last 30 years? That's why. 'nuff said.
Upvotes: 19