Verifying SSL/TLS certificates manually

Published: 2016-07-28
Last Updated: 2016-07-28 06:34:55 UTC
by Bojan Zdrnja (Version: 1)
3 comment(s)

I think that we can surely say that, with all its deficiencies, SSL/TLS is still a protocol we cannot live without, and basis of today’s secure communication on the Internet. Quite often I get asked on how certificates are really verified by browsers or other client utilities. Sure, the canned answer that “certificates get signed by CA’s and a browser verifies if signatures are correct” is always there, but more persistent questions on how it exactly works happen here and there as well.

So, if you ever wondered on how a certificate could be fully manually verified by checking all the steps, this is a diary for you! In this example we will manually verify the certificate of the site you are reading this diary on, We will use the openssl utility so you can replicate all the steps for any certificate on any machine where you have openssl. Here we go.

In order to get the certificate we want to verify we can simply connect to with the openssl utility. For that, the s_client command will be handy and it will print out the certificate in PEM format on the screen so we just have to catch it and put it into a file:

$ openssl s_client -connect < /dev/null | sed -ne '/-BEGIN CERTIFICATE-/,/-END CERTIFICATE-/p' >

The file now contains the certificate from We could try to verify it with openssl directly as shown below:

$ openssl verify -verbose C = US, postalCode = 20814, ST = Maryland, L = Bethesda, street = Suite 205, street = 8120 Woodmont Ave, O = The SANS Institute, OU = Network Operations Center (NOC), OU = Unified Communications, CN =
error 20 at 0 depth lookup:unable to get local issuer certificate

Hmm, no luck. But that is because the CA file that comes with Linux by default is missing some of the intermediates. Those either have to be in the CA store, or the server has to deliver the whole chain to us when we initially connect. Ok, not a problem – let’s continue manually.
First we can see who the issuer really here is, and what are the certificate’s parameters:

$ openssl x509 -in -noout -text
        Version: 3 (0x2)
        Serial Number:
    Signature Algorithm: sha256WithRSAEncryption
        Issuer: C=GB, ST=Greater Manchester, L=Salford, O=COMODO CA Limited, CN=COMODO RSA Organization Validation Secure Server CA
            Not Before: Apr  7 00:00:00 2015 GMT
            Not After : Apr  6 23:59:59 2018 GMT
        Subject: C=US/postalCode=20814, ST=Maryland, L=Bethesda/street=Suite 205/street=8120 Woodmont Ave, O=The SANS Institute, OU=Network Operations Center (NOC), OU=Unified Communications,

Ok, so the certificate is valid, and it is signed by Comodo, as you can see in the highlighted line. The part that matters to the browsers is actually only the CN component. In the Subject field we can see that the CN matches our site ( and in the Issuer field we can see that that signing CA (which is an intermediate CA) is called COMODO RSA Organization Validation Secure Server CA.

We can verify this information in the RFC2253 format as well, for both the subject and issuer; this will be easier to read:

$ openssl x509 -in -noout -subject -issuer -nameopt RFC2253
subject=,OU=Unified Communications,OU=Network Operations Center (NOC),O=The SANS Institute,street=8120 Woodmont Ave,street=Suite 205,L=Bethesda,ST=Maryland,postalCode=20814,C=US
issuer= CN=COMODO RSA Organization Validation Secure Server CA,O=COMODO CA Limited,L=Salford,ST=Greater Manchester,C=GB

So, let’s first try getting the CAcert file that is used by Mozilla. This might help us verify everything. That being said, getting the CAcert file from Mozilla is not all that trivial and some extractions/conversions should be done. Luckily, the good folks at curl already publish the cacert file in the PEM format so we can get it from their web; it’s available at

$ curl -o cacert.pem

The file even contains name of CA’s in plain text. Let’s search for Comodo:

$ grep -i Comodo cacert.pem
Comodo AAA Services root
Comodo Secure Services root
Comodo Trusted Services root
COMODO Certification Authority
COMODO ECC Certification Authority
COMODO RSA Certification Authority

It doesn’t have the one that we need: remember that it must match precisely to the CN field! This also confirms that it is an intermediate CA. We will probably have to find the intermediate CA’s certificate on Comodo’s web site. Let’s paste the name into Google (“COMODO RSA Organization Validation Secure Server CA“) and see what we get.

The first hit will lead us to and sure - this is were our intermediate CA is. Let’s download it:

$ curl '' > comodo.crt

Now let’s check the issuer and subject here as well:

$ openssl x509 -in comodo.crt -subject -issuer -noout -nameopt RFC2253
subject= CN=COMODO RSA Organization Validation Secure Server CA,O=COMODO CA Limited,L=Salford,ST=Greater Manchester,C=GB
issuer= CN=COMODO RSA Certification Authority,O=COMODO CA Limited,L=Salford,ST=Greater Manchester,C=GB

Great! That’s exactly what we need – see that the Subject field (the CN component) matches exactly to the signer of our certificate. We are lucky even with the issuer:

$ grep "COMODO RSA Certification Authority" cacert.pem
COMODO RSA Certification Authority

It is a root CA, that exists in Mozilla cacert.pem – so we have the full chain!
Let’s get back to verifying our certificate from First we need to check which signature algorithm has been used:

$ openssl x509 -in -noout -text | grep Signature
    Signature Algorithm: sha256WithRSAEncryption

Ok, SHA256 with RSA (great job Johannes on renewing the cert properly :)). What does this mean? This means that the critical parts of the certificate have been hashed by the CA with the SHA256 hashing algorithm and then encrypted with CA’s private key. It’s public key is available in the comodo.crt file we just downloaded (and’s public key is in the certificate we got from the web site). Openssl can confirm that as well for us:

