20

I want access a SOAP webservice url having https hosted in a remote vm. I am getting an exception while accessing it using HttpURLConnection.

Here's my code:

import javax.net.ssl.*;
import java.io.OutputStream;
import java.net.HttpURLConnection;
import java.net.URL;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;

/**
 * Created by prasantabiswas on 07/03/17.
 */
public class Main
{
    public static void main(String [] args)
    {
        try
        {
            URL url = new URL("https://myhost:8913/myservice/service?wsdl");
            HttpURLConnection http = null;

            if (url.getProtocol().toLowerCase().equals("https")) {
                trustAllHosts();
                HttpsURLConnection https = (HttpsURLConnection) url.openConnection();
                https.setHostnameVerifier(DO_NOT_VERIFY);
                http = https;
            } else {
                http = (HttpURLConnection) url.openConnection();
            }
            String SOAPAction="";
//            http.setRequestProperty("Content-Length", String.valueOf(b.length));
            http.setRequestProperty("Content-Type", "text/xml; charset=utf-8");
            http.setRequestProperty("SOAPAction", SOAPAction);
            http.setRequestMethod("GET");
            http.setDoOutput(true);
            http.setDoInput(true);
            OutputStream out = http.getOutputStream();
        }
        catch (Exception e)
        {
            e.printStackTrace();
        }
    }

    final static HostnameVerifier DO_NOT_VERIFY = new HostnameVerifier() {
        public boolean verify(String hostname, SSLSession session) {
            return true;
        }
    };

