Clearing Java certificates cache (force reload certificates)

A simple question here.

One application gave me this exception when trying to access a website with a expired certificate: java.security.cert.CertificateExpiredException

So, I renewed the certificated from the website machine and restarted it. When I try to access it from Firefox or Chrome it will load the new certificate (which it's expiration date is set somewhere near 2040).

The problem is, Java applications doesn't seems to renew this certificate, it seems to be stuck in some kind of internal cache. I already tried to add it to the keystore and set options in the application properties like -Dcom.sun.net.ssl.checkRevocation=false. No matter what I do, it always throw me a java.security.cert.CertificateExpiredException

Any ideas?


The default Java keystore location is a .keystore file under your home directory (user.home system property) so unless you specify otherwise that is where a Java application will look.

Try running:

$ keytool -list -keystore ~/.keystore -storepass changeit -v

to see if the expired certificate is in there.

If you want to specify a different identity keystore to use then you can do so using the following system properties:

javax.net.ssl.Keystore=/path/to/identity.jks
javax.net.ssl.keyStorePassword=mykeystorepassword

I believe that Firefox uses NSS and you can view its keystore using the certutil utility (from the nss-tools or similar package) - something like:

$ certutil -L -d sql:$HOME/.pki/nssdb

You should be able to use the pk12util utility to extract the key and certificate into a PKCS12 file but you're probably better off just generating a new certificate signing request using the keytool utility.

Note that a revoked certificate is not the same as an expired one which is why your checkRevocation=false doesn't work. The CA can revoke a certificate at any time even if it has not yet expired and this indicates that it should no longer be trusted.


The issue turns out to be due to a bug which causes HttpsUrlConnection not to use SNI when a custom hostname verifier is used.

  • This SNI bug can be quickly verified.

Bug workaround

javax.net.ssl.HttpsURLConnection connection = (javax.net.ssl.HttpsURLConnection) new java.net.URL(url).openConnection();
connection.setHostnameVerifier(... hostname verifier which indirectly causes a bug ...);

//the remaining code fixes the bug by forcing the use of SNI even with the custom hostname verifier
final javax.net.ssl.SSLSocketFactory originalSocketFactory = connection.getSSLSocketFactory();
connection.setSSLSocketFactory(new javax.net.ssl.SSLSocketFactory() {
  public String[] getDefaultCipherSuites() {
    return originalSocketFactory.getDefaultCipherSuites();
  }

  public String[] getSupportedCipherSuites() {
    return originalSocketFactory.getSupportedCipherSuites();
  }

  private java.net.Socket convertSocket(javax.net.ssl.SSLSocket socket, String host) {
    javax.net.ssl.SNIHostName serverName = new javax.net.ssl.SNIHostName(host);
    java.util.List<javax.net.ssl.SNIServerName> serverNames = new java.util.ArrayList<>(1);
    serverNames.add(serverName);
    javax.net.ssl.SSLParameters params = socket.getSSLParameters();
    params.setServerNames(serverNames);
    socket.setSSLParameters(params);
    return socket;
  }

  public java.net.Socket createSocket(java.net.Socket s, String host, int port, boolean autoClose) throws IOException {

    //host = new URL(url).getHost();
    javax.net.ssl.SSLSocket socket = (javax.net.ssl.SSLSocket) originalSocketFactory.createSocket(s,host,port,autoClose);
    return convertSocket(socket, host);
  }

  public java.net.Socket createSocket(java.net.InetAddress host, int port) throws IOException {
    //You may need convertSocket here, I didn't
    return originalSocketFactory.createSocket(host, port);
  }

  public java.net.Socket createSocket(java.net.InetAddress address, int port, java.net.InetAddress localAddress, int localPort) throws IOException {
    //You may need convertSocket here, I didn't
    return originalSocketFactory.createSocket(address, port, localAddress, localPort);
  }

  public java.net.Socket createSocket(String host, int port) throws IOException {
    //You may need convertSocket here, I didn't
    return originalSocketFactory.createSocket(host, port);
  }

  public java.net.Socket createSocket(String host, int port, java.net.InetAddress localHost, int localPort) throws IOException {

    //You may need convertSocket here, I didn't
    return originalSocketFactory.createSocket(host, port, localHost, localPort);
  }
});