91

I have an end-entity/server certificate which have an intermediate and root certificate. When I cat on the end-entity certificate, I see only a single BEGIN and END tag. It is the only the end-entity certificate.

Is there any way I can view the intermediate and root certificate content. I need only the content of BEGIN and END tag.

In Windows I can see the full cert chain from the "Certification Path". Below is the example for the Stack Exchange's certificate.

enter image description here

From there I can perform a View Certificate and export them. I can do that for both root and intermediate in Windows. I am looking for this same method in Linux.

enter image description here

dr_
  • 28,763
  • 21
  • 89
  • 133
Anirban Nag 'tintinmj'
  • 1,115
  • 1
  • 10
  • 10

5 Answers5

85

From a web site, you can do:

openssl s_client -showcerts -verify 5 -connect stackexchange.com:443 < /dev/null

That will show the certificate chain and all the certificates the server presented.

Now, if I save those two certificates to files, I can use openssl verify:

$ openssl verify -show_chain -untrusted dc-sha2.crt se.crt 
se.crt: OK
Chain:
depth=0: C = US, ST = NY, L = New York, O = "Stack Exchange, Inc.", CN = *.stackexchange.com (untrusted)
depth=1: C = US, O = DigiCert Inc, OU = www.digicert.com, CN = DigiCert SHA2 High Assurance Server CA (untrusted)
depth=2: C = US, O = DigiCert Inc, OU = www.digicert.com, CN = DigiCert High Assurance EV Root CA

The -untrusted option is used to give the intermediate certificate(s); se.crt is the certificate to verify. The depth=2 result came from the system trusted CA store.

If you don't have the intermediate certificate(s), you can't perform the verify. That's just how X.509 works.

Depending on the certificate, it may contain a URI to get the intermediate from. As an example, openssl x509 -in se.crt -noout -text contains:

        Authority Information Access: 
            OCSP - URI:http://ocsp.digicert.com
            CA Issuers - URI:http://cacerts.digicert.com/DigiCertSHA2HighAssuranceServerCA.crt

That "CA Issuers" URI points to the intermediate cert (in DER format, so you need to use openssl x509 -inform der -in DigiCertSHA2HighAssuranceServerCA.crt -out DigiCertSHA2HighAssuranceServerCA.pem to convert it for further use by OpenSSL).

If you run openssl x509 -in /tmp/DigiCertSHA2HighAssuranceServerCA.pem -noout -issuer_hash you get 244b5494, which you can look for in the system root CA store at /etc/ssl/certs/244b5494.0 (just append .0 to the name).

I don't think there is a nice, easy OpenSSL command to do all that for you.