$ openssl x509 -in -noout -text

        Subject Public Key Info:
            Public Key Algorithm: rsaEncryption
                Public-Key: (4096 bit)

What we need to do know is the following:

  • We need to extract the signature from the certificate and then use Comodo’s public key to decrypt it; with this we will get the SHA256 hash of the certificate
  • Then we need to calculate our own SHA256 hash of the certificate
  • If those two match: the certificate is signed properly

In order to extract components of a certificate we need to decode it to ASN.1 format. Luckily, openssl can do that for us, so let’s see what we get on’s certificate:

$ openssl asn1parse -in

1582:d=1  hl=2 l=  13 cons: SEQUENCE
 1584:d=2  hl=2 l=   9 prim: OBJECT            :sha256WithRSAEncryption
 1595:d=2  hl=2 l=   0 prim: NULL
 1597:d=1  hl=4 l= 257 prim: BIT STRING

So, the last object is actually the signature – it starts at offset 1597, so let’s extract it with openssl:

$ openssl asn1parse -in -out -noout -strparse 1597

Now we got the file, which is RSA encrypted SHA256 of the signature. How do we decrypt it? We need Comodo’s public key, which is available in its certificate, so let’s extract it:

$ openssl x509 -in comodo.crt -pubkey -noout >

Now that we have the Comodo’s public key, we can finally decrypt the SHA256 hash. It will work only if the original has been encrypted with the corresponding private key. We’ll get an ASN.1 structure back so let’s show it properly as well on the screen:

$ openssl rsautl -verify -pubin -inkey -in -asn1parse
    0:d=0  hl=2 l=  49 cons: SEQUENCE
    2:d=1  hl=2 l=  13 cons:  SEQUENCE
    4:d=2  hl=2 l=   9 prim:   OBJECT            :sha256
   15:d=2  hl=2 l=   0 prim:   NULL
   17:d=1  hl=2 l=  32 prim:  OCTET STRING
      0000 - 4b ca b8 23 4d 52 da e1-31 f1 0d b0 ba 3d 33 6b   K..#MR..1....=3k
      0010 - 0e 3d 68 0f 99 cb 35 43-69 ff 70 d0 1d a6 ef c1   .=h...5Ci.p.....

Yay, it worked. So it has been encrypted properly. The highlighted part is actually the SHA256 hash.
The last step now is to extract the critical parts of the certificate and verify if both hashes match. So what are the critical parts of the certificate? The X509 standard defines it as a so called TBSCertificate (To Be Signed Certificate), and it is the first object in the certificate:

$ openssl asn1parse -in 
    0:d=0  hl=4 l=1854 cons: SEQUENCE
    4:d=1  hl=4 l=1574 cons: SEQUENCE
    8:d=2  hl=2 l=   3 cons: cont [ 0 ]
   10:d=3  hl=2 l=   1 prim: INTEGER           :02
   13:d=2  hl=2 l=  16 prim: INTEGER           :242168A75513741AD195FB622690C91D
   31:d=2  hl=2 l=  13 cons: SEQUENCE
   33:d=3  hl=2 l=   9 prim: OBJECT            :sha256WithRSAEncryption

Ok, the first object starts at offset 4, let’s extract it the same way as before:

$ openssl asn1parse -in -out tbsCertificate -strparse 4

The file tbsCertificate contains what we need to run SHA256 has over. We can again use openssl for that:

$ openssl dgst -sha256 -hex tbsCertificate
SHA256(tbsCertificate)= 4bcab8234d52dae131f10db0ba3d336b0e3d680f99cb354369ff70d01da6efc1

Remember the decrypted ASN.1 object? Scroll up – or let me paste it here one more time (this diary is already longer than I thought really):

   17:d=1  hl=2 l=  32 prim:  OCTET STRING
      0000 - 4b ca b8 23 4d 52 da e1-31 f1 0d b0 ba 3d 33 6b   K..#MR..1....=3k
      0010 - 0e 3d 68 0f 99 cb 35 43-69 ff 70 d0 1d a6 ef c1   .=h...5Ci.p.....

Yay! It’s a full, 100% match. So the certificate is correctly signed by Comodo’s intermediate. We could now repeat all the steps to verify if the intermediate CA is correctly signed by the root CA that we got from Mozilla’s cacert.pem, but we can also have openssl do that for us, we just need to tell it with CA file to use:

$ cat comodo.crt >> cacert.pem
$ openssl verify -verbose -CAfile cacert.pem OK

And that get’s us to the end of verification.

-- Bojan

Keywords: openssl verify
3 comment(s)


You must get paid by the hour instead of being on salary. :-)

If it's an Internet-accessible site try these:

For SMTP servers try If you use the More Detail button it gives you everything you need and more. They actually use OpenSSL to derive those results.

Hmmm, the SMTP servers for and do not have TLS encryption enabled but there are MX records? Tested with and .edu has a soft-fail SPF record instead of a hard-fail and doesn't even have an SPF record yet it has MX records?

Domains that do not send email should have an SPF record of v=spf1 -all has a p=none DMARC record, which is understandable because of the soft-fail on its SPF record.

I do not fully agree with the recent Krebs article on email security but the presence of that article should be a wake-up call for all organizations to double-check their email configurations.

Perception = Reality, unfortunately.
Nice write-up...

Also for us overworked (or is that lazy) I like: (All credit to @drwetter)
Easier ways to find the intermediate certificate:

a. use the -showcerts command in the openssl s_client command.

b. get it from the url in the Authority Information Access field in the end entity certificate.

Diary Archives