Grabbing certificates with OpenSSL

TLS and SSL are widely used to encrypt network traffic. One key part of the protocol is verifying the identity of a server, this is done with X.509 certificates.

To verify certificates a client normally need a copy of the certificate. Alternatively if a certificate authority the client trusts was used to sign the certificate, the CA certificate can be used. If a client such as curl is unable to verify a certificate you will get an error similar to the following:

$ curl  https://localhost.localdomain
curl: (60) Issuer certificate is invalid.
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.

OpenSSL s_client

s_client is a useful OpenSSL command which can be used to debug TLS/SSL connections. The -connect and -showcert options can be used to display certificates:

$ openssl s_client -showcerts -connect localhost.localdomain:443
CONNECTED(00000003)
depth=0 C = --, ST = SomeState, L = SomeCity, O = SomeOrganization, OU = SomeOrganizationalUnit, CN = localhost.localdomain, emailAddress = root@localhost.localdomain
verify error:num=20:unable to get local issuer certificate
verify return:1
depth=0 C = --, ST = SomeState, L = SomeCity, O = SomeOrganization, OU = SomeOrganizationalUnit, CN = localhost.localdomain, emailAddress = root@localhost.localdomain
verify error:num=21:unable to verify the first certificate
verify return:1
--- 
Certificate chain
 0 s:/C=--/ST=SomeState/L=SomeCity/O=SomeOrganization/OU=SomeOrganizationalUnit/CN=localhost.localdomain/emailAddress=root@localhost.localdomain
   i:/C=--/ST=SomeState/L=SomeCity/O=SomeOrganization/OU=SomeOrganizationalUnit/CN=localhost.localdomain/emailAddress=root@localhost.localdomain
-----BEGIN CERTIFICATE-----
MIIEETCCAvmgAwIBAgICb5kwDQYJKoZIhvcNAQELBQAwgbsxCzAJBgNVBAYTAi0t
MRIwEAYDVQQIDAlTb21lU3RhdGUxETAPBgNVBAcMCFNvbWVDaXR5MRkwFwYDVQQK
DBBTb21lT3JnYW5pemF0aW9uMR8wHQYDVQQLDBZTb21lT3JnYW5pemF0aW9uYWxV
bml0MR4wHAYDVQQDDBVsb2NhbGhvc3QubG9jYWxkb21haW4xKTAnBgkqhkiG9w0B
CQEWGnJvb3RAbG9jYWxob3N0LmxvY2FsZG9tYWluMB4XDTE3MDIwMjIyMTM1MFoX
DTE4MDIwMjIyMTM1MFowgbsxCzAJBgNVBAYTAi0tMRIwEAYDVQQIDAlTb21lU3Rh
dGUxETAPBgNVBAcMCFNvbWVDaXR5MRkwFwYDVQQKDBBTb21lT3JnYW5pemF0aW9u
MR8wHQYDVQQLDBZTb21lT3JnYW5pemF0aW9uYWxVbml0MR4wHAYDVQQDDBVsb2Nh
bGhvc3QubG9jYWxkb21haW4xKTAnBgkqhkiG9w0BCQEWGnJvb3RAbG9jYWxob3N0
LmxvY2FsZG9tYWluMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAnaxY
Fp6M3FKLOCPCod7oXQhaQjHQ5fudv+GyLogzAfvsKs508ZuUtl7uzkMEXTiHRKkN
H1L/U4CF6I/uww+aRRUAFRKsyZvGK3w2TiNXKE87UnjR6Z9hYngYpztwaMbzkm9U
6G7iLU9L5t9m9l3cuR8xcZpYKnwUwjxdVumCTCbOwAtg1SMVr8UimssuXLvHoSxn
GaLuqu4vzOg3jswsP7bgvHlSwd8DlLnWdS0snaJEys2bu+EGYgHUebItsZpwRuZ6
48u+6T3YsAh5RzwwF2kHbeiy+4Y9+Mmpfm4Tevftm8Yc9Tl1BBnDxw/oEMCXkMEn
0pX9lYtPq6Nq3C2Q6QIDAQABox0wGzAMBgNVHRMEBTADAQH/MAsGA1UdDwQEAwIF
4DANBgkqhkiG9w0BAQsFAAOCAQEAbo4QdiimaooItbE0EWHu0/3zcZ11QDea6a7p
/EJn1c6pNmfqbxCmkmkQf/bIj/bLLd4t0/PKyjfN3Y3mNr/igkS8AKh7Yd4myZGv
HqXjaS8WYI1RBKO/D8ikBvZjkEPf5AwH2kIJW8Wens+5wb65SaRBp1ujDaLZeSpa
5rOvyzvhOJn98InC5qtH8rXB+RTkGqg4jTSn+8FrzHZHvh5DYdTRCtfY6FGKl89V
sNJwWNecBpZQ9NNHKzwe8XVNZsTMvBCLqdrNbSzlsh6GT1JUrvOMgmoN7qLwPNt0
5O3065uEdpFypPGdLM0bt6x3rRvRC3J1dnZOe/1tiTisk+78Tw==
-----END CERTIFICATE-----
...

sed can be used to limit the output from OpenSSL when saving the certificate:

echo | openssl s_client -showcerts -connect localhost:443 2> /dev/null | \
  sed -n '/^-----BEGIN CERTIFICATE-----$/,/^-----END CERTIFICATE-----$/p' \
  > localhost.pem

Once you have a copy of the cert you can pass it to clients like curl:

$ curl --cacert localhost.pem https://localhost.localdomain/
<!DOCTYPE html>
<html>
  <head>
    <title>Hello world</title>
    ...

Working with proxies

OpenSSL recently added support for connecting through web proxies (see ticket 2651). Unfortunately this feature hasn't made it into CentOS 7 yet. It is however fairly straightforward to compile a later version of OpenSSL:

sudo yum groupinstall "Development tools"
curl -O https://www.openssl.org/source/openssl-1.1.0d.tar.gz
tar xzf openssl-1.1.0d.tar.gz
cd openssl-1.1.0d/
./configure
make

Assuming everything compiles correctly, you should now be able to use the -proxy option with s_client:

LD_LIBRARY_PATH=. ./apps/openssl s_client -proxy localhost:3128 -connect example.com:443

Note: after compiling, OpenSSL can be installed by running sudo make install; however use caution when replacing key system libraries like OpenSSL!