derobert
  • 107,579
  • 20
  • 231
  • 279
  • That's the main problem. I don't have the URL with me. I only have the certificate file. And I need the encrypted content between `BEGIN` and `END` tag (which you will get after just `cat`ing the .crt file. – Anirban Nag 'tintinmj' May 30 '17 at 18:56
  • @AnirbanNag'tintinmj' If you're just looking to decode the certificate, try `openssl x509 -in file.crt -noout -text` – derobert May 30 '17 at 19:25
  • I want the content of intermediate at root cert which is between the `BEGIN` and `END` tag. – Anirban Nag 'tintinmj' May 30 '17 at 19:29
  • Or if you can give a command which will extract the intermediate and root cert from the primary cert and save that into a file... I can live with that too. – Anirban Nag 'tintinmj' May 30 '17 at 19:30
  • @AnirbanNag'tintinmj' If it's not showing up with the `openssl x509` command, I don't think the intermediate certificate is there. (Unless you got some error from `openssl x509`). – derobert May 30 '17 at 19:41
  • With `openssl x509` command the whole chain is showing up. I need to extract that... and the intermediate certificate is there. You can try with the stackexchange certificate. – Anirban Nag 'tintinmj' May 30 '17 at 19:46
  • @AnirbanNag'tintinmj' if you take off that `-noout` it should output all the certificates (I'm not sure what SE certificate file you're using; could you post it?) – derobert May 30 '17 at 19:48
  • Let us [continue this discussion in chat](http://chat.stackexchange.com/rooms/59573/discussion-between-anirban-nag-tintinmj-and-derobert). – Anirban Nag 'tintinmj' May 30 '17 at 20:21
  • And failing all this, you go to the Digicert page, in the case of StackExchange, you double check them with the browser certs properties and have there at hand the intermediate and root certs. – Rui F Ribeiro May 31 '17 at 08:32
  • 1
    Also see [How to split a PEM file](https://serverfault.com/q/391396/145545) on Server Fault, [How to view all SSL certificates in a bundle?](https://serverfault.com/a/591263/145545) on Ubuntu.SE, [How can I split a CA certificate bundle into separate files?](https://stackoverflow.com/q/23644473/608639), etc. –  Jun 14 '17 at 13:15
67

tl;dr - one liner bash magic to dump all certs in the chain

openssl s_client -showcerts -verify 5 -connect wikipedia.org:443 < /dev/null |
   awk '/BEGIN CERTIFICATE/,/END CERTIFICATE/{ if(/BEGIN CERTIFICATE/){a++}; out="cert"a".pem"; print >out}'
for cert in *.pem; do 
        newname=$(openssl x509 -noout -subject -in $cert | sed -nE 's/.*CN ?= ?(.*)/\1/; s/[ ,.*]/_/g; s/__/_/g; s/_-_/-/; s/^_//g;p' | tr '[:upper:]' '[:lower:]').pem
        echo "${newname}"; mv "${cert}" "${newname}" 
done

Explanation in 2 steps

To dump all certs in the chain to the current dir as cert${chain_number}.pem:

openssl s_client -showcerts -verify 5 -connect your_host:443 < /dev/null |
 awk '/BEGIN CERTIFICATE/,/END CERTIFICATE/{ if(/BEGIN CERTIFICATE/){a++}; out="cert"a".pem"; print >out}' 

The bonus-track to rename them to their common name:

for cert in *.pem; do 
   newname=$(openssl x509 -noout -subject -in $cert | sed -nE 's/.*CN ?= ?(.*)/\1/; s/[ ,.*]/_/g; s/__/_/g; s/_-_/-/; s/^_//g;p' | tr '[:upper:]' '[:lower:]').pem
   mv $cert $newname 
done
Archemar
  • 31,183
  • 18
  • 69
  • 104
estani
  • 863
  • 7
  • 9
  • Is there any reason to choose ".pem" versus ".crt" as the extension since you're only renaming them, not changing the format? – Captain Man Feb 05 '19 at 21:23
  • 3
    No, not really. ".pem" is the format (base64 of the DER) ".crt" means it's a certificate but doesn't tell anything about the encoding. I was about to make it more consistent and name all pem... But I think is simpler to read in the one liner to understand where the certificate is getting downloaded (crt) and finish what I typically use in the Linux world (pem). Hope this clarifies things a bit... – estani Feb 06 '19 at 23:16
  • 1
    Yes it does, I realize PEM is a format but I often see crt and wasn't sure if it was a format. This actually helps explain why I see "crt" used so often. Thanks :) – Captain Man Feb 07 '19 at 16:59
  • This seems to only work if you already trust the source. I'm trying to download a custom CA but it's only downloading the first certificate, not the chain. – mjaggard Jul 05 '19 at 08:37
  • @mjaggard strange since I used it exactly for that case. That being said, the server might not be sending the root CA at all, if you take a look at my example, it used to download 3 certs now it's downloading only 2. I assume wikipedia server is not sending the root (there's really no point, if you don't have it, you won't trust it anyways...). I'm pretty sure it wasn't the case before. – estani Jul 05 '19 at 09:36
  • `openssl s_client -showcerts -verify 5 -connect stackexchange.com:443 < /dev/null` doesn't show the root cert, only the intermediate and server cert – bk201 Jun 01 '20 at 23:11
  • This blows up on certs without a Subject (ie. using Subject Alternative Name) – shuckc Mar 12 '21 at 14:03
  • @shuckc If your certs do not have a subject (not very common, though valid), then don't rename them. Just use the first step and you should get the certs in the chain order. The first steep should work even without a common name. – estani Mar 12 '21 at 15:05
  • The awk expression somehow generates incomplete files for cert2.pem and beyond (the files don't go all the way until END CERTIFICATE). The reason is that two of the certificates in my output contain the `END` string! The fix is to just specify `END CERTIFICATE` (and also `BEGIN CERTIFICATE` ) in the awk expression. – Albert Godfrind Jun 09 '21 at 14:21
  • Like this: ```openssl s_client -showcerts -verify 5 -connect data.api.oneatlas.airbus.com:443 < /dev/null | awk '/BEGIN CERTIFICATE/,/END CERTIFICATE/{ if(/BEGIN/){a++}; out="cert"a".pem"; print >out}'; for cert in *.pem; do newname=$(openssl x509 -noout -subject -in $cert | sed -nE 's/.*CN ?= ?(.*)/\1/; s/[ ,.*]/_/g; s/__/_/g; s/_-_/-/; s/^_//g;p' | tr '[:upper:]' '[:lower:]').pem; echo "${newname}"; mv "${cert}" "${newname}"; done``` – Albert Godfrind Jun 09 '21 at 14:23
7

I found out that with the option -verify 5 openssl is going deep in the chain showing all the cert, even that not included in your certificate deployment.

If you really want to understand which chain is provided with your certificate you should run:

openssl s_client -showcerts -partial_chain -connect YOUR_ENDPOINT:443 < /dev/null |less
Zioalex
  • 278
  • 3
  • 4
2

The above failed to work for me when I was using a Lets Encrypt Free Wildcard Certificate.

To be more specific:
I have a Kubernetes Cluster + Ingress Controller that's configured using a Lets Encrypt Free Wildcard Certificate for *.mydomain.dev, it's hosting the following 2 domain names:

  • grafana.mydomain.dev
  • prometheus.mydomain.dev

I had to add the -servername flag following per this site: https://community.letsencrypt.org/t/where-can-i-download-the-trusted-root-ca-certificates-for-lets-encrypt/33241/2

export DOMAIN=grafana.mydomain.dev
openssl s_client -showcerts -verify 5 -connect $DOMAIN:443 -servername $DOMAIN < /dev/null 2> /dev/null | awk '/BEGIN/,/END/{ if(/BEGIN/){a++}; print}'

I also tweaked the chain so it gives me the full cert + intermediate + root to standard output & hide stderr noise.

Actually... When testing I found out this doesn't give me the CA... openssl s_client -showcerts -verify 5 -connect letsencrypt.org:443 < /dev/null 2> /dev/null | awk '/BEGIN/,/END/{ if(/BEGIN/){a++}; print}'

curl https://letsencrypt.org/certs/isrgrootx1.pem Gives a different value (and when testing some stuff this is the value that works.)

neoakris
  • 121
  • 4
2

I also had the same challenge and next to that I discovered that openssl doesn't return the root ca. I have built an alternative for specifically for this purpose which might be useful for other developers, see here: GitHub - Certificate ripper

Usage

  • Printing to the console
crip print --url=https://stackexchange.com --format=pem
  • Exporting to a p12 trustore
crip export pkcs12 --url=https://stackexchange.com

The pkcs12 option can be replaced for pem or der if you want a different output.

Hakan54
  • 121
  • 3