40

Is there a way for the standard java SSL sockets to disable hostname verfication for ssl connections with a property? The only way I found until now, is to write a hostname verifier which returns true all the time.

Weblogic provides this possibility, it is possible to disable the hostname verification with the following property:

-Dweblogic.security.SSL.ignoreHostnameVerify

paweloque
  • 18,466
  • 26
  • 80
  • 136
  • 1
    Well, yours is pretty much the cleanest solution I can think of; is there something wrong with it? – Piskvor left the building May 17 '11 at 13:22
  • 1
    well, you just want to disable the check and do this without code changes. Normally you have plenty of properties to control ssl connections, but apparently not in this case... – paweloque May 17 '11 at 13:28
  • well, you *could* make a hostname verifier *factory* which would check your custom property and return the "always-ok" dummy verifier if it's set, or the default verifier if not; however, this doesn't really solve the issue, does it? – Piskvor left the building May 17 '11 at 13:54
  • you're right, this doesn't solve my problem. In fact I'm not using sockets directly, but the wls webservice framework where it seems I'm not in drivers seat when it comes to socket factory config and especially setting my own hostname verifier. – paweloque May 17 '11 at 14:39

6 Answers6

37

It should be possible to create custom java agent that overrides default HostnameVerifier:

import javax.net.ssl.*;
import java.lang.instrument.Instrumentation;

public class LenientHostnameVerifierAgent {
    public static void premain(String args, Instrumentation inst) {
        HttpsURLConnection.setDefaultHostnameVerifier(new HostnameVerifier() {
            public boolean verify(String s, SSLSession sslSession) {
                return true;
            }
        });
    }
}

Then just add -javaagent:LenientHostnameVerifierAgent.jar to program's java startup arguments.

Benedikt Köppel
  • 4,853
  • 4
  • 32
  • 42
Vadzim
  • 24,954
  • 11
  • 143
  • 151
  • 2
    This might be a great way to handle it, but I'm dealing with Apache's http client. How could I create a javaagent to set `SSLSocketFactory.setHostnameVerifier(new AllowAllHostnameVerifier())`? – end-user Jan 13 '16 at 19:16
  • 1
    @end-user, just the same way as in this answer. – Vadzim Jan 13 '16 at 22:08
  • 2
    Remember to add a manifest with 'Premain-Class: LenientHostnameVerifierAgent' – thoredge Mar 17 '22 at 08:42
12

The answer from @Nani doesn't work anymore with Java 1.8u181. You still need to use your own TrustManager, but it needs to be a X509ExtendedTrustManager instead of a X509TrustManager:

import java.io.IOException;
import java.net.HttpURLConnection;
import java.net.Socket;
import java.net.URL;
import java.security.KeyManagementException;
import java.security.NoSuchAlgorithmException;
import java.security.cert.X509Certificate;

import javax.net.ssl.HttpsURLConnection;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLEngine;
import javax.net.ssl.SSLHandshakeException;
import javax.net.ssl.TrustManager;
import javax.net.ssl.X509ExtendedTrustManager;

public class Test {

   public static void main (String [] args) throws IOException {
      // This URL has a certificate with a wrong name
      URL url = new URL ("https://wrong.host.badssl.com/");

      try {
         // opening a connection will fail
         url.openConnection ().connect ();
      } catch (SSLHandshakeException e) {
         System.out.println ("Couldn't open connection: " + e.getMessage ());
      }

      // Bypassing the SSL verification to execute our code successfully
      disableSSLVerification ();

      // now we can open the connection
      url.openConnection ().connect ();

      System.out.println ("successfully opened connection to " + url + ": " + ((HttpURLConnection) url.openConnection ()).getResponseCode ());
   }

   // Method used for bypassing SSL verification
   public static void disableSSLVerification () {

      TrustManager [] trustAllCerts = new TrustManager [] {new X509ExtendedTrustManager () {
         @Override
         public void checkClientTrusted (X509Certificate [] chain, String authType, Socket socket) {

         }

         @Override
         public void checkServerTrusted (X509Certificate [] chain, String authType, Socket socket) {

         }

         @Override
         public void checkClientTrusted (X509Certificate [] chain, String authType, SSLEngine engine) {

         }

         @Override
         public void checkServerTrusted (X509Certificate [] chain, String authType, SSLEngine engine) {

         }

         @Override
         public java.security.cert.X509Certificate [] getAcceptedIssuers () {
            return null;
         }

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

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

      }};

      SSLContext sc = null;
      try {
         sc = SSLContext.getInstance ("SSL");
         sc.init (null, trustAllCerts, new java.security.SecureRandom ());
      } catch (KeyManagementException | NoSuchAlgorithmException e) {
         e.printStackTrace ();
      }
      HttpsURLConnection.setDefaultSSLSocketFactory (sc.getSocketFactory ());
   }
}
Markus Heberling
  • 835
  • 13
  • 18
  • Is it still required to add a startup parameter? – kiltek Nov 01 '19 at 14:16
  • When you use the solution I posted, you don't need any extra startup parameter. You just need to make sure the socket factory is set in your program. – Markus Heberling Nov 25 '19 at 16:37
  • 2
    **Warning**: The solution in this answer seems to disable all trust verification without so much of a warning in the post (if you exclude the `trustAllCerts ` in the code). – Maarten Bodewes Jul 25 '22 at 08:03
