Importing certificate into Java Keystore


If any host is using self-signed certificate, then a typical HTTPS request to that host will fail in the Java code with the following exception:

javax.net.ssl.SSLHandshakeException: sun.security.validator.ValidatorException: PKIX path building failed: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target

A very NOT recommended solution to this problem is to bypass the certificate check

import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;

import javax.net.ssl.X509TrustManager;

public class TrustAnyTrustManager implements X509TrustManager 
{

    public void checkClientTrusted(X509Certificate[] chain, String authType) throws CertificateException 
    {
    }

    public void checkServerTrusted(X509Certificate[] chain, String authType) throws CertificateException 
    {
    }

    public X509Certificate[] getAcceptedIssuers() 
    {
        return new X509Certificate[] {};
    }
}

And then use this TrustAnyTrustManager while forming the request

HttpsURLConnection conn = null;
URL url = new URL(serviceUrl);
conn = (HttpsURLConnection) url.openConnection();
SSLContext sc = SSLContext.getInstance("SSL");  
sc.init(null, new TrustManager[]{new TrustAnyTrustManager()}, new java.security.SecureRandom());  
conn.setSSLSocketFactory(sc.getSocketFactory());

Let me re-iterate, do not use the above code in production or even development envrionment. This is highly insecure and susceptible to MITM attacks

A better solution is to import the untrusted certificate into the Java keystore. First, we have to export the certificate. This can be done using Chrome and then import it through command line.

All this can be automated through either a simple shell script, which can be executed on the server itself. For example, the below script can be executed using:

sudo ./javacert.sh google.com

Though, the above solution works for development servers, this is not a recommended solution for production servers. A situation should not arise where you have to import the un-siged certificate into production environment, but even if it arises, its better to go with orchestration solution either using Ansible or Chef

In my case, the below simple ansible playbook does the job for us:

---
 - hosts: all
   tasks:
    - name: Import SSL certificate from google.com to a given cacerts keystore
      java_cert:
        cert_url: google.com
        cert_port: 443
        keystore_path: /usr/lib/jvm/jre7/lib/security/cacerts
        keystore_pass: changeit
        state: present