How to locally check SSL certificate

Check locallly stored SSL certificate using basic utilities like openssl and curl. This knowlege is especially useful when you want to prepare SSL certificate for load balancer.

Self-signed SSL certificate

This is the simplest possible example that is designed to fail as there is no way to validate some random self-signed SSL certificate.

Execute simple SSL/TLS server program using self-signed SSL certificate and its private key.

$ openssl s_server -no_dhe -accept 8282 -www -key example.org.key -cert example.org.crt

Execute simple SSL/TLS client program to inspect this certificate.

$ echo -n | openssl s_client -servername example.org -connect localhost:8282  -CApath /etc/ssl/certs
CONNECTED(00000003)
depth=0 C = PL, ST = pomorskie, O = personal, L = Gdansk, CN = example.org, OU = IT, emailAddress = [email protected]
verify error:num=18:self signed certificate
verify return:1
depth=0 C = PL, ST = pomorskie, O = personal, L = Gdansk, CN = example.org, OU = IT, emailAddress = [email protected]
verify return:1
---
Certificate chain
 0 s:/C=PL/ST=pomorskie/O=personal/L=Gdansk/CN=example.org/OU=IT/[email protected]
   i:/C=PL/ST=pomorskie/O=personal/L=Gdansk/CN=example.org/OU=IT/[email protected]
