Java Exception Handling

Java Exception Handling – SSLHandshakeException

Moving along through our detailed Java Exception Handling series we come to the SSLHandshakeException. The SSLHandshakeException is thrown when an error occurs while a client and server connection fails to agree on their desired security level. This exception is one of a handful of classes that inherits from the parent SSLException class.

Within this article we’ll examine the SSLHandshakeException by looking at where it sits in the overall Java Exception Hierarchy. We’ll also dig into some functional sample code that shows how an SSL connection might be established in a Java application, and how failure to set things up properly can lead to SSLHandshakeExceptions, so let’s get to it!

The Technical Rundown

All Java errors implement the java.lang.Throwable interface, or are extended from another inherited class therein. The full exception hierarchy of this error is:

Full Code Sample

Below is the full code sample we’ll be using in this article. It can be copied and pasted if you’d like to play with the code yourself and see how everything works.

This code sample also uses the Logging utility class, the source of which can be found here on GitHub.

When Should You Use It?

To understand what can cause an SSLHandshakeException we should briefly discuss how SSL connections differ from non-secure connections in Java. As it happens, SSL (Secure Socket Layer) in general has since been “replaced” with the newer protocol known as TLS (Transport Layer Security). Regardless, many modern code bases, languages, documents, and articles (including this one) continue to refer to this protocol as SSL, since both terms are commonly used interchangeably.

That said, SSL works the same under the hood no matter the language it’s being used with. An SSL connection is established between a client and server using the common practice of public-key cryptography. Essentially, a pair of keys are created that are uniquely linked to one another (through mathematical algorithms). One key is known only by the server as is known as the private key, while the other key is known to everyone, including the client, and is known as the public key. Each key can only perform a transformation of data in one direction. Thus, the public key is only capable of encrypting data, while the private key is only capable of decrypting data. Since only one key is publicly available, and yet both keys are required to successfully transmit, encrypt, and then decrypt data, a secure connection and exchange of information is established, so long as the client trusts the server.

This last point is critical and where an SSL certificate comes into the picture. An SSL certificate is a file that combines a key with unique information about the organization/site/domain that the certificate represents. This is why, when you visit a site like https://airbrake.io you’ll typically see a green lock icon in your browser that indicates that the site has a trusted security certificate. You can even view the details of the certificate. In Chrome, press Ctrl + Shift + I, click Security, and then click View Certificate. This dialog shows all the details about the site’s certificate, including the Issued to field, which shows what specific domain the certificate is valid for. Most organizations just use their base domain name (e.g. airbrake.io), so that the certificate applies to all pages and sub-domains that might be added later.

That said, just having an SSL certificate isn’t enough, since that doesn’t prove that the site really is trustworthy or is who the browser says it is. This is where a certificate authority comes in, which is a third-party that issues SSL certificates. There are a handful of trusted authorities out there that issue certificates, and these authorities are used to authenticate the signature that is claimed on each SSL certificate they have issued. For example, the current airbrake.io certificate was issued by SSL.com.

Alright, so with that out of the way we can explore our Java code sample that attempts to create a simple client/server connection via SSL. For no particular reason we start with the SSLServer class:

Most of the logic occurs in the createServer(int port) method, where we get the default SSLServerSocketFactory instance, then try creating a ServerSocket using the passed port. A continuous loop is then used to accept incoming connections, process the incoming information, and output the result to the client. You may also notice that the SSLServer class implements the Runnable interface, which includes the run() method. This interface allows us to pass an instance of this class to a new Thread instance, so we can create a server in a separate thread.

Next is the SSLClient class:

Since this is a client class we start with the default SSLSocketFactory, as opposed to the server version of SSLServerSocketFactory. We then attempt to create and connect to the socket of the specified host and port, after which we prompt the user for input to pass to the server. Again, implementation of the Runnable interface allows us to execute the run() method on a unique thread.

With both the client and server set up we can test things out in our Main.main(...) method:

Since both the SSLServer and SSLClient use infinite loops we need to instantiate each on a separate thread, so we’ve done so above. Executing this code produces the following output:

Perhaps unsurprisingly our code threw an SSLHandshakeException, indicating that there was a handshake_failure. This is a bit vague, but we can see that the second exception’s inner exception says the issue is “no cipher suites in common.” In fact, the issue is that we haven’t created or established the SSL certificate that the client and server should be referencing! Normally a certificate is obtained from a trusted certificate authority for a public site, but for testing and development purposes it’s common practice to create a self-signed certificate, which just means you are the issuer. These are typically issued to the localhost domain, so they’ll function locally but not be trusted elsewhere.

Creating a self-signed certificate for Java involves using the keytool command. We won’t go into the full explanation of the following commands, but check out the official documentation for more details on using keytool. For now, we can start by generating a new key and storing it in a local keystore file:

Next, we’ll export the keystore to a local file:

Now we want to import the certificate into the truststore. The best way to think of the difference between a keystore and truststore is that the keystore is used for private keys, while the truststore is for public certificates.

These files will be generated locally, so we now have keystore, truststore, and airbrake.cer. We now need to tell our little Java application about these certificates. There are a number of ways to accomplish this, but for testing purposes we’ll just directly inform our application at runtime by setting a few environment variables.

To accomplish this we’ll add the following to the SSLServer class:

And add this to the SSLClient class:

As the property names suggest, we’re telling the JVM where it can find the appropriate keystore and truststore, along with the key passwords for each. It will then use these property values when an SSL connection is established in the code we’ve already written.

With our certificate created and stored in the appropriate public and private stores, we can now execute our Main.main() method as before, attempting to create a server and client connection. Doing so now produces the following output:

Everything works just as expected! Our server asks for a message, so entering "Hello there" sends the message via our SSL connection to the server, which is processed and returned to the client prompting for another message.

The Airbrake-Java library provides real-time error monitoring and automatic exception reporting for all your Java-based projects. Tight integration with Airbrake’s state of the art web dashboard ensures that Airbrake-Java gives you round-the-clock status updates on your application’s health and error rates. Airbrake-Java easily integrates with all the latest Java frameworks and platforms like Spring, Maven, log4j, Struts, Kotlin, Grails, Groovy, and many more. Plus, Airbrake-Java allows you to easily customize exception parameters and gives you full, configurable filter capabilities so you only gather the errors that matter most.

Check out all the amazing features Airbrake-Java has to offer and see for yourself why so many of the world’s best engineering teams are using Airbrake to revolutionize their exception handling practices!