HTTPS and Java - Pitfalls and Best Practices - Part 2

Chain of Trust

When a Java program connects to a host over HTTPS, it’s important to know you’re really communicating with who you think you are. If I write a program which connects to https://www.example.com, then I’m sending information over a secure channel. How can I be sure I’m not talking to a malicious third party who is somehow intercepting my traffic, such as user credentials?

The identity of a server is proven with certificates. As an overview, when a Java program connects to a host over HTTPS, before secure communication can take place, the server sends back its certificate. Java then verifies that this certificate was issued by a legitimate Certificate Authority (CA). This is possible because Java ships with a trust store, containing a white list of trusted Certificate Authorities. As Java updates are rolled out, from time to time certificates are added to (or removed from) this trust store.

It was not possible for Java to communicate over HTTPS with sites certified by Let’s Encrypt until Java 8 update 101, which was released in July 2016. By this point the public beta was complete, and Let’s Encrypt had issued about 5 million certificates. If you try to connect to one of these websites using an earlier version of Java, you will see a stack trace containing that message “PKIX path building failed”. This means Java is unable to validate the certificate chain, because it doesn’t have the issuing CA in its trust store.

So what do we really mean when we say “certificate chain”? In practice, there are several levels of Certificate Authority: Intermediaries, and Root CAs. There is a small number of Root Certificate Authorities. The role of these is to hold a very closely-guarded private key, in a very secure location such as a HSM. Each private key is used to generate a certificate, representing the Root CA. This certificate contains a public key which can be used to verify the holder of the certificate also holds the corresponding private key. These certificates are installed as standard into web browsers, operating systems, and Java installations. The presence of this public key in Java’s trust store means it is able to verify that a certificate presented by a server was issued by one of these Root Certificate Authorities.

Certificates for individual websites are issued by Intermediary CAs. These intermediary Certificate Authorities are issued certificates by a Root CA – for example Let’s Encrypt is certified by the root CA called IdenTrust. The reason for the existence of these intermediaries is that it’s relatively easy to create new intermediate certificates (and also to revoke them), without having to roll out a software update en-masse each time. These intermediaries can then issue certificates to individual sites. Client software will validate the chain of certificates – your own domain can be certified by Let’s Encrypt, and Let’s Encrypt is certified by the Root CA called IdenTrust.

In the case of IdenTrust, their certificate was only added to Java 8 Update 101. This meant that any versions of Java prior to this version would fail to connect to any sites certified by Let’s Encrypt.

So to recap, keeping up to date with Java releases is really important to ensure you can safely authenticate parties, as well as maintaining compatibility with your customers.

Read on to Part 3 to learn about the role ciphers play.