    private static void trustAllHosts() {
        // Create a trust manager that does not validate certificate chains
        TrustManager[] trustAllCerts = new TrustManager[] { new X509TrustManager() {
            public java.security.cert.X509Certificate[] getAcceptedIssuers() {
                return new java.security.cert.X509Certificate[] {};
            }

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

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

        // 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 (Exception e) {
            e.printStackTrace();
        }
    }
}

I'm getting the following exception:

javax.net.ssl.SSLHandshakeException: java.security.cert.CertificateException: Certificates does not conform to algorithm constraints
    at sun.security.ssl.Alerts.getSSLException(Alerts.java:192)
    at sun.security.ssl.SSLSocketImpl.fatal(SSLSocketImpl.java:1949)
    at sun.security.ssl.Handshaker.fatalSE(Handshaker.java:302)
    at sun.security.ssl.Handshaker.fatalSE(Handshaker.java:296)
    at sun.security.ssl.ClientHandshaker.serverCertificate(ClientHandshaker.java:1509)
    at sun.security.ssl.ClientHandshaker.processMessage(ClientHandshaker.java:216)
    at sun.security.ssl.Handshaker.processLoop(Handshaker.java:979)
    at sun.security.ssl.Handshaker.process_record(Handshaker.java:914)
    at sun.security.ssl.SSLSocketImpl.readRecord(SSLSocketImpl.java:1062)
    at sun.security.ssl.SSLSocketImpl.performInitialHandshake(SSLSocketImpl.java:1375)
    at sun.security.ssl.SSLSocketImpl.startHandshake(SSLSocketImpl.java:1403)
    at sun.security.ssl.SSLSocketImpl.startHandshake(SSLSocketImpl.java:1387)
    at sun.net.www.protocol.https.HttpsClient.afterConnect(HttpsClient.java:559)
    at sun.net.www.protocol.https.AbstractDelegateHttpsURLConnection.connect(AbstractDelegateHttpsURLConnection.java:185)
    at sun.net.www.protocol.http.HttpURLConnection.getOutputStream0(HttpURLConnection.java:1283)
    at sun.net.www.protocol.http.HttpURLConnection.getOutputStream(HttpURLConnection.java:1258)
    at sun.net.www.protocol.https.HttpsURLConnectionImpl.getOutputStream(HttpsURLConnectionImpl.java:250)
    at Main.main(Main.java:35)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke(Method.java:498)
    at com.intellij.rt.execution.application.AppMain.main(AppMain.java:147)
Caused by: java.security.cert.CertificateException: Certificates does not conform to algorithm constraints
    at sun.security.ssl.AbstractTrustManagerWrapper.checkAlgorithmConstraints(SSLContextImpl.java:1055)
    at sun.security.ssl.AbstractTrustManagerWrapper.checkAdditionalTrust(SSLContextImpl.java:981)
    at sun.security.ssl.AbstractTrustManagerWrapper.checkServerTrusted(SSLContextImpl.java:923)
    at sun.security.ssl.ClientHandshaker.serverCertificate(ClientHandshaker.java:1491)
    ... 18 more

Tried different solution from the google search, Non of them worked. I want to avoid using keytool because I will be running my tests on different vm.

Does anyone have any solution for this?

8
  • 1
    Try it with a higher java version. You might be using an outdated java version that doesn't support the encryption types used. Commented Mar 15, 2017 at 10:16
  • I am using JAVA 8 Commented Mar 15, 2017 at 10:17
  • You are doing the SOAP request on your own private server? On your test VM add the key to the trusted keys. For the public soap you don't need that since there are CA's that can vouch for the autenticty. Commented Mar 15, 2017 at 10:19
  • SOAP service is hosted on server that is using a self signed certificate. I am running my test from my local as well as any vm. I don't want to add the key to truststore in each vm. Is there any other way arround? Commented Mar 15, 2017 at 10:23
  • @PrasantaBiswas self-signed certificates aren't accepted without manual addition to the trust store, browsers(chrome,firefox,etc) don't accept these as well Commented Mar 15, 2017 at 10:26

5 Answers 5

31

Using X509ExtendedTrustManager instead of X509TrustManager() solved the problem. Here's the example:

public void trustAllHosts()
    {
        try
        {
            TrustManager[] trustAllCerts = new TrustManager[]{
                    new X509ExtendedTrustManager()
                    {
                        @Override
                        public java.security.cert.X509Certificate[] getAcceptedIssuers()
                        {
                            return null;
                        }

                        @Override
                        public void checkClientTrusted(java.security.cert.X509Certificate[] certs, String authType)
                        {
                        }

                        @Override
                        public void checkServerTrusted(java.security.cert.X509Certificate[] certs, String authType)
                        {
                        }

                        @Override
                        public void checkClientTrusted(java.security.cert.X509Certificate[] xcs, String string, Socket socket) throws CertificateException
                        {

                        }

                        @Override
                        public void checkServerTrusted(java.security.cert.X509Certificate[] xcs, String string, Socket socket) throws CertificateException
                        {

                        }

                        @Override
                        public void checkClientTrusted(java.security.cert.X509Certificate[] xcs, String string, SSLEngine ssle) throws CertificateException
                        {

                        }

                        @Override
                        public void checkServerTrusted(java.security.cert.X509Certificate[] xcs, String string, SSLEngine ssle) throws CertificateException
                        {

                        }

                    }
            };

            SSLContext sc = SSLContext.getInstance("SSL");
            sc.init(null, trustAllCerts, new java.security.SecureRandom());
            HttpsURLConnection.setDefaultSSLSocketFactory(sc.getSocketFactory());

            // Create all-trusting host name verifier
            HostnameVerifier allHostsValid = new  HostnameVerifier()
            {
                @Override
                public boolean verify(String hostname, SSLSession session)
                {
                    return true;
                }
            };
            // Install the all-trusting host verifier
            HttpsURLConnection.setDefaultHostnameVerifier(allHostsValid);
        }
        catch (Exception e)
        {
            log.error("Error occurred",e);
        }
    }
Sign up to request clarification or add additional context in comments.

9 Comments

This is a pretty unsafe approach as it undoes much of the purpose of https. A better approach is to use a TrustManager with a KeyStore containing the issuing CA certificate.
this is only for a test environment. not for production.
thanks ! I was looking for an easy solution like this for a while. Would like to know how to do it in spring boot as well ....
@R13mus what is your use case with springboot?
@AntonCavanaugh I didn't use HttpClient. I used Java's core APIs.
|
6

Edit : Understand the vulnerability this would cause before using it. This is by no means recommended for production use.

The best way is to create a dummy trustmanager that trusts everything.

 TrustManager[] dummyTrustManager = new TrustManager[] { new X509TrustManager() {
      public java.security.cert.X509Certificate[] getAcceptedIssuers() {
        return null;
      }

      public void checkClientTrusted(X509Certificate[] certs, String authType) {
      }

      public void checkServerTrusted(X509Certificate[] certs, String authType) {
      }
    } };

Then use the dummy trustmanager to initialize the SSL Context

SSLContext sc = SSLContext.getInstance("SSL");
    sc.init(null, dummyTrustManager, new java.security.SecureRandom());

Finally use the SSLContext to open connection

HttpsURLConnection.setDefaultSSLSocketFactory(sc.getSocketFactory());

