ProblemI was developing an application that communicated with a remote server over SSL. In production, the SSL certificate used by the remote server would be chained to a valid root certificate authority. However, for development I had to test against a server with a self-signed certificate which was causing a problem since I wanted SSL verification to be fully enabled -- It would have been no good for me to just disable certificate validation because when the code went to production I would lose a main purpose for using SSL, verifying the identity of the communicating server
This situation is very common. When deploying SSL to an internal server, many system administrators tend to use what is called a self-signed certificate. This is a certificate that is generated and signed by itself. This cost no money to generate, but it won't be trusted by clients because there's no chain of trust to follow. So, when I made a connection with this server, the underlying SSL implementation complained that I was not communicating with a verifiable source.
SolutionMy test and production systems are running on the Ubuntu linux distribution. So I'm going to describe how to add a new trusted certificate to a Ubuntu system. Once the target SSL certificate is trusted, connections from the system will no longer complain of being unverifiable and the code can be exactly as it will be production.
Getting the Public CertificateFirst, you'll need the public portion of the self-signed certificate in PEM format. If you generated it yourself then this should be easy to find as its part of what you installed in your web server (NOTE: remember you want the PUBLIC part). If you didn't or don't know which is the public part, we can use the openssl library to retrieve it directly from the operating target server, the following is the necessary openssl command executed against google. In a self signed example, there'd be only 1 certificate in the certificat chain section:
$ openssl s_client -connect google.com:443 -showcertsThe bolded section included the "----BEGIN CERTIFICATE----" and "----END CERTIFICATE----" lines represents a PEM encoded public certificate. This is the content that you'll want to add to your trusted certificates on your system.
CONNECTED(00000003)depth=1 /C=ZA/O=Thawte Consulting (Pty) Ltd./CN=Thawte SGC CA
verify error:num=20:unable to get local issuer certificate
0 s:/C=US/ST=California/L=Mountain View/O=Google Inc/CN=www.google.com
i:/C=ZA/O=Thawte Consulting (Pty) Ltd./CN=Thawte SGC CA
1 s:/C=ZA/O=Thawte Consulting (Pty) Ltd./CN=Thawte SGC CA
i:/C=US/O=VeriSign, Inc./OU=Class 3 Public Primary Certification Authority
subject=/C=US/ST=California/L=Mountain View/O=Google Inc/CN=www.google.com
issuer=/C=ZA/O=Thawte Consulting (Pty) Ltd./CN=Thawte SGC CA
No client certificate CA names sent
SSL handshake has read 1765 bytes and written 313 bytes
New, TLSv1/SSLv3, Cipher is RC4-SHA
Server public key is 1024 bit
Protocol : TLSv1
Cipher : RC4-SHA
Key-Arg : None
Start Time: 1289266030
Timeout : 300 (sec)
Verify return code: 0 (ok)
Adding the CertificateOn Ubuntu, you'll want to create a new file in /usr/local/share/ca-certificates named appropriately for the host certificate you're adding. The name can be anything, but it must end with a .crt extension for the next command step to work. For my example, google.com.crt would be appropriate. Now that you've created the file, you need to tell the system about it. This is done with the command:
$ sudo update-ca-certificatesThe command will both, link the certificate appropriately into /etc/ssl/certs as well as updating the JRE cacerts files (if you have a JRE installed).
Updating certificates in /etc/ssl/certs... 1 added, 0 removed; done.
Running hooks in /etc/ca-certificates/update.d....
updating keystore /etc/ssl/certs/java/cacerts...