8

There is no hostname verification in standard Java SSL sockets or indeed SSL, so that's why you can't set it at that level. Hostname verification is part of HTTPS (RFC 2818): that's why it manifests itself as javax.net.ssl.HostnameVerifier, which is applied to an HttpsURLConnection.

user207421
  • 305,947
  • 44
  • 307
  • 483
  • 1
    That sounds reasonable, however, why does weblogic provide such a switch and why is there no such property on the HttpsURLConnection level? – paweloque May 18 '11 at 07:10
  • 1
    @lewap I assume these are questions are rhetorical? I can't answer questions about WebLogic, or about why the JDK is the way it is. The WebLogic thing looks like a security hole to me, I wouldn't want it in the JDK. – user207421 May 18 '11 at 08:17
  • 5
    they are not at all rhetorical, I'm trying to understand how ssl works and to understand the differences between jdk & weblogic. Maybe there is a reason for that. – paweloque May 18 '11 at 09:16
  • @lewap Of course there's a reason for it. You're just not asking the right person, or in the right place. – user207421 May 18 '11 at 09:35
  • This will provide more context to the hostname verification issue, which is related to the jdk implementation of the SSL: https://tersesystems.com/blog/2014/03/23/fixing-hostname-verification/ – razvanone Jun 13 '23 at 10:16
5

I also had the same problem while accessing RESTful web services. And I their with the below code to overcome the issue:

public class Test {
    //Bypassing the SSL verification to execute our code successfully 
    static {
        disableSSLVerification();
    }

    public static void main(String[] args) {    
        //Access HTTPS URL and do something    
    }
    //Method used for bypassing SSL verification
    public static void disableSSLVerification() {

        TrustManager[] trustAllCerts = 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) {
            }

        } };

        SSLContext sc = null;
        try {
            sc = SSLContext.getInstance("SSL");
            sc.init(null, trustAllCerts, new java.security.SecureRandom());
        } catch (KeyManagementException e) {
            e.printStackTrace();
        } catch (NoSuchAlgorithmException e) {
            e.printStackTrace();
        }
        HttpsURLConnection.setDefaultSSLSocketFactory(sc.getSocketFactory());

        HostnameVerifier allHostsValid = new HostnameVerifier() {
            public boolean verify(String hostname, SSLSession session) {
                return true;
            }
        };      
        HttpsURLConnection.setDefaultHostnameVerifier(allHostsValid);           
    }
}

It worked for me. try it!!

Nani
  • 1,148
  • 3
  • 20
  • 35
  • 11
    This insecurely trusts all certificates, but it does not bypass hostname verification, so it doesn't answer the question. – user207421 Dec 22 '16 at 22:57
  • 1
    Disregard the comment, it bypasses hostnameverifier (see HttpsURLConnection.setDefaultHostnameVerifier(allHostsValid); ) – Frischling Jul 13 '21 at 13:17
2

@user207421 is right, there is no hostname verification in standard Java SSL sockets or indeed SSL.
But X509ExtendedTrustManager implement the host name check logic(see it's javadoc). To disable this, We can set SSLParameters .endpointIdentificationAlgorithm to null as JDK AbstractAsyncSSLConnection did:

        if (!disableHostnameVerification)
            sslParameters.setEndpointIdentificationAlgorithm("HTTPS"); // default is null

disableHostnameVerification is read from property: jdk.internal.httpclient.disableHostnameVerification。

How to modify SSLParameters Object is dependends on the specify soft you use。

as spring webflux WebClient:

HttpClient httpClient = HttpClient.create()
    .secure(sslContextSpec ->
        sslContextSpec
            .sslContext(sslContext)
            .handlerConfigurator(sslHandler -> {
                SSLEngine engine = sslHandler.engine();
                SSLParameters newSslParameters = engine.getSSLParameters(); // 返回的是一个新对象
                // 参考:https://github.com/AdoptOpenJDK/openjdk-jdk11/blob/master/src/java.net.http/share/classes/jdk/internal/net/http/AbstractAsyncSSLConnection.java#L116
                newSslParameters.setEndpointIdentificationAlgorithm(null);
                engine.setSSLParameters(newSslParameters);
            })
    )
    
WebClient webclient = WebClient.builder()
                .clientConnector(new ReactorClientHttpConnector(httpClient))
                .build();
copbint
  • 79
  • 6
1

In case you're using apache's http-client 4:

SSLConnectionSocketFactory sslConnectionSocketFactory = 
    new SSLConnectionSocketFactory(sslContext,
             new String[] { "TLSv1.2" }, null, new HostnameVerifier() {
                    public boolean verify(String arg0, SSLSession arg1) {
                            return true;
            }
      });