How to Ignore an Invalid SSL Certificate in Java
Sometimes during development it is useful to use a certificate whose CN
(Common Name) does not match the host name in the URL, for example
localhost
. In these cases Java will throw an SSLHandshakeException
.
How can we easily disable certificate checking for localhost
and other
domains of our choosing?
Your own certificate checking
The easiest way is to create your own class that implements the
interface HostnameVerifier
:
WhitelistHostnameVerifier.java
package com.relentlesscoding.https;
import javax.net.ssl.HostnameVerifier;
import javax.net.ssl.HttpsURLConnection;
import javax.net.ssl.SSLSession;
import java.util.HashSet;
import java.util.Set;
// singleton
enum WhitelistHostnameVerifier implements HostnameVerifier {
// these hosts get whitelisted
INSTANCE("localhost", "sub.domain.com");
private Set whitelist = new HashSet<>();
private HostnameVerifier defaultHostnameVerifier =
HttpsURLConnection.getDefaultHostnameVerifier();
WhitelistHostnameVerifier(String... hostnames) {
for (String hostname : hostnames) {
whitelist.add(hostname);
}
}
@Override
public boolean verify(String host, SSLSession session) {
if (whitelist.contains(host)) {
return true;
}
// important: use default verifier for all other hosts
return defaultHostnameVerifier.verify(host, session);
}
}
Main.java
// use our HostnameVerifier for all future connections
HttpsURLConnection.setDefaultHostnameVerifier(
WhitelistHostnameVerifier.INSTANCE);
final String url = "https://relentlesscoding.com";
HttpsURLConnection conn =
(HttpsURLConnection) new URL(url).openConnection();
System.out.println(conn.getResponseCode());
HttpsURLConnection.DefaultHostnameVerifier#verify
always returns
false
, so we might as well return false
ourselves, but this way the
code will keep working when the implementation of HttpsURLConnection
changes.
Note: you can set the default HostnameVerifier
globally by using the
static method setDefaultHostnameVerifier
, or you can set it per
instance, using conn.setHostnameVerifier
.
Problems with Let’s Encrypt certificates
One thing I found out while testing this, was that OpenJDK version 1.8.0_131 won’t correctly handle Let’s Encrypt certificates:
java.io.IOException: HTTPS hostname wrong: should be <relentlesscoding.com>
This is due to the fact that Let’s Encrypt certificates are not present in the Java installation. Upgrading to version 1.8.0_141 resolves this (for Oracle versions >= 8U101 or >= 7U111).
Whitelisting, NOT disabling certificate checking
The best approach here is whitelisting. This will keep the certificate
checking in place for most sites, and will only disable it for
pre-approved hosts. I saw quite a bit of examples online that disable
certificate checking completely, by writing the verify
method as
follows:
public boolean verify(String hostname, SSLSession session) {
return true;
}
This is not secure, as it completely ignores all invalid certificates.