    URL url = new URL("https://myhost:8913/myservice/service?wsdl");

This question has already been answered here in more detail Java: Overriding function to disable SSL certificate check

Update:

Above issue is due to certificate signature algorithm not being supported by Java. As per this post, later releases of Java 8 have disabled md5 algorithm.

To enable md5 support, locate java.security file under <jre_home>/lib/security and locate the line (535)

jdk.certpath.disabledAlgorithms=MD2, MD5, RSA keySize < 1024, 

and remove MD5

8 Comments

If you look at my code , I have already done that. Didn't work. It is working fine when the url is google.com
@PrasantaBiswas even though you bypass certificate validation, the connection still has to perform a SSL handkshake and encrypt the contents if you mean to use https. It seems the certificate is invalid. Can you download the certificate using broswer and share the certificate details, especially the signature algorithm
@PrasantaBiswas as a quickfix you can try updating the JCE policy jars for java 8, if not already done so. Do reply if this works for you
I have downloaded the certificate which is expired but still chrome can access the url. It uses MD5 RSA algorithm. Now how do I use this certificate to bypass ssl handshake?
@PrasantaBiswas see updated answer. FYI you cannot bypass ssl handshake, it is pre-requisite to establish the connection
|
5

Try with Apache HTTP client, this works for me.

SSLContextBuilder builder = new SSLContextBuilder();
builder.loadTrustMaterial(null, new TrustStrategy() {
     public boolean isTrusted(final X509Certificate[] chain, String authType) throws CertificateException {
          return true;
     }
});
SSLConnectionSocketFactory sslsf = new SSLConnectionSocketFactory(builder.build());

CloseableHttpClient httpclient = HttpClients.custom().setSSLSocketFactory(sslsf).build();

// GET or POST request with the client
...

2 Comments

I am using HttpURLConnection
Dereck, can you tell me what version of Apache HTTP client, there are so many versions many that wont work. Thanks.
2

Instead of using HttpsURLConnection.setDefaultSSLSocketFactory and your own implementation of TrustManager or X509ExtendedTrustManager, you can use TrustManagerFactory with a KeyStore with the certificate that issued the certificate you need to trust (for a self-signed certificate, this is the same as the host certificate) and call HttpsURLConnection.setSSLSocketFactory on the specific instance. This is both less code and avoids the security problems with trusting all HTTPS certicates.

In main:

            if (url.getProtocol().toLowerCase().equals("https")) {
                HttpsURLConnection https = (HttpsURLConnection) url.openConnection();
                https.setSSLSocketFactory(createSSLSocketFactory());
                http = https;
            }

The method createSSLSocketFactory looks like this:

    private static SSLSocketFactory createSSLSocketFactory() {
         File crtFile = new File("server.crt");
         Certificate certificate =          CertificateFactory.getInstance("X.509").generateCertificate(new FileInputStream(crtFile));

         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);

         return sslContext.getSocketFactory();
    }

Comments

0

Another way if using httpClient from Apache:

public static CloseableHttpClient getCloseableHttpClient()
{
    CloseableHttpClient httpClient = null;
    try {
        httpClient = HttpClients.custom().setSSLHostnameVerifier(NoopHostnameVerifier.INSTANCE)
                .setSSLContext(new SSLContextBuilder().loadTrustMaterial(null, new TrustStrategy()
                {
                    public boolean isTrusted(X509Certificate[] arg0, String arg1) throws CertificateException
                    {
                        return true;
                    }
                }).build()).build();

    } catch (KeyManagementException e) {
        LOGGER.error("KeyManagementException in creating http client instance", e);
    } catch (NoSuchAlgorithmException e) {
        LOGGER.error("NoSuchAlgorithmException in creating http client instance", e);
    } catch (KeyStoreException e) {
        LOGGER.error("KeyStoreException in creating http client instance", e);
    }
    return httpClient;
}

And then: HttpPost post = new HttpPost("your uri");

CloseableHttpResponse response = getCloseableHttpClient.execute(post);

And of course all the try-catches blocks (or use try with resources)

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.