kamal
kamal

Reputation: 9785

How to bypass certificate checking in a Java web service client

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)

Upvotes: 5

Views: 28680

Answers (2)

Devon_C_Miller
Devon_C_Miller

Reputation: 16518

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 ...

Upvotes: 14

Dungeon Hunter
Dungeon Hunter

Reputation: 20623

Just Implement your own trust manager like in the below code

import java.net.URL;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;
import java.io.*;
import javax.net.ssl.HttpsURLConnection;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLSocketFactory;
import javax.net.ssl.TrustManager;
import javax.net.ssl.X509TrustManager;

public class JavaHttpsExample
{
  public static void main(String[] args)
  throws Exception
  {
    String httpsURL = "https://a.b.c.d/zz?wsdl";
    URL myurl = new URL(httpsURL);
    SSLContext ssl = SSLContext.getInstance("TLSv1");
    ssl.init(null, new TrustManager[]{new SimpleX509TrustManager()}, null);
    SSLSocketFactory factory = ssl.getSocketFactory();


    HttpsURLConnection con = (HttpsURLConnection)myurl.openConnection();
    con.setSSLSocketFactory(factory);
    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();
  }
}

class SimpleX509TrustManager implements X509TrustManager {
    public void checkClientTrusted(
            X509Certificate[] cert, String s)
            throws CertificateException {
    }

    public void checkServerTrusted(
            X509Certificate[] cert, String s)
            throws CertificateException {
      }

    @Override
    public X509Certificate[] getAcceptedIssuers() {
        // TODO Auto-generated method stub
        return null;
    }

}

Upvotes: 7

Related Questions