Clearing Java certificates cache (force reload certificates)

24,730

Solution 1

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

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);
  }
});

Solution 2

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.

Share:
24,730

Related videos on Youtube

Alberto Fernández
Author by

Alberto Fernández

Programmer.

Updated on September 18, 2022

Comments

  • Alberto Fernández
    Alberto Fernández over 1 year

    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?

    • Admin
      Admin over 11 years
      Hello, did it work?
    • user3338098
      user3338098 almost 6 years
      using -Djavax.net.debug=SSL I can see that it is using the old certificate and that the root certificate chain is the same, it's just the main certificate that is outdated (and expired). Could the website be returning the correct certificate to my browser while returning an expired one to my java app?(seems unlikely)
    • slm
      slm almost 6 years
      @user3338098 - Is this an application running on Tomcat or some type of appserver? The app may have its own .jks file. I'd try using lsof -p <pid of java app> and see if you can't find where it's picking up the certificate. It's going to be in 1 of 3 places, the OS, the JVMs, or the locally to the app. It has to be in one of these places.
    • user3338098
      user3338098 almost 6 years
      @slm Just a plain java app packaged in a jar. lsof run on a continuous loop only occasionally reports the use of /etc/pki/java/cacerts even though -Djavax.net.debug=SSL reports keyStore is : /root/.keystore (I ran java with -Djavax.net.ssl.keyStore=/root/.keystore) and trustStore is: /usr/lib/jvm/java-1.8.0-openjdk-1.8.0.71-1.b15.el6_7.x86_64/‌​jre/lib/security/cac‌​erts I checked all these key stores for the sites certificate and found nothing and even added the updated certificate to all of them in the hopes it would be used, but it didn't work, same certificate expiration issue.
    • slm
      slm almost 6 years
      @user3338098 - when you connect to the app using something like openssl s_client -connect <host>:<port> does it present the same 'stale' cert that you're seeing when you use the browser?
    • user3338098
      user3338098 almost 6 years
      @slm sorry for not responding, my bounty on this question caused me to run out of the reputation required to comment. Your comment sent me in the right direction towards the solution.
    • slm
      slm almost 6 years
      @user3338098 - no worries, glad you got it figured out.
  • user3338098
    user3338098 almost 6 years
    might not help but there is also a cacerts file that is used, the path is like /usr/lib/jvm/java-version/jre/lib/security/cacerts on unix based systems