---
Server certificate
-----BEGIN CERTIFICATE-----
MIIFkjCCA3oCCQCza+lCDud16jANBgkqhkiG9w0BAQsFADCBijELMAkGA1UEBhMC
[...]
2QZm3C+8jOxDR+n1b6bwtrrVl7fD7L0I4iLPDlTBZgzHHibLQbysWzJKQVsuvmbW
QEkCajRYUX0lSHABrIJtgTu5QgJfPOc4Rjzo0LEsoIHonUBiXWc=
-----END CERTIFICATE-----
subject=/C=PL/ST=pomorskie/O=personal/L=Gdansk/CN=example.org/OU=IT/[email protected]
issuer=/C=PL/ST=pomorskie/O=personal/L=Gdansk/CN=example.org/OU=IT/[email protected]
---
No client certificate CA names sent
Peer signing digest: SHA512
Server Temp Key: ECDH, P-256, 256 bits
---
SSL handshake has read 2361 bytes and written 451 bytes
---
New, TLSv1/SSLv3, Cipher is ECDHE-RSA-AES256-GCM-SHA384
Server public key is 4096 bit
Secure Renegotiation IS supported
Compression: NONE
Expansion: NONE
No ALPN negotiated
SSL-Session:
    Protocol  : TLSv1.2
    Cipher    : ECDHE-RSA-AES256-GCM-SHA384
    Session-ID: C1B33BCF1F8A611EE106E7E9D9DED34FD634FFE45C516B716DB14C771F02D32C
    Session-ID-ctx:
    Master-Key: 22C05CC82AA56345C7E13937A4E7C884A3D6B4CC7C954B54A79C04141CD09DDAF760852D9E31D20900DA20865A42BE52
    Key-Arg   : None
    PSK identity: None
    PSK identity hint: None
    SRP username: None
    TLS session ticket lifetime hint: 300 (seconds)
    TLS session ticket:
    0000 - 6c d9 32 c1 a9 7b 97 c7-7d 86 75 49 05 10 3b d8   l.2..{..}.uI..;.
    0010 - 9e 2c 6b 26 ac 11 b6 d6-2a 4b 7b de 07 1d 71 b0   .,k&....*K{...q.
    0020 - 19 25 66 5b 88 39 ab 51-c8 c6 ad ed 79 c0 15 b2   .%f[.9.Q....y...
    0030 - 3f ae db 35 5a b6 95 52-1a a6 34 1e 89 bb dc be   ?..5Z..R..4.....
    0040 - 54 15 3c 92 c0 55 7e e0-c0 4a 2c dd 6b 8b b3 3d   T.<..U~..J,.k..=
    0050 - e6 1b 53 3f 4d 30 2f 72-bd 9c 18 0d 3d 62 17 a0   ..S?M0/r....=b..
    0060 - 08 a8 32 64 d9 65 06 54-d4 f4 cc 8a 05 8b de 6a   ..2d.e.T.......j
    0070 - 8c d0 8e a9 9d 68 95 52-ed 71 fa 93 c8 8a b5 a8   .....h.R.q......
    0080 - 58 1a 38 47 9d a6 9f 23-f5 c2 42 b6 5f aa 7e a2   X.8G...#..B._.~.
    0090 - dc f4 ea 29 d5 8c 1d b6-92 43 4d 09 79 2e 74 1f   ...).....CM.y.t.
    00a0 - 37 79 2b b8 1a 8a 91 f3-a3 06 c9 4f 54 0e a8 fb   7y+........OT...

    Start Time: 1510239128
    Timeout   : 300 (sec)
    Verify return code: 18 (self signed certificate)
---
DONE

Verify return code - you cannot use this certificate without turning off SSL certificate verification.

$ curl --silent --resolve example.org:8282:127.0.0.1 https://example.org:8282/
curl: (60) server certificate verification failed. CAfile: /etc/ssl/certs/ca-certificates.crt CRLfile: none
More details here: http://curl.haxx.se/docs/sslcerts.html

curl performs SSL certificate verification by default, using a "bundle"
 of Certificate Authority (CA) public keys (CA certs). If the default
 bundle file isn't adequate, you can specify an alternate file
 using the --cacert option.
If this HTTPS server uses a certificate signed by a CA represented in
 the bundle, the certificate verification probably failed due to a
 problem with the certificate (it might be expired, or the name might
 not match the domain name in the URL).
If you'd like to turn off curl's verification of the certificate, use
 the -k (or --insecure) option.

Everything is as it should be.

Regular certificate

Execute simple SSL/TLS server program using SSL certificate, its private key and intermediate certificates.

$ openssl s_server -no_dhe -accept 8282 -www -key example.com.key -cert example.com.crt -CAfile example.com.intermediate.crt

Execute simple SSL/TLS client program to inspect this certificate.

$ echo -n | openssl s_client -servername example.com -connect localhost:8282 -CApath /etc/ssl/certs
CONNECTED(00000003)
---
Certificate chain
 0 s:/CN=example.com
   i:/C=US/O=Let's Encrypt/CN=Let's Encrypt Authority X3
 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
 2 s:/O=Digital Signature Trust Co./CN=DST Root CA X3
   i:/O=Digital Signature Trust Co./CN=DST Root CA X3
---
Server certificate
-----BEGIN CERTIFICATE-----
MIIFGzCCBAOgAwIBAgISA8J1sgvKsx7iOY1ME0atryKhMA0GCSqGSIb3DQEBCwUA
[...]
ncpOI1U724+/U6vTitOoSAHb83qroLAmXl0OF0SBl5xawTSMhAf44UEIvV6x2iJT
Ul9lKA4Qgyrl3tiXrOZu
-----END CERTIFICATE-----
subject=/CN=example.com
issuer=/C=US/O=Let's Encrypt/CN=Let's Encrypt Authority X3
---
No client certificate CA names sent
Peer signing digest: SHA512
Server Temp Key: ECDH, P-256, 256 bits
---
SSL handshake has read 4012 bytes and written 451 bytes
---
---
New, TLSv1/SSLv3, Cipher is ECDHE-RSA-AES256-GCM-SHA384
Server public key is 2048 bit
Secure Renegotiation IS supported
Compression: NONE
Expansion: NONE
No ALPN negotiated
SSL-Session:
    Protocol  : TLSv1.2
    Cipher    : ECDHE-RSA-AES256-GCM-SHA384
    Session-ID: 469596E7A4616EEF91EA3D2344AE9C0FBD4D0E96C4CB0130BC7BCDA7F7DA020E
    Session-ID-ctx: 
    Master-Key: D4DF9C8D2324F51A58EEB5E7D3774F1EE54C9E8E0AB69F7F4EC9E3D3B3A9239992AE832F4C638732B2327ADD35F772CE
    Key-Arg   : None
    PSK identity: None
    PSK identity hint: None
    SRP username: None
    TLS session ticket lifetime hint: 300 (seconds)
    TLS session ticket:
    0000 - 66 78 90 a9 98 12 11 40-b8 03 44 58 03 1e 92 32   [email protected]
    0010 - da 55 0f c7 26 d7 5e 60-94 56 02 4a ba ae c5 16   .U..&.^`.V.J....
    0020 - b7 c6 9e 1c ed 3f 3b 2a-16 31 87 10 1b f6 1e 44   .....?;*.1.....D
    0030 - e5 b4 b8 5c d5 b7 66 0d-b3 44 1d c3 cd 73 26 14   ...\..f..D...s&.
    0040 - 93 aa 00 f9 32 45 84 e5-bc e4 cf c8 3e 4e f9 b0   ....2E......>N..
    0050 - b7 f9 53 5a 0c 36 60 8f-bb 4b 90 20 ef 67 30 21   ..SZ.6`..K. .g0!
    0060 - 26 49 83 94 04 e4 81 9d-76 bf 44 96 d3 6f 5e 3b   &I......v.D..o^;
    0070 - 49 c6 80 f3 ea d5 e8 06-4c db 8b 74 fa ed 7e df   I.......L..t..~.
    0080 - 85 b5 ae 91 97 e6 72 36-39 f6 e4 6e 9e 9a 6a a1   ......r69..n..j.
    0090 - 48 d5 2a ee b1 48 6d fb-4b 91 1e 28 70 cd 07 98   H.*..Hm.K..(p...
    00a0 - 2c 66 fa 19 33 2d 15 7f-5c 23 31 ff 38 97 12 13   ,f..3-..\#1.8...

    Start Time: 1510259115
    Timeout   : 300 (sec)
    Verify return code: 0 (ok)
---

Verify return code - you can safely use this certificate.

$ curl --silent --capath /etc/ssl/certs --resolve example.com:8282:127.0.0.1 https://example.com:8282/ 
<HTML><BODY BGCOLOR="#ffffff">
<pre>

s_server -no_dhe -accept 8282 -www -key devel.key -cert devel.crt -CAfile devel.int.crt 
Secure Renegotiation IS supported
Ciphers supported in s_server binary
TLSv1/SSLv3:ECDHE-RSA-AES256-GCM-SHA384TLSv1/SSLv3:ECDHE-ECDSA-AES256-GCM-SHA384
TLSv1/SSLv3:ECDHE-RSA-AES256-SHA384  TLSv1/SSLv3:ECDHE-ECDSA-AES256-SHA384
TLSv1/SSLv3:ECDHE-RSA-AES256-SHA     TLSv1/SSLv3:ECDHE-ECDSA-AES256-SHA   
TLSv1/SSLv3:SRP-DSS-AES-256-CBC-SHA  TLSv1/SSLv3:SRP-RSA-AES-256-CBC-SHA  
TLSv1/SSLv3:SRP-AES-256-CBC-SHA      TLSv1/SSLv3:DH-DSS-AES256-GCM-SHA384 
TLSv1/SSLv3:DHE-DSS-AES256-GCM-SHA384TLSv1/SSLv3:DH-RSA-AES256-GCM-SHA384 
TLSv1/SSLv3:DHE-RSA-AES256-GCM-SHA384TLSv1/SSLv3:DHE-RSA-AES256-SHA256    
TLSv1/SSLv3:DHE-DSS-AES256-SHA256    TLSv1/SSLv3:DH-RSA-AES256-SHA256     
TLSv1/SSLv3:DH-DSS-AES256-SHA256     TLSv1/SSLv3:DHE-RSA-AES256-SHA       
TLSv1/SSLv3:DHE-DSS-AES256-SHA       TLSv1/SSLv3:DH-RSA-AES256-SHA        
TLSv1/SSLv3:DH-DSS-AES256-SHA        TLSv1/SSLv3:DHE-RSA-CAMELLIA256-SHA  
TLSv1/SSLv3:DHE-DSS-CAMELLIA256-SHA  TLSv1/SSLv3:DH-RSA-CAMELLIA256-SHA   
TLSv1/SSLv3:DH-DSS-CAMELLIA256-SHA   TLSv1/SSLv3:ECDH-RSA-AES256-GCM-SHA384
TLSv1/SSLv3:ECDH-ECDSA-AES256-GCM-SHA384TLSv1/SSLv3:ECDH-RSA-AES256-SHA384   
TLSv1/SSLv3:ECDH-ECDSA-AES256-SHA384 TLSv1/SSLv3:ECDH-RSA-AES256-SHA      
TLSv1/SSLv3:ECDH-ECDSA-AES256-SHA    TLSv1/SSLv3:AES256-GCM-SHA384        
TLSv1/SSLv3:AES256-SHA256            TLSv1/SSLv3:AES256-SHA               
TLSv1/SSLv3:CAMELLIA256-SHA          TLSv1/SSLv3:PSK-AES256-CBC-SHA       
TLSv1/SSLv3:ECDHE-RSA-AES128-GCM-SHA256TLSv1/SSLv3:ECDHE-ECDSA-AES128-GCM-SHA256
TLSv1/SSLv3:ECDHE-RSA-AES128-SHA256  TLSv1/SSLv3:ECDHE-ECDSA-AES128-SHA256
TLSv1/SSLv3:ECDHE-RSA-AES128-SHA     TLSv1/SSLv3:ECDHE-ECDSA-AES128-SHA   
TLSv1/SSLv3:SRP-DSS-AES-128-CBC-SHA  TLSv1/SSLv3:SRP-RSA-AES-128-CBC-SHA  
TLSv1/SSLv3:SRP-AES-128-CBC-SHA      TLSv1/SSLv3:DH-DSS-AES128-GCM-SHA256 
TLSv1/SSLv3:DHE-DSS-AES128-GCM-SHA256TLSv1/SSLv3:DH-RSA-AES128-GCM-SHA256 
TLSv1/SSLv3:DHE-RSA-AES128-GCM-SHA256TLSv1/SSLv3:DHE-RSA-AES128-SHA256    
TLSv1/SSLv3:DHE-DSS-AES128-SHA256    TLSv1/SSLv3:DH-RSA-AES128-SHA256     
TLSv1/SSLv3:DH-DSS-AES128-SHA256     TLSv1/SSLv3:DHE-RSA-AES128-SHA       
TLSv1/SSLv3:DHE-DSS-AES128-SHA       TLSv1/SSLv3:DH-RSA-AES128-SHA        
TLSv1/SSLv3:DH-DSS-AES128-SHA        TLSv1/SSLv3:DHE-RSA-SEED-SHA         
TLSv1/SSLv3:DHE-DSS-SEED-SHA         TLSv1/SSLv3:DH-RSA-SEED-SHA          
TLSv1/SSLv3:DH-DSS-SEED-SHA          TLSv1/SSLv3:DHE-RSA-CAMELLIA128-SHA  
TLSv1/SSLv3:DHE-DSS-CAMELLIA128-SHA  TLSv1/SSLv3:DH-RSA-CAMELLIA128-SHA   
TLSv1/SSLv3:DH-DSS-CAMELLIA128-SHA   TLSv1/SSLv3:ECDH-RSA-AES128-GCM-SHA256
TLSv1/SSLv3:ECDH-ECDSA-AES128-GCM-SHA256TLSv1/SSLv3:ECDH-RSA-AES128-SHA256   
TLSv1/SSLv3:ECDH-ECDSA-AES128-SHA256 TLSv1/SSLv3:ECDH-RSA-AES128-SHA      
TLSv1/SSLv3:ECDH-ECDSA-AES128-SHA    TLSv1/SSLv3:AES128-GCM-SHA256        
TLSv1/SSLv3:AES128-SHA256            TLSv1/SSLv3:AES128-SHA               
TLSv1/SSLv3:SEED-SHA                 TLSv1/SSLv3:CAMELLIA128-SHA          
TLSv1/SSLv3:PSK-AES128-CBC-SHA       TLSv1/SSLv3:ECDHE-RSA-RC4-SHA        
TLSv1/SSLv3:ECDHE-ECDSA-RC4-SHA      TLSv1/SSLv3:ECDH-RSA-RC4-SHA         
TLSv1/SSLv3:ECDH-ECDSA-RC4-SHA       TLSv1/SSLv3:RC4-SHA                  
TLSv1/SSLv3:RC4-MD5                  TLSv1/SSLv3:PSK-RC4-SHA              
TLSv1/SSLv3:ECDHE-RSA-DES-CBC3-SHA   TLSv1/SSLv3:ECDHE-ECDSA-DES-CBC3-SHA 
TLSv1/SSLv3:SRP-DSS-3DES-EDE-CBC-SHA TLSv1/SSLv3:SRP-RSA-3DES-EDE-CBC-SHA 
TLSv1/SSLv3:SRP-3DES-EDE-CBC-SHA     TLSv1/SSLv3:EDH-RSA-DES-CBC3-SHA     
TLSv1/SSLv3:EDH-DSS-DES-CBC3-SHA     TLSv1/SSLv3:DH-RSA-DES-CBC3-SHA      
TLSv1/SSLv3:DH-DSS-DES-CBC3-SHA      TLSv1/SSLv3:ECDH-RSA-DES-CBC3-SHA    
TLSv1/SSLv3:ECDH-ECDSA-DES-CBC3-SHA  TLSv1/SSLv3:DES-CBC3-SHA             
TLSv1/SSLv3:PSK-3DES-EDE-CBC-SHA     
---
Ciphers common between both SSL end points:
ECDHE-RSA-AES256-GCM-SHA384 ECDHE-ECDSA-AES256-GCM-SHA384 ECDHE-RSA-AES256-SHA384   
ECDHE-ECDSA-AES256-SHA384  ECDHE-RSA-AES256-SHA       ECDHE-ECDSA-AES256-SHA    
DH-DSS-AES256-GCM-SHA384   DHE-DSS-AES256-GCM-SHA384  DH-RSA-AES256-GCM-SHA384  
DHE-RSA-AES256-GCM-SHA384  DHE-RSA-AES256-SHA256      DHE-DSS-AES256-SHA256     
DH-RSA-AES256-SHA256       DH-DSS-AES256-SHA256       DHE-RSA-AES256-SHA        
DHE-DSS-AES256-SHA         DH-RSA-AES256-SHA          DH-DSS-AES256-SHA         
DHE-RSA-CAMELLIA256-SHA    DHE-DSS-CAMELLIA256-SHA    DH-RSA-CAMELLIA256-SHA    
DH-DSS-CAMELLIA256-SHA     ECDH-RSA-AES256-GCM-SHA384 ECDH-ECDSA-AES256-GCM-SHA384
ECDH-RSA-AES256-SHA384     ECDH-ECDSA-AES256-SHA384   ECDH-RSA-AES256-SHA       
ECDH-ECDSA-AES256-SHA      AES256-GCM-SHA384          AES256-SHA256             
AES256-SHA                 CAMELLIA256-SHA            ECDHE-RSA-AES128-GCM-SHA256
ECDHE-ECDSA-AES128-GCM-SHA256 ECDHE-RSA-AES128-SHA256    ECDHE-ECDSA-AES128-SHA256 
ECDHE-RSA-AES128-SHA       ECDHE-ECDSA-AES128-SHA     DH-DSS-AES128-GCM-SHA256  
DHE-DSS-AES128-GCM-SHA256  DH-RSA-AES128-GCM-SHA256   DHE-RSA-AES128-GCM-SHA256 
DHE-RSA-AES128-SHA256      DHE-DSS-AES128-SHA256      DH-RSA-AES128-SHA256      
DH-DSS-AES128-SHA256       DHE-RSA-AES128-SHA         DHE-DSS-AES128-SHA        
DH-RSA-AES128-SHA          DH-DSS-AES128-SHA          DHE-RSA-SEED-SHA          
DHE-DSS-SEED-SHA           DH-RSA-SEED-SHA            DH-DSS-SEED-SHA           
DHE-RSA-CAMELLIA128-SHA    DHE-DSS-CAMELLIA128-SHA    DH-RSA-CAMELLIA128-SHA    
DH-DSS-CAMELLIA128-SHA     ECDH-RSA-AES128-GCM-SHA256 ECDH-ECDSA-AES128-GCM-SHA256
ECDH-RSA-AES128-SHA256     ECDH-ECDSA-AES128-SHA256   ECDH-RSA-AES128-SHA       
ECDH-ECDSA-AES128-SHA      AES128-GCM-SHA256          AES128-SHA256             
AES128-SHA                 SEED-SHA                   CAMELLIA128-SHA           
ECDHE-RSA-DES-CBC3-SHA     ECDHE-ECDSA-DES-CBC3-SHA   EDH-RSA-DES-CBC3-SHA      
EDH-DSS-DES-CBC3-SHA       DH-RSA-DES-CBC3-SHA        DH-DSS-DES-CBC3-SHA       
ECDH-RSA-DES-CBC3-SHA      ECDH-ECDSA-DES-CBC3-SHA    DES-CBC3-SHA
Signature Algorithms: RSA+SHA512:DSA+SHA512:ECDSA+SHA512:RSA+SHA384:DSA+SHA384:ECDSA+SHA384:RSA+SHA256:DSA+SHA256:ECDSA+SHA256:RSA+SHA224:DSA+SHA224:ECDSA+SHA224:RSA+SHA1:DSA+SHA1:ECDSA+SHA1
Shared Signature Algorithms: RSA+SHA512:DSA+SHA512:ECDSA+SHA512:RSA+SHA384:DSA+SHA384:ECDSA+SHA384:RSA+SHA256:DSA+SHA256:ECDSA+SHA256:RSA+SHA224:DSA+SHA224:ECDSA+SHA224:RSA+SHA1:DSA+SHA1:ECDSA+SHA1
Supported Elliptic Curves: P-256:P-521:brainpoolP512r1:brainpoolP384r1:P-384:brainpoolP256r1:secp256k1:B-571:K-571:K-409:B-409:K-283:B-283
Shared Elliptic curves: P-256:P-521:brainpoolP512r1:brainpoolP384r1:P-384:brainpoolP256r1:secp256k1:B-571:K-571:K-409:B-409:K-283:B-283
---
New, TLSv1/SSLv3, Cipher is ECDHE-RSA-AES256-GCM-SHA384
SSL-Session:
    Protocol  : TLSv1.2
    Cipher    : ECDHE-RSA-AES256-GCM-SHA384
    Session-ID: 824C62C3468CD687000D4DEEA7967979681A68F724EA51BEA1F62BC756162D30
    Session-ID-ctx: 01000000
    Master-Key: 1FE48D580D50BD7936CA40CD20D0EAD44EB7B27ECDF13F0A1B0E6F7C76F4384638C4FC1869D8F1752BFD860805EF68F8
    Key-Arg   : None
    PSK identity: None
    PSK identity hint: None
    SRP username: None
    Start Time: 1510259200
    Timeout   : 300 (sec)
    Verify return code: 0 (ok)
---
   1 items in the session cache
   0 client connects (SSL_connect())
   0 client renegotiates (SSL_connect())
   0 client connects that finished
   1 server accepts (SSL_accept())
   0 server renegotiates (SSL_accept())
   1 server accepts that finished
   0 session cache hits
   0 session cache misses
   0 session cache timeouts
   0 callback cache hits
   0 cache full overflows (128 allowed)
---
no client certificate available
</BODY></HTML>

Use verbose output to inspect the whole process further.

$ curl --silent --capath /etc/ssl/certs --output /dev/null --verbose --resolve example.com:8282:127.0.0.1 https://example.com:8282/ 
* Added example.com:8282:127.0.0.1 to DNS cache                                       
* Hostname example.com was found in DNS cache                                         
*   Trying 127.0.0.1...                                                                                
* TCP_NODELAY set                                                                                      
* Connected to example.com (127.0.0.1) port 8282 (#0)                                 
* ALPN, offering http/1.1                                                                              
* Cipher selection: ALL:!EXPORT:!EXPORT40:!EXPORT56:!aNULL:!LOW:!RC4:@STRENGTH                         
* successfully set certificate verify locations:                                                       
*   CAfile: /etc/ssl/certs/ca-certificates.crt                                                         
  CApath: /etc/ssl/certs                                                                               
* TLSv1.2 (OUT), TLS header, Certificate Status (22):                                                  
} [5 bytes data]         
* TLSv1.2 (OUT), TLS handshake, Client hello (1):  
} [512 bytes data]       
* TLSv1.2 (IN), TLS handshake, Server hello (2):   
{ [94 bytes data]        
* TLSv1.2 (IN), TLS handshake, Certificate (11):   
{ [3347 bytes data]      
* TLSv1.2 (IN), TLS handshake, Server key exchange (12):                                               
{ [333 bytes data]       
* TLSv1.2 (IN), TLS handshake, Server finished (14):                                                   
{ [4 bytes data]         
* TLSv1.2 (OUT), TLS handshake, Client key exchange (16):                                              
} [70 bytes data]        
* TLSv1.2 (OUT), TLS change cipher, Client hello (1):                                                  
} [1 bytes data]         
* TLSv1.2 (OUT), TLS handshake, Finished (20):     
} [16 bytes data]        
* TLSv1.2 (IN), TLS change cipher, Client hello (1):                                                   
{ [1 bytes data]         
* TLSv1.2 (IN), TLS handshake, Finished (20):      
{ [16 bytes data]        
* SSL connection using TLSv1.2 / ECDHE-RSA-AES256-GCM-SHA384                                           
* ALPN, server did not agree to a protocol         
* Server certificate:    
*  subject: CN=example.com        
*  start date: Oct 28 19:55:12 2017 GMT            
*  expire date: Jan 26 19:55:12 2018 GMT           
*  subjectAltName: host "example.com" matched cert's "example.com"   
*  issuer: C=US; O=Let's Encrypt; CN=Let's Encrypt Authority X3                                        
*  SSL certificate verify ok.                      
} [5 bytes data]         
> GET :/ HTTP/1.1         
> Host: example.com:8282          
> User-Agent: curl/7.55.1
> Accept: */*            
>                        
{ [5 bytes data]
* HTTP 1.0, assume close after body
< HTTP/1.0 200 ok
< Content-type: text/html
<
{ [7670 bytes data]
* Closing connection 0
} [5 bytes data]
* TLSv1.2 (OUT), TLS alert, Client hello (1):
} [2 bytes data]

HAproxy certificate bundle

After certificate inspection you need to create domain.pem certificate bundle that includes domain.crt certificate, domain.intermediate.crt intermediate certificates and domain.key private key. Exactly in that order. There's nothing more to it.

Additional notes

You can use web-browser instead of curl to verify locally stored certificate, but this is not really convenient.

I have disabled ephemeral DH cipher suites, but you can specify DH parameter file to use.

You can use openssl s_server to serve files over network, but this is just another tidbit.

Verify return code (it is different from exit code) as it will give you a strong indication when there is something wrong with provided intermediate certificates.

Return code is not an exit code.

Return codes are described in verify manual page.

0 X509_V_OK: ok
    the operation was successful.

2 X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT: unable to get issuer certificate
    the issuer certificate of a looked up certificate could not be found. This normally means the list of trusted certificates is not complete.

3 X509_V_ERR_UNABLE_TO_GET_CRL: unable to get certificate CRL
    the CRL of a certificate could not be found.

4 X509_V_ERR_UNABLE_TO_DECRYPT_CERT_SIGNATURE: unable to decrypt certificate's signature
    the certificate signature could not be decrypted. This means that the actual signature value could not be determined rather than it not matching the expected value, this is only meaningful for RSA
    keys.

5 X509_V_ERR_UNABLE_TO_DECRYPT_CRL_SIGNATURE: unable to decrypt CRL's signature
    the CRL signature could not be decrypted: this means that the actual signature value could not be determined rather than it not matching the expected value. Unused.

6 X509_V_ERR_UNABLE_TO_DECODE_ISSUER_PUBLIC_KEY: unable to decode issuer public key
    the public key in the certificate SubjectPublicKeyInfo could not be read.

7 X509_V_ERR_CERT_SIGNATURE_FAILURE: certificate signature failure
    the signature of the certificate is invalid.

8 X509_V_ERR_CRL_SIGNATURE_FAILURE: CRL signature failure
    the signature of the certificate is invalid.

9 X509_V_ERR_CERT_NOT_YET_VALID: certificate is not yet valid
    the certificate is not yet valid: the notBefore date is after the current time.

10 X509_V_ERR_CERT_HAS_EXPIRED: certificate has expired
    the certificate has expired: that is the notAfter date is before the current time.

11 X509_V_ERR_CRL_NOT_YET_VALID: CRL is not yet valid
    the CRL is not yet valid.

12 X509_V_ERR_CRL_HAS_EXPIRED: CRL has expired
    the CRL has expired.

13 X509_V_ERR_ERROR_IN_CERT_NOT_BEFORE_FIELD: format error in certificate's notBefore field
    the certificate notBefore field contains an invalid time.

14 X509_V_ERR_ERROR_IN_CERT_NOT_AFTER_FIELD: format error in certificate's notAfter field
    the certificate notAfter field contains an invalid time.

15 X509_V_ERR_ERROR_IN_CRL_LAST_UPDATE_FIELD: format error in CRL's lastUpdate field
    the CRL lastUpdate field contains an invalid time.

16 X509_V_ERR_ERROR_IN_CRL_NEXT_UPDATE_FIELD: format error in CRL's nextUpdate field
    the CRL nextUpdate field contains an invalid time.

17 X509_V_ERR_OUT_OF_MEM: out of memory
    an error occurred trying to allocate memory. This should never happen.

18 X509_V_ERR_DEPTH_ZERO_SELF_SIGNED_CERT: self signed certificate
    the passed certificate is self signed and the same certificate cannot be found in the list of trusted certificates.

19 X509_V_ERR_SELF_SIGNED_CERT_IN_CHAIN: self signed certificate in certificate chain
    the certificate chain could be built up using the untrusted certificates but the root could not be found locally.

20 X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT_LOCALLY: unable to get local issuer certificate
    the issuer certificate could not be found: this occurs if the issuer certificate of an untrusted certificate cannot be found.

21 X509_V_ERR_UNABLE_TO_VERIFY_LEAF_SIGNATURE: unable to verify the first certificate
    no signatures could be verified because the chain contains only one certificate and it is not self signed.

22 X509_V_ERR_CERT_CHAIN_TOO_LONG: certificate chain too long
    the certificate chain length is greater than the supplied maximum depth. Unused.

23 X509_V_ERR_CERT_REVOKED: certificate revoked
    the certificate has been revoked.

24 X509_V_ERR_INVALID_CA: invalid CA certificate
    a CA certificate is invalid. Either it is not a CA or its extensions are not consistent with the supplied purpose.

25 X509_V_ERR_PATH_LENGTH_EXCEEDED: path length constraint exceeded
    the basicConstraints pathlength parameter has been exceeded.

26 X509_V_ERR_INVALID_PURPOSE: unsupported certificate purpose
    the supplied certificate cannot be used for the specified purpose.

27 X509_V_ERR_CERT_UNTRUSTED: certificate not trusted
    the root CA is not marked as trusted for the specified purpose.

28 X509_V_ERR_CERT_REJECTED: certificate rejected
    the root CA is marked to reject the specified purpose.

29 X509_V_ERR_SUBJECT_ISSUER_MISMATCH: subject issuer mismatch
    the current candidate issuer certificate was rejected because its subject name did not match the issuer name of the current certificate. Only displayed when the -issuer_checks option is set.

30 X509_V_ERR_AKID_SKID_MISMATCH: authority and subject key identifier mismatch
    the current candidate issuer certificate was rejected because its subject key identifier was present and did not match the authority key identifier current certificate. Only displayed when the
    -issuer_checks option is set.

31 X509_V_ERR_AKID_ISSUER_SERIAL_MISMATCH: authority and issuer serial number mismatch
    the current candidate issuer certificate was rejected because its issuer name and serial number was present and did not match the authority key identifier of the current certificate. Only
    displayed when the -issuer_checks option is set.

32 X509_V_ERR_KEYUSAGE_NO_CERTSIGN:key usage does not include certificate signing
    the current candidate issuer certificate was rejected because its keyUsage extension does not permit certificate signing.

50 X509_V_ERR_APPLICATION_VERIFICATION: application verification failure
    an application specific error. Unused.

About Milosz Galazka

Milosz is a Linux Foundation Certified Engineer working for a successful Polish company as a system administrator and a long time supporter of Free Software Foundation and Debian operating system.