awkopenssl

Extract certifcates from output of "openssl s_client"


I would like to get all certificates of the certicate chain of a http-site. Using openssl I connect to the http-site and store output to file out.txt:

openssl s_client -connect www.openssl.org:443 -showcerts > out.txt

The content of out.txt looks like below. It contains two certificates:

...
 0 s:CN = www.openssl.org
   i:C = US, O = Let's Encrypt, CN = Let's Encrypt Authority X3
-----BEGIN CERTIFICATE-----
MIIFVTCCBD2gAwIBAgISAwk9QUiwVmoQAtcCLKybaK7yMA0GCSqGSIb3DQEBCwUA
...
mQBom1EISBOiNyu5koR6iRZcXsn6x/4kwA==
-----END CERTIFICATE-----
 1 s:C = US, O = Let's Encrypt, CN = Let's Encrypt Authority X3
   i:O = Digital Signature Trust Co., CN = DST Root CA X3
-----BEGIN CERTIFICATE-----
MIIEkjCCA3qgAwIBAgIQCgFBQgAAAVOFc2oLheynCDANBgkqhkiG9w0BAQsFADA/
...
KOqkqm57TH2H3eDJAkSnh6/DNFu0Qg==
-----END CERTIFICATE-----
---
...

Now I want to store every certificate in a file ending with extension .cer. For the above example the files Let's Encrypt Authority X3.cer and www.openssl.org.cer should be created.

Using command openssl x509 I was only able to store first certifcate contained in out.txt.

cat out.txt | openssl x509 > www.openssl.org.cer

But I want to store all certifcates contained in out.txt not just the first one.

Can this be done with openssl somehow ? Or with awk ?


Solution

  • Yes, this can be done with awk. I would like to present my solution piece by piece.

    Step 1: extract everything between "BEGIN CERTIFICATE" and "END CERTIFICATE"

    Only write lines between "BEGIN CERTIFICATE" and "END CERTIFICATE" to output:

    awk '
    /BEGIN CERTIFICATE/,/END CERTIFICATE/ {
    print $0
    }
    ' out.txt
    

    Step 2: extract line containing CN

    selects line which contains CN (e.g. 0 s:CN = www.openssl.org)

    awk '
    /^ [0-9]+ s:.*CN = / {
    print $0
    }
    ' out.txt
    

    This yields output:

     0 s:CN = www.openssl.org
     1 s:C = US, O = Let's Encrypt, CN = Let's Encrypt Authority X3
    

    Step 3: extract content of field CN

    awk '
    BEGIN {
    # change field separator, so that $2 returns everything after "CN = "
    FS="CN = "
    }
    # selects line which contains CN (e.g.  0 s:CN = www.openssl.org)
    /^ [0-9]+ s:.*CN = / {
    # use CN (e.g. www.openssl.org) as filename
    print $2
    }
    ' out.txt
    

    This yields output:

    www.openssl.org
    Let's Encrypt Authority X3
    

    Step 4: Full Solution

    This creates files Let's Encrypt Authority X3.cer and www.openssl.org.cer:

    awk '
    BEGIN {
    # change field separator, so that $2 returns everything after "CN = "
    FS="CN = "
    }
    # selects line which contains CN (e.g.  0 s:CN = www.openssl.org)
    /^ [0-9]+ s:.*CN = / {
    # use CN (e.g. www.openssl.org) as filename
    filename=$2".cer"
    }
    # write all lines between "BEGIN CERTIFICATE" and "END CERTIFICATE" to filename
    /BEGIN CERTIFICATE/,/END CERTIFICATE/ {
    print $0 > filename
    }
    ' out.txt
    

    AWK Documentation: https://www.gnu.org/software/gawk/manual/html_node/index.html