Java – How to bypass certificate checking in a Java web service client

httpsjavawsdl

Use Case:
There is a web service posted as https://a.b.c.d/zz?wsdl

What i would like to do is to query this URI and IF i get a VALID WSDL, i return a boolean "true" else "false.
Now, if i go via Chrome browser to this URL, i would have to manually do an accept on the cert warning, and THEN the WSDL gets downloaded. But how can do this via Java / HttpsURLConnection

import java.net.URL;
import java.io.*;
import javax.net.ssl.HttpsURLConnection;

public class JavaHttpsExample
{
  public static void main(String[] args)
  throws Exception
  {
    String httpsURL = "https://a.b.c.d/zz/V2.0/api?wsdl";
    URL myurl = new URL(httpsURL);
    HttpsURLConnection con = (HttpsURLConnection)myurl.openConnection();
    InputStream ins = con.getInputStream();
    InputStreamReader isr = new InputStreamReader(ins);
    BufferedReader in = new BufferedReader(isr);

    String inputLine;

    while ((inputLine = in.readLine()) != null)
    {
      System.out.println(inputLine);
    }

    in.close();
  }
}

and i get an error:

Exception in thread "main" javax.net.ssl.SSLHandshakeException:
java.security.cert.CertificateException: No subject alternative names
matching IP address a.b.c.d found at
com.sun.net.ssl.internal.ssl.Alerts.getSSLException(Unknown Source)
at com.sun.net.ssl.internal.ssl.SSLSocketImpl.fatal(Unknown Source)
at com.sun.net.ssl.internal.ssl.Handshaker.fatalSE(Unknown Source)
at com.sun.net.ssl.internal.ssl.Handshaker.fatalSE(Unknown Source)
at
com.sun.net.ssl.internal.ssl.ClientHandshaker.serverCertificate(Unknown
Source) at
com.sun.net.ssl.internal.ssl.ClientHandshaker.processMessage(Unknown
Source) at
com.sun.net.ssl.internal.ssl.Handshaker.processLoop(Unknown Source)
at com.sun.net.ssl.internal.ssl.Handshaker.process_record(Unknown
Source) at
com.sun.net.ssl.internal.ssl.SSLSocketImpl.readRecord(Unknown Source)
at
com.sun.net.ssl.internal.ssl.SSLSocketImpl.performInitialHandshake(Unknown
Source) at
com.sun.net.ssl.internal.ssl.SSLSocketImpl.startHandshake(Unknown
Source) at
com.sun.net.ssl.internal.ssl.SSLSocketImpl.startHandshake(Unknown
Source) at
sun.net.www.protocol.https.HttpsClient.afterConnect(Unknown Source)
at
sun.net.www.protocol.https.AbstractDelegateHttpsURLConnection.connect(Unknown
Source) at
sun.net.www.protocol.http.HttpURLConnection.getInputStream(Unknown
Source) at
sun.net.www.protocol.https.HttpsURLConnectionImpl.getInputStream(Unknown
Source) at JavaHttpsExample.main(JavaHttpsExample.java:14) Caused by:
java.security.cert.CertificateException: No subject alternative names
matching IP address a.b.c.d found at
sun.security.util.HostnameChecker.matchIP(Unknown Source) at
sun.security.util.HostnameChecker.match(Unknown Source) at
com.sun.net.ssl.internal.ssl.X509TrustManagerImpl.checkIdentity(Unknown
Source) at
com.sun.net.ssl.internal.ssl.X509TrustManagerImpl.checkServerTrusted(Unknown
Source)

i have replaced the real IP with a.b.c.d (of course)

Best Answer

Don't use a stubbed out TrustManager as this makes your application trust everyone. I would recommend downloading the certificate presented by the site and adding it to a private, trusted keystore. This lets you make an exception for that one site without greenlighting everyone.

I also like this approach because it requires no code changes.

In Chrome, click the lock icon to the left of the url. Then click "Certificate Information". Go to the "Details" tab and click "Copy to file". Save it as a "base64 encoded X.509 (.cer)" to "SITENAME.cer".

Copy $JAVA_HOME/lib/security/cacerts to your application's directory as "mykeystore.jks".

Install the certificate with:

keytool -keystore mykeystore.jks -storepass changeit -importcert -alias SITENAME -trustcacerts -file SITE.cer

Now, when you run your application, tell it to use the private certificate store:

java -Djavax.net.ssl.trustStore=mykeystore.jks ...