150

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?

John Kugelman
  • 307,513
  • 65
  • 473
  • 519
Indra
  • 1,503
  • 2
  • 9
  • 4

8 Answers8

195

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.

moffeltje
  • 4,095
  • 4
  • 24
  • 49
Priyadi
  • 2,101
  • 1
  • 10
  • 6
  • 3
    I'm up-voting this answer as I recently had to do this and after trying different options listed by `man verify`, I found that the `-untrusted` parameter is the correct one to use when specifying the intermediate certificate. – Anthony Geoghegan Feb 10 '15 at 12:45
  • I think the second answer: http://stackoverflow.com/a/31205833/173062 is more accurate - it passes the chain of certificates to the -CAfile parameter. – Glenjamin Apr 15 '16 at 07:37
  • 3
    `-untrusted` doesn't check whether certificate chain is fully valid. Please consider to pass both intermediate and root to command as `-CAfile` as other questions suggests. – Envek Jun 22 '16 at 18:06
  • 2
    Use -untrusted for Intermediate.pem if it is possible that the following happens: https://mail.python.org/pipermail/cryptography-dev/2016-August/000676.html – Greg Smethells Mar 17 '17 at 20:34
  • 2
    That's not what OP asked for, but in case you want to verify NOT self-signed chain, then use system/browser CA file instead of your own. For example on OS X with openssl from homebrew use: `openssl verify -CAfile /usr/local/etc/openssl/cert.pem -untrusted Intermediate.pem UserCert.pem` – Greg Dubicki Mar 22 '17 at 13:35
57

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.

Peter
  • 24,684
  • 4
  • 36
  • 46
  • Will this actually verify the intermediate cert against the root cert? – augurar Aug 10 '16 at 18:45
  • It does. I just re-ran the commands with a chain that I know is correct (it serves production traffic for my employer), and then again with another, unrelated root certificate. See [the transcript gist](https://gist.github.com/pschultz/528864ec770e6469cab3c85418b34ab7). – Peter Aug 11 '16 at 09:07
  • Thanks, was wondering about the order of concatenation, which apparently varies with tooling. – Dominic Cerisano Sep 12 '16 at 01:21
  • 11
    WARNING: do *NOT* use this if Intermediate.pem is at all untrusted. For more info read here: https://mail.python.org/pipermail/cryptography-dev/2016-August/000676.html – Greg Smethells Mar 17 '17 at 20:32
  • 1
    Thanks for pointing that out, Greg. When I gave the answer, I downloaded both roots and intermediates from the issuers' homepages, so the thought didn't occur to me. I have updated the answer to make clear that the intermediate is implictly trusted with this command. – Peter Mar 20 '17 at 10:37
  • downvoted because intermediates should be added with the `-untrusted` option – David 天宇 Wong Aug 01 '17 at 10:43
  • Same command works with `openssl verify -CAfile RootCert.pem -untrusted – davenpcj Feb 15 '19 at 22:01
  • @davenpcj or just use `-untrusted` option multiple times. from man _This option can be specified more than once to include untrusted certificates from multiple files_. `openssl verify -CAfile RootCert.pem -untrusted Intermediate1.pem -untrusted Intermediate2.pem UserCert.pem` – Julien May 24 '19 at 13:03
  • openssl ver. 1.0.2r Feb 2019 requires the use of the `-untrusted` flag for the Intermediate.pem so the concatenation using `cat` did not work for me. Needed `openssl verify -verbose -CAfile ./ROOT-CA.pem -untrusted ./INTERMEDIATE-CA.pem webCert.pem` – Tony Barganski Aug 15 '19 at 11:27
  • @Peter I get `error 2 at 1 depth lookup:unable to get issuer certificate` using this answer while getting `OK` using @Priyadi's answer. Any ideas? – somenickname Nov 05 '19 at 01:32
  • 1
    @somenickname, see Tony's comment. The -untrusted option is preferable anyway. I suggest you ask your own question if you want further help. Comments aren't the right place to debug your problem. – Peter Nov 05 '19 at 08:07
  • How can an "intermediate" cert be self-signed? If it's a self-signed CA, surely it's a root cert, not an intermediate cert. As such, unless I miss something, using it as a CAfile (trusted) should mean it's trusted [feature], using it as an intermediate (untrusted) should have no effect because the cert that proves it is OK is itself [not a bug]. – Ben Aveling Feb 21 '20 at 06:48
  • To answer my own comment, the concern is that an intermediate cert might not really be an intermediate cert - adding the cert to -CAfile, instead of to -untrusted, allows an attacker to short-circuit the certificate chain. It's misleading to say the command 'implicitly trusts the intermediate cert', more accurate to say that it 'explicitly trusts the intermediate cert'. – Ben Aveling Feb 21 '20 at 07:11
17

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 using NginX or haproxy usually can read and understand this perfectly, too ;)

There is a GitHub Gist of this which might have some updates

Prerequisites of this script:

  • You have the trusted CA root data in /etc/ssl/certs as usual for example on Ubuntu
  • Create a directory DIR where you store 3 files:
    • DIR/certificate.crt which contains the certificate
    • DIR/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.
  • Now run the script: ./check DIR/certificate (this assumes that the script is named check in the current directory)
  • There is a very unlikely case that the script outputs 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.
  • For this test port 4433 must be unused on your workstation. And better only run this in a secure environment, as it opens port 4433 shortly to the public, which might see foreign connects in a hostile environment.

How to create the certificate.bundle file?

In the WWW the trust chain usually looks like this:

  • trusted certificate from /etc/ssl/certs
  • unknown intermediate certificate(s), possibly cross signed by another CA
  • your certificate (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.

Community
  • 1
  • 1
Tino
  • 7,380
  • 3
  • 48
  • 52
  • To the downvoter: Please explain what is wrong with my answer. Thanks. – Tino Aug 16 '16 at 14:32
  • 3
    I am one of the downvoters. What triggered the downvote is this : "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". I don't think SSL is a special case. Problems like this should have a deterministic solution. – ychaouche Sep 19 '16 at 13:00
  • 2
    @ychaouche Thanks! Like you I like desterministic things. The question was, "What is wrong" and how to do it with "openssl verify". As we are on stackoverflow, I explained that, followed by a programmatic (thus deterministic) yes/no answer. You can even use it to automate checks for the new Bundle before installing it into production. This fully answers the question. What you dislike is that I told about the frustration on "How to create a proper bundle?". As I think there cannot be a short deterministic answer for that, answering this would be offtopic in the context here. – Tino Sep 22 '16 at 15:00
  • 8
    "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." Obviously intermediate certificates are never self signed (if they were they'd be root certificates). And the whole point of verification is to check that you have included all the certificates in the chain all the way to a trusted root certificate. This is precisely what the openssl verify does. However, openssl tends to be rather conservative with its trusting policies... – Timo Nov 29 '16 at 14:59
  • ...this causes some confusion at times, when browsers for instance may trust some intermediate CAs directly meaning that things may work just fine without the full chain in real life, but openssl verify will fail. – Timo Nov 29 '16 at 15:02
  • 4
    "often the intermediate cert is self-signed". This is wrong, and terminology confusions like this make it hard for newcomers to understand a topic that actually is rather simple when explained in the right way. From RFC 5280: "[...] CA certificates may be further divided into three classes: cross-certificates, self-issued certificates, and self-signed certificates. Cross-certificates are CA certificates in which the issuer and subject are different entities. Cross-certificates describe a trust relationship between the two CAs. [...]". – Dr. Jan-Philip Gehrcke Apr 21 '17 at 00:58
  • The "intermediate CA certificate" is, using the RFC 5280 terminology, actually a cross-certificate. It can never be a self-signed certificate. Those two are mutually exclusive. Ref: https://tools.ietf.org/html/rfc5280 – Dr. Jan-Philip Gehrcke Apr 21 '17 at 00:58
  • This is a bad answer. Instead what OP could have done: verify that the chain doesn't work between the CA and the user cert if the intermediate is not included with the option `-untrusted`. – David 天宇 Wong Aug 01 '17 at 10:46
  • Another downvote: there is already enough confusion while explaining SSL certificates, their validity and how chains come in to play, but to have a blantly wrong "the intermediate cert is self-signed" statement near the beginning of the answer is just misleading. – ravemir Sep 13 '18 at 10:06
  • This answer does not use `openssl verify`, so it does not answer the question. – reinierpost Jun 04 '20 at 11:26
7

I've had to do a verification of a letsencrypt certificate and I did it like this:

  1. Download the root-cert and the intermediate-cert from the letsencrypt chain of trust.
  2. 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
    
John Kugelman
  • 307,513
  • 65
  • 473
  • 519
Michael
  • 368
  • 3
  • 13
  • 2
    Well, seems that John didn't like it that I said thank you. I insist on the "Thank You" tough, so here's the deleted text: Hope it helps you for your letsencrypt certs. Thanks for Priyadi, your solution helped me finding this command. Please make sure to upvote his solution. – Michael May 20 '20 at 12:18
4

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 :

enter image description here

praveen.chandran
  • 440
  • 4
  • 10
  • 2
    Doesn’t try to answer question which is about how to use `openssl verify`. – binki Apr 10 '17 at 05:11
  • yes but this kind of tool can give you the needed visualization of that kind of things if you do not understand the cryptic information of openssl command line tools :) So here's my upvote, there might be some online stuff that do that as well. – David 天宇 Wong Aug 01 '17 at 10:46
3

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
Marinos An
  • 6,191
  • 2
  • 35
  • 68
  • is there any equivalent command for ```openssl verify -no-CAfile -no-CApath -partial_chain -trusted Intermediate.pem UserCert.pem``` in Python 3.7? – Bogota May 20 '20 at 03:22
0

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;
-5

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

jorfus
  • 2,057
  • 19
  • 21
  • 5
    1) This is entirely without any kind of explanation. 2) this is an answer to a question the asker didn't ask, without any context. – Shadur May 22 '18 at 13:39