4

I am writing a simple REST api client based on JDK11 HttpClient, simple code as below:

public class MyClass {

    private static final X509TrustManager TRUST_MANAGER = new X509TrustManager() {
        public void checkClientTrusted(X509Certificate[] xcs, String string) {
        }

        public void checkServerTrusted(X509Certificate[] xcs, String string) {
        }

        public X509Certificate[] getAcceptedIssuers() {
            return null;
        }
    };

    private static HttpClient getNewHttpClient() {
        int timeout = 600;
        try {

            HttpsURLConnection.setDefaultHostnameVerifier((hostname, session) -> true);

            SSLContext sslContext = SSLContext.getInstance("TLSv1.2");
            sslContext.init(null, new TrustManager[]{TRUST_MANAGER}, new SecureRandom());

            // Install the all-trusting host verifier
            HttpsURLConnection.setDefaultSSLSocketFactory(sslContext.getSocketFactory());

            //Set SSL parameters
            SSLParameters parameters = new SSLParameters();
            parameters.setEndpointIdentificationAlgorithm("HTTPS");
                HttpClient httpClient = HttpClient.newBuilder()
                    .connectTimeout(Duration.ofMillis(timeout * 1000))
                    .sslContext(sslContext)
                    .sslParameters(parameters)
                    .build();
            return httpClient;
        } catch (Exception e) {
            logger.warn("Unable to create HttpClient with disabled SSL Certificate verifying, default client will be used", e);
            return HttpClient.newHttpClient();
        }
    }

    public static void main(String[] args) {
        HttpRequest requestBuilder = HttpRequest.newBuilder()
                .uri(URI.create("https://somehostname.xx.xxx.net"))
                .GET()
                .build();

        getNewHttpClient().send(request, HttpResponse.BodyHandlers.ofString());
    }
}

Problem is that when I try to open some SSL domain I get an error:

Caused by: java.security.cert.CertificateException: No subject alternative DNS name matching somehostname.xx.xxx.net found.

How can I solve this problem?

Naman
  • 27,789
  • 26
  • 218
  • 353
Andrey
  • 41
  • 1
  • 2
  • looked into https://stackoverflow.com/questions/19540289/how-to-fix-the-java-security-cert-certificateexception-no-subject-alternative ? – Naman Oct 17 '18 at 16:44
  • Yes. But it doesn't help me. As you can see I've tried disable verification by adding HttpsURLConnection.setDefaultHostnameVerifier((hostname, session) -> true); but it doesn't have influence on HttpClient from JDK11. – Andrey Oct 18 '18 at 05:17
  • @Andrey Do you face the same issue using JDK < 11? Can you provide a public hostname to test on? – Mikhail Kholodkov Oct 18 '18 at 13:39
  • Disabling the hostname verification is never a good idea - it makes your connection insecure. What happens if you enter the URL in a web browser? Does the browser accept the server or does it show an error? If it shows an error it is a server side/server certificate problem and not a client/Java problem. – Robert Oct 20 '18 at 10:29
  • 3
    @Andrey, What is the particular use-case? Why does the hostname, of the server that you are connecting to, not match the CN or SAN in its certificate? If this is just a testing environment, then hostname verification can be disabled using _-Djdk.internal.httpclient.disableHostnameVerification_ at your own risk. – chegar999 Oct 23 '18 at 15:40
  • 2
    programatically disable hostname verification before instantiating httpclient `// PREVENTS HOST VALIDATION` `final Properties props = System.getProperties();` `props.setProperty("jdk.internal.httpclient.disableHostnameVerification", Boolean.TRUE.toString());` – liltitus27 Nov 16 '18 at 15:23
  • @liltitus27 and now imagine you have two clients - one needs that property disabled and one needs that enabled. :( – Eugene Aug 18 '20 at 14:24

1 Answers1

2

While turning hostname verification off completly (i.e. for the whole VM) may be an option it may be dangerous. You can also disable hostname verification if you derive your TRUST_MANAGER from javax.net.ssl.X509ExtendedTrustManager.

    private static final TrustManager DUMMY_TRUST_MANAGER = new X509ExtendedTrustManager() {        
    @Override
    public java.security.cert.X509Certificate[] getAcceptedIssuers() {
        return new java.security.cert.X509Certificate[0];
    }
    @Override
    public void checkServerTrusted(java.security.cert.X509Certificate[] chain, String authType) throws CertificateException { }
    @Override
    public void checkClientTrusted(java.security.cert.X509Certificate[] chain, String authType) throws CertificateException { }
    @Override
    public void checkServerTrusted(X509Certificate[] chain, String authType, SSLEngine engine) throws CertificateException { }
    @Override
    public void checkServerTrusted(X509Certificate[] chain, String authType, Socket socket) throws CertificateException { }
    @Override
    public void checkClientTrusted(X509Certificate[] chain, String authType, SSLEngine engine) throws CertificateException { }
    @Override
    public void checkClientTrusted(X509Certificate[] chain, String authType, Socket socket) throws CertificateException { }
};

Of course this will also completely disable any checks of the certificates presented to your client.

There is also an open Issue at https://bugs.openjdk.java.net/browse/JDK-8213309 to add API to the JDK to specifically turn off hostname verification. If you have access too the openjdk bug database you may vote for the issue.

rli
  • 1,745
  • 1
  • 14
  • 25
  • This is working for me. Can you explain this function a little, please? By the way, thank you for this solution! – clauub Jun 25 '21 at 11:16
  • OP had already posted his attempt to solve the problem by using a custom X509TrustManager. However this will not work for the "Hostname validation" part of TLS connection checks. If however you derive your trust manager from X509ExtendedTrustManager (which is a subclass of X509TrustManager ...) then the hostname validation can also be done from "your" code instead of the built-in checks in the JDK. – rli Nov 03 '21 at 07:17