Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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

It looks like a standard question, but I couldn't find clear directions anywhere.

I have java code trying to connect to a server with probably self-signed (or expired) certificate. The code reports the following error :

[HttpMethodDirector] I/O exception (javax.net.ssl.SSLHandshakeException) caught  when processing request: sun.security.validator.ValidatorException: PKIX path  building failed: sun.security.provider.certpath.SunCertPathBuilderException:  unable to find valid certification path to requested target 

As I understand it, I have to use keytool and tell java that it's OK to allow this connection.

All instructions to fix this problem assume I'm fully proficient with keytool, such as

generate private key for server and import it into keystore

Is there anybody who could post detailed instructions?

I'm running unix, so bash script would be best.

Not sure if it's important, but code executed in jboss.

like image 473
Nikita Rybak Avatar asked May 23 '10 22:05

Nikita Rybak


People also ask

How do I accept a self-signed certificate?

Navigate to the site with the cert you want to trust, and click through the usual warnings for untrusted certificates. In the address bar, right click on the red warning triangle and "Not secure" message and, from the resulting menu, select "Certificate" to show the certificate.

How do I import a self-signed certificate into Java keystore?

Copy the file JAVA_HOME\lib\security\cacerts to another folder. Click OK for the warning about the trust path. Click OK when it displays the details about the certificate. Click Yes to accept the certificate as trusted.


2 Answers

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 chains TrustManager[] 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 manager try {     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 truststore try {      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.

like image 119
Pascal Thivent Avatar answered Oct 11 '22 13:10

Pascal Thivent


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.

like image 25
Johannes Brodwall Avatar answered Oct 11 '22 12:10

Johannes Brodwall