There are many (good) resources out there for SSL and its underpinnings. There are detailed forays into the dark underbelly of handshake, encryption, certificates, keystores and the like. But how much do you really need to know? Surely, pursuit of absolute knowledge is a noble task, but when you’re on the company dime, just short on time, or … something else that ends in -ime, maybe you just need to know enough to get past your problem and move on to other things.
First thing to know is that SSL (as a protocol) is essentially dead, although perhaps obsolete is the better term here. The latest craze is TLS 1.2 with 1.3 on its way as of this writing. Why TLS? Because it’s more secure, of course! People still generally refer to secure communications over http as SSL, but the protocol of choice is TLS due to vulnerabilities in the original SSL, SSLv2 and SSLv3 protocols. We’ll continue to refer to the process of securing communication as “SSL” throughout this page, but bear in mind that we’re talking about the process, not the protocol.
Second, we need to differentiate between One-way SSL and Two-way SSL. The difference between them is really as plain as it seems. One-way SSL is when the client needs to validate the server, but not vice versa. Two-way SSL is, as you might expect, when both the client and server need to validate each other. We’ll get into how that happens later, but for now just know that those are two are the basic varieties of “who needs to trust who”. One-way SSL is by far the most common, and is what you most often encounter when banking or using any secure website.
Third, we need to understand the parts of how SSL works. I’ll try not to get into too much detail here (as I mentioned, there are plenty of good sites for that), but as a broad overview the important pieces (from a developer standpoint) are the handshake and the certificate exchanges.
The handshake consists of agreeing on a protocol (e.g. SSLv2, SSLv3, TLSv1.1, TLSv1.2), agreeing on a cipher (the mechanism for encryption) and performing the actual certificate exchange and verification. When you encounter an issue, this is one big place where things can go wrong.
The certificate exchange itself involves the server providing its certificate to the client, which the client verifies. In Two-way SSL, the client will also send its own certificate to the server to be verified. The respective certificates are stored in a key store (one on the client, one on the server) and are verified by using a trust store (again, one on the client, one on the server).
Where can things go wrong?
Lots of things can interfere with valid communication between two services via SSL. We’ll work our way through the handshake to highlight some examples. Note that these examples all will reference the use of Java, but these issues themselves are not necessarily language related.
Handshake Protocol Selection
In order to be able to handshake, the client and server must be able to agree on how to talk. Beyond that, they must also agree on how to handshake. There is a somewhat problematic protocol called SSLv2Hello that can cause issues when the server is more restrictive on which protocols it accepts. SSLv2Hello (like all the SSL protocols) is considered vulnerable to attack and so, rightly, it should not be used. But upgrades take time and can be forgotten and adoption by end users can be slow. So sometimes these protocols hang around longer than they really should.
SSLv2Hello is not a normal SSL protocol in that it’s not used for sending any actual messages. It’s a ‘pseudo-protocol’ that’s used to perform the handshake. So how can you perform a handshake with a protocol that is known to have vulnerabilities when connecting to a server that is restricted to TLS? Easy – you can’t.
Sometimes this manifests itself plainly by stating that SSLv2Hello failed. Other times it’s more cryptic, like “Early EOF Exception”. Debugging these can be a bit trickier – you’ll often need to know the protocols the server supports beforehand or be able to debug the SSL connection. In Java, adding -Djava.net.debug=ssl should help.
So how do we disable SSLv2Hello from ever being used in the first place? In Java we can utilize the -https.protocols and jdk.tls.client.protocols flags to “TLSv1.1,TLSv1.2” (for example) to include only TLS protocols v1.1 and 1.2.
Invalid Cipher Selection
Just like protocols, ciphers can be compromised as well. It can be more difficult to diagnose these situations when dealing with a simple generic Java exception. Again, adding -Djavax.net.debug=ssl can help pinpoint the issue.
Additionally, there are legal restrictions on the strength of ciphers that can be exported from the United States (outside of the United States, you’ll have to consult the applicable laws). In the event that you are inside the United States, you may simply need to allow other ciphers to be used. For Java, those are found here.
Certificates can be thought of as ID cards that two entities might show to each other to prove they are who they say they are. When you contact your bank and enter your password, you want to make sure that they are who they say they are! So imagine when you go to the bank, the teller shows you identification that verifies the teller’s identity. But we can’t just trust the teller, or even the ID. We also must trust whoever issued that ID. In this case, the ID itself is analogous to the certificate, while the issuer is analogous to the Root Certificate Authority (Root CA’s).
Windows and Java come pre-installed with a list of Trusted Root CA’s that are generally recognized as, well, trustworthy. If they issue a certificate to a server, then that server (according to the powers that be) can be trusted as well. This is all well and good, but ID’s (and certificates) don’t get issued for free! (Well, sometimes they do)
Often times during development, it’s too costly or time-consuming to procure valid certificates signed by recognized Root CA’s, so a self-signed certificate is used. Since it’s development, this in and of itself usually isn’t a problem, but it can be if your client isn’t set up to trust that certificate. (In Java, this usually manifests itself with a “PKIX path building failed:
sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target” or similar message) Many people recommend what amounts to turning your SSL Certificate Validation off (AKA trusting all certificates). This gets you past the issue and lets you proceed but is not recommended because often times this slips past into production and then things can really get compromised!
So another possibility is to simply add the server’s certificate to the client’s trust store. This takes a few more steps, but it is easier once you’ve done it a few times. A walk-through for that is coming but the steps involve using the browser to download the certificate. Once that is done, I’d recommend copying over the default java cacerts and adding the certificate to the new truststore using Java’s keytool command. Then you’ll have to tell your client to use the new truststore instead of Java’s default by using -Djavax.net.ssl.trustStore and -Djavax.net.ssl.trustStorePassword.
For Two-way SSL, the reverse has to take place as well. While you may have to type in your banking password to get into your account, another option would be to prove to the bank that you are who you say you are via certificate as well. This generally isn’t the case for current websites, as it would be too cumbersome for each and every customer to go and procure valid certificates that the bank trusts, but it is an option in environments where a smaller number of unique hosts communicate.
And those are the major points of general issues faced by developers when using SSL. Certainly there is more (much more!) to cover, but most of the time the issue falls into one of those categories. As time permits I’ll add diagrams and walk-throughs, but if you’d like to see additional details or examples, feel free to leave some comments and I’ll try to prioritize those. Thanks for reading!