Cheat sheet for secure connection in Java
The standard Java package that provides an abstraction over secure network communication (certificate management, handshaking and verification) is javax.net.ssl
. The most popular protocol which developers have to deal with is the HTTPS. HTTPS is the secure version of the request-response HTTP (RFC 2616) protocol. It can be either implemented over SSL or a more secure and upgraded version – TLS.
Protocol | Published | Website support | Security |
---|---|---|---|
SSL 1.0 | Unpublished | ||
SSL 2.0 | 1995 | 1.6% | Insecure |
SSL 3.0 | 1996 | 6.7% | Insecure |
TLS 1.0 | 1999 | 65.0% | Depends on cipher and client mitigations |
TLS 1.1 | 2006 | 75.1% | Depends on cipher and client mitigations |
TLS 1.2 | 2008 | 96.0% | Depends on cipher and client mitigations |
TLS 1.3 | 2018 | 18.4% | Secure |
Sources: https://en.wikipedia.org/wiki/Transport_Layer_Security, https://www.ssllabs.com/ssl-pulse/
The support for various protocol versions and ciphers in Java is implemented in the form of a pluggable security architecture through the means of security providers. By default, at least one security provider is distributed with JRE/JDK and if needed a third-party provider can be added.
For example, at the moment of writing this, Oracle JRE8/JDK8 does not provide support for TLS 1.3, though it is planned for 2020-07-14. Meanwhile, you can enjoy TLS 1.3 on Java 11 and on Azul's Zing/Zulu Java 8 JVMs/JDKs.
Customizing the secure connection
A security provider is injected into the SSLContext which is used for initiating the connection. The default supported protocols can be seen by querying SSLContext parameters SSLContext.getDefault().getSupportedSSLParameters().getProtocols()
. To restrict the list only to the chosen protocols, we can use setEnabledProtocols(String[] protocols)
method of the SSLContext
.
Let's check first what elements are there to initialize the context:
Class | Description | Example use |
---|---|---|
SSL | An abstraction over SSL/TSL connection, facilitates connection using certificates contained within managed trust and key stores. |
|
Trust | A keystore containing trusted certificates from the client's point of view. | |
Key | A store containing our identity certificate. |
|
Trust / Key | Factories for initialization of trust/key managers from key stores or managers provided by the runtime. The trust/key managers can also be instantiated using your own implementation. |
|
Key | Presents a certificate chain with the public key to the client and provides a private key for decryption of the data encrypted by the public key. |
We've seen that passwords are associated with key stores but private keys can also have a password. Since there is no way to provide a password for the private key to the KeyManager, when the default "SunX509" KeyManagerFactory algorithm is used, it's assumed to be the same as the keystore password.
However, if we use "NewSunX509" algorithm we can overcome this issue – a more detailed explanation by Will Argent. |
Trust | Decides whether the credentials provided by the peer should be accepted. | |
Hostname | During the SSL/TLS connection to further prevent MITM attacks, it's recommended to verify whether the target hostname is the same as the one provided with the certificate. |
Three popular implementations can be found in Apache HttpComponents library:
|
Most of the HTTP clients support customizing the connection through the SSLContext class. In general, the default configuration provided by the JDK/JRE would often suffice when making a secure connection as a client. Unless of course the server also requires a valid certificate from us. In such a case, we will have to prove our identity through the KeyManager.
Some examples of the final link between the secure connection configuration and client/connection classes:
// javax.net.ssl
HttpsURLConnection connection = (HttpsURLConnection) url.openConnection();
connection.setSSLSocketFactory(sslContext.getSocketFactory());
connection.setHostnameVerifier(hostnameVerifier);
// org.apache.httpcomponents:httpclient:4.5
CloseableHttpClient httpClient = HttpClientBuilder.create()
.setSSLContext(sslContext)
.setHostnameVerifier(hostnameVerifier)
.build();
// com.squareup.okhttp3:okhttp:4.x
OkHttpClient okHttpClient = OkHttpClient.Builder()
.sslSocketFactory(sslContext.getSocketFactory(), trustManager)
.hostnameVerifier(hostnameVerifier)
.build()
// org.glassfish.jersey.core:jersey-client:2.x
Client jerseyClient = ClientBuilder.newBuilder()
.sslContext(sslContext)
.hostnameVerifier(hostnameVerifier)
.build();
To manage key stores, create CSR (Certificate Signing Request – to be signed by a Certification Authority) we use the keytool
command-line program included in JRE/JDK bin
directory. For some popular commands, refer to the SSL Shopper's article.
When in doubt why the standard configuration does not work, it's always a good idea to check the validity of the site certificate, domain name, and trust chain unless the certificate is self-signed and imported into the trust store (if so, verify this too).
Often though, on the servers, checking the certificate through the browser isn't a feasible scenario as they're usually run in a headless mode. You can still use some command-line tools like curl or openssl to extract the certificate in such a situation.
Cheers, and stay safe!