Accept server's self-signed ssl certificate in Java client Accept server's self-signed ssl certificate in Java client java java

Accept server's self-signed ssl certificate in Java client


You have basically two options here: add the self-signed certificate to your JVM truststore or configure your client to

Option 1

Export the certificate from your browser and import it in your JVM truststore (to establish a chain of trust):

<JAVA_HOME>\bin\keytool -import -v -trustcacerts-alias server-alias -file server.cer-keystore cacerts.jks -keypass changeit-storepass changeit 

Option 2

Disable Certificate Validation:

// Create a trust manager that does not validate certificate chainsTrustManager[] trustAllCerts = new TrustManager[] {     new X509TrustManager() {             public java.security.cert.X509Certificate[] getAcceptedIssuers() {             return new X509Certificate[0];        }         public void checkClientTrusted(             java.security.cert.X509Certificate[] certs, String authType) {            }         public void checkServerTrusted(             java.security.cert.X509Certificate[] certs, String authType) {        }    } }; // Install the all-trusting trust managertry {    SSLContext sc = SSLContext.getInstance("SSL");     sc.init(null, trustAllCerts, new java.security.SecureRandom());     HttpsURLConnection.setDefaultSSLSocketFactory(sc.getSocketFactory());} catch (GeneralSecurityException e) {} // Now you can access an https URL without having the certificate in the truststoretry {     URL url = new URL("https://hostname/index.html"); } catch (MalformedURLException e) {} 

Note that I do not recommend the Option #2 at all. Disabling the trust manager defeats some parts of SSL and makes you vulnerable to man in the middle attacks. Prefer Option #1 or, even better, have the server use a "real" certificate signed by a well known CA.


There's a better alternative to trusting all certificates: Create a TrustStore that specifically trusts a given certificate and use this to create a SSLContext from which to get the SSLSocketFactory to set on the HttpsURLConnection. Here's the complete code:

File crtFile = new File("server.crt");Certificate certificate = CertificateFactory.getInstance("X.509").generateCertificate(new FileInputStream(crtFile));// Or if the crt-file is packaged into a jar file:// CertificateFactory.getInstance("X.509").generateCertificate(this.class.getClassLoader().getResourceAsStream("server.crt"));KeyStore keyStore = KeyStore.getInstance(KeyStore.getDefaultType());keyStore.load(null, null);keyStore.setCertificateEntry("server", certificate);TrustManagerFactory trustManagerFactory = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());trustManagerFactory.init(keyStore);SSLContext sslContext = SSLContext.getInstance("TLS");sslContext.init(null, trustManagerFactory.getTrustManagers(), null);HttpsURLConnection connection = (HttpsURLConnection) new URL(url).openConnection();connection.setSSLSocketFactory(sslContext.getSocketFactory());

You can alternatively load the KeyStore directly from a file or retrieve the X.509 Certificate from any trusted source.

Note that with this code, the certificates in cacerts will not be used. This particular HttpsURLConnection will only trust this specific certificate.


Apache HttpClient 4.5 supports accepting self-signed certificates:

SSLContext sslContext = SSLContexts.custom()    .loadTrustMaterial(new TrustSelfSignedStrategy())    .build();SSLConnectionSocketFactory socketFactory =    new SSLConnectionSocketFactory(sslContext);Registry<ConnectionSocketFactory> reg =    RegistryBuilder.<ConnectionSocketFactory>create()    .register("https", socketFactory)    .build();HttpClientConnectionManager cm = new PoolingHttpClientConnectionManager(reg);        CloseableHttpClient httpClient = HttpClients.custom()    .setConnectionManager(cm)    .build();HttpGet httpGet = new HttpGet(url);CloseableHttpResponse sslResponse = httpClient.execute(httpGet);

This builds an SSL socket factory which will use the TrustSelfSignedStrategy, registers it with a custom connection manager then does an HTTP GET using that connection manager.

I agree with those who chant "don't do this in production", however there are use-cases for accepting self-signed certificates outside production; we use them in automated integration tests, so that we're using SSL (like in production) even when not running on the production hardware.