133

I am having two Spring-based web apps A and B, on two different machines.

I want to make an HTTPS call from web app A to web app B, however, I am using a self-signed certificate in Machine B. So my HTTPS request fails.

How can I disable HTTPS certificate validation when using RestTemplate in Spring? I want to disable validation because both web app A and B are within the internal network, but data transfer has to happen over HTTPS

vasadia
  • 366
  • 5
  • 8
Prabhu R
  • 13,836
  • 21
  • 78
  • 112

15 Answers15

111
@Bean
public RestTemplate restTemplate() 
                throws KeyStoreException, NoSuchAlgorithmException, KeyManagementException {
    TrustStrategy acceptingTrustStrategy = (X509Certificate[] chain, String authType) -> true;

    SSLContext sslContext = org.apache.http.ssl.SSLContexts.custom()
                    .loadTrustMaterial(null, acceptingTrustStrategy)
                    .build();

    SSLConnectionSocketFactory csf = new SSLConnectionSocketFactory(sslContext);

    CloseableHttpClient httpClient = HttpClients.custom()
                    .setSSLSocketFactory(csf)
                    .build();

    HttpComponentsClientHttpRequestFactory requestFactory =
                    new HttpComponentsClientHttpRequestFactory();

    requestFactory.setHttpClient(httpClient);
    RestTemplate restTemplate = new RestTemplate(requestFactory);
    return restTemplate;
 }
Raibaz
  • 9,280
  • 10
  • 44
  • 65
DanieleDM
  • 1,762
  • 2
  • 16
  • 13
  • 2
    import Apache HttpClient in your project (>4.4): 'org.apache.httpcomponents:httpclient:4.5.13' – bucky Apr 22 '22 at 07:04
  • I tried this solution, but I still get `Caused by: javax.net.ssl.SSLHandshakeException: sun.security.validator.ValidatorException: PKIX path building failed: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target` – Tobia Feb 01 '23 at 09:17
85

Essentially two things you need to do are use a custom TrustStrategy that trusts all certs, and also use NoopHostnameVerifier() to disable hostname verification. Here is the code, with all the relevant imports:

import java.security.KeyManagementException;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;
import javax.net.ssl.SSLContext;
import org.apache.http.conn.ssl.NoopHostnameVerifier;
import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
import org.apache.http.conn.ssl.TrustStrategy;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.springframework.http.client.HttpComponentsClientHttpRequestFactory;
import org.springframework.web.client.RestTemplate;

public RestTemplate getRestTemplate() throws KeyStoreException, NoSuchAlgorithmException, KeyManagementException {
    TrustStrategy acceptingTrustStrategy = (x509Certificates, s) -> true;
    SSLContext sslContext = org.apache.http.ssl.SSLContexts.custom().loadTrustMaterial(null, acceptingTrustStrategy).build();
    SSLConnectionSocketFactory csf = new SSLConnectionSocketFactory(sslContext, new NoopHostnameVerifier());
    CloseableHttpClient httpClient = HttpClients.custom().setSSLSocketFactory(csf).build();
    HttpComponentsClientHttpRequestFactory requestFactory = new HttpComponentsClientHttpRequestFactory();
    requestFactory.setHttpClient(httpClient);
    RestTemplate restTemplate = new RestTemplate(requestFactory);
    return restTemplate;
}
Community
  • 1
  • 1
rouble
  • 16,364
  • 16
  • 107
  • 102
  • 6
    TrustStrategy acceptingTrustStrategy = (X509Certificate[] x509Certificates, String s) ->true; – Humoyun Ahmad Nov 15 '17 at 07:05
  • 2
    Thank you, this saved my life. All other methods (adding exported .crt to java cacerts using keytool, http://blog.codeleak.pl/2016/02/skip-ssl-certificate-verification-in.html, and about 5~6 other stackoverflow posts including answers on this one) failed, took 7 hours of my time. This answer was my last hope, thanks. – Gyuhyeon Lee Jan 27 '18 at 12:20
  • 2
    The `NoopHostnameVerifier` is very important - other answer neglect this. One use case is using your public SSL cert via loopback to `localhost`. – jocull Sep 05 '18 at 17:08
  • NoopHostnameVerifier is not available in httpclient-android:4.3.5.1, and if I use httpclient:4.5.6 I get this error: No static field INSTANCE of type Lorg/apache/http/conn/ssl/AllowAllHostnameVerifier. Could anyone help me, please? – Celtik Nov 11 '18 at 20:19
  • 2
    Please mention the Apache HTTP client library name and version that supports this solution, the original question asks about Spring REST Template which AFAICT does not automatically bring in that library, thanks. – chrisinmtown Oct 08 '19 at 13:51
  • 4
    This works with 4.5.13 (https://mvnrepository.com/artifact/org.apache.httpcomponents/httpclient/4.5.13) – jaletechs Nov 26 '20 at 07:48
  • 1
    Thanks a lot, this is what I needed! The NoopHostnameVerifier was what helped me for testing locally using different domain names all mapped to localhost. – Christoph Mar 17 '21 at 15:32
44

What you need to add is a custom HostnameVerifier class bypasses certificate verification and returns true

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

This needs to be placed appropriately in your code.

moffeltje
  • 4,521
  • 4
  • 33
  • 57
Raghuram
  • 51,854
  • 11
  • 110
  • 122
  • 7
    I am using Spring's RestTemplate class, do you know how I could do that in RestTemplate? – Prabhu R Nov 02 '10 at 10:43
  • 8
    Actually this is outside of RestTemplate. Some sample code available at http://forum.springsource.org/showthread.php?t=60854. Also look at http://stackoverflow.com/questions/1725863/ssl-handshake-issue-using-spring-resttemplate – Raghuram Nov 02 '10 at 11:49
  • 31
    Where is appropriate place for that can you explain it? – kamaci Nov 11 '11 at 09:35
  • 10
    This helped, thanks. By the way you can simplify this to lambda expressions like this `HttpsURLConnection.setDefaultHostnameVerifier((hostname, session) -> true);` – saidfagan Oct 15 '18 at 06:37
  • 1
    As far as "appropriate space". Since this is a static method you can put it in some initialization code. For example I put it into my class that `extends WebSecurityConfigurerAdapter` inside the `configure` method – Bostone Jul 16 '21 at 23:30
17

Another way to do that trick very simple without import any APACHE or Any unknow package.

import javax.net.ssl.HttpsURLConnection;
import javax.net.ssl.SSLContext;
import javax.net.ssl.TrustManager;
import javax.net.ssl.X509TrustManager;

    private void ignoreCertificates() {
    TrustManager[] trustAllCerts = new TrustManager[] { new X509TrustManager() {
        @Override
        public X509Certificate[] getAcceptedIssuers() {
            return null;
        }

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

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

    try {
        SSLContext sc = SSLContext.getInstance("TLS");
        sc.init(null, trustAllCerts, new SecureRandom());
        HttpsURLConnection.setDefaultSSLSocketFactory(sc.getSocketFactory());
    } catch (Exception e) {
     

    }

and set ignoreCertificates() before RestTemplate:

   ignoreCertificates();
   RestTemplate restTemplate = new RestTemplate();
Sérgio Thiago Mendonça
  • 1,161
  • 2
  • 13
  • 23
16

I found a simple way

    TrustStrategy acceptingTrustStrategy = (X509Certificate[] chain, String authType) -> true;
    SSLContext sslContext = org.apache.http.ssl.SSLContexts.custom().loadTrustMaterial(null, acceptingTrustStrategy).build();
    SSLConnectionSocketFactory csf = new SSLConnectionSocketFactory(sslContext);
    CloseableHttpClient httpClient = HttpClients.custom().setSSLSocketFactory(csf).build();
    HttpComponentsClientHttpRequestFactory requestFactory = new HttpComponentsClientHttpRequestFactory();
    requestFactory.setHttpClient(httpClient);

    RestTemplate restTemplate = new RestTemplate(requestFactory);

Imports used

import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
import org.apache.http.conn.ssl.TrustStrategy;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.springframework.http.client.HttpComponentsClientHttpRequestFactory;
import org.springframework.web.client.RestTemplate;
import javax.net.ssl.SSLContext;
import java.security.KeyManagementException;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
import java.security.cert.X509Certificate;
Yash Jagdale
  • 1,446
  • 14
  • 17
11

Add my response with cookie :

public static void main(String[] args) {
     MultiValueMap<String, String> params = new LinkedMultiValueMap<>();
     params.add("username", testUser);
     params.add("password", testPass);
     NullHostnameVerifier verifier = new NullHostnameVerifier(); 
     MySimpleClientHttpRequestFactory requestFactory = new MySimpleClientHttpRequestFactory(verifier , rememberMeCookie);
     ResponseEntity<String> response = restTemplate.postForEntity(appUrl + "/login", params, String.class);

     HttpHeaders headers = response.getHeaders();
     String cookieResponse = headers.getFirst("Set-Cookie");
     String[] cookieParts = cookieResponse.split(";");
     rememberMeCookie = cookieParts[0];
     cookie.setCookie(rememberMeCookie);

     requestFactory = new  MySimpleClientHttpRequestFactory(verifier,cookie.getCookie());
          restTemplate.setRequestFactory(requestFactory);
}


public class MySimpleClientHttpRequestFactory extends SimpleClientHttpRequestFactory {

        private final HostnameVerifier verifier;
        private final String cookie;

        public MySimpleClientHttpRequestFactory(HostnameVerifier verifier ,String cookie) {
            this.verifier = verifier;
            this.cookie = cookie;
        }

        @Override
        protected void prepareConnection(HttpURLConnection connection, String httpMethod) throws IOException {
            if (connection instanceof HttpsURLConnection) {
                ((HttpsURLConnection) connection).setHostnameVerifier(verifier);
                ((HttpsURLConnection) connection).setSSLSocketFactory(trustSelfSignedSSL().getSocketFactory());
                ((HttpsURLConnection) connection).setAllowUserInteraction(true);
                String rememberMeCookie = cookie == null ? "" : cookie; 
                ((HttpsURLConnection) connection).setRequestProperty("Cookie", rememberMeCookie);
            }
            super.prepareConnection(connection, httpMethod);
        }

        public SSLContext trustSelfSignedSSL() {
            try {
                SSLContext ctx = SSLContext.getInstance("TLS");
                X509TrustManager tm = new X509TrustManager() {

                    public void checkClientTrusted(X509Certificate[] xcs, String string) throws CertificateException {
                    }

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

                    public X509Certificate[] getAcceptedIssuers() {
                        return null;
                    }
                };
                ctx.init(null, new TrustManager[] { tm }, null);
                SSLContext.setDefault(ctx);
                return ctx;
            } catch (Exception ex) {
                ex.printStackTrace();
            }
            return null;
        }

    }


    public class NullHostnameVerifier implements HostnameVerifier {
           public boolean verify(String hostname, SSLSession session) {
              return true;
           }
        }
Renats Stozkovs
  • 2,549
  • 10
  • 22
  • 26
Ran Adler
  • 3,587
  • 30
  • 27
7

You can use this with HTTPClient API.

public RestTemplate getRestTemplateBypassingHostNameVerifcation() {
    CloseableHttpClient httpClient = HttpClients.custom().setSSLHostnameVerifier(new NoopHostnameVerifier()).build();
    HttpComponentsClientHttpRequestFactory requestFactory = new HttpComponentsClientHttpRequestFactory();
    requestFactory.setHttpClient(httpClient);
    return new RestTemplate(requestFactory);

}
Amit Parashar
  • 1,447
  • 12
  • 15
  • 5
    This fails with "sun.security.validator.ValidatorException: PKIX path building failed...", but it was asked to skip certificate check as well – socona Jul 31 '19 at 13:48
  • 1
    @socona Thanks for pointing out. This is simply turning off hostname verification, as mentioned in the method name. – Amit Parashar Aug 02 '19 at 10:37
7

Complete code to disable SSL hostname verifier,

RestTemplate restTemplate = new RestTemplate();
//to disable ssl hostname verifier
restTemplate.setRequestFactory(new SimpleClientHttpRequestFactory() {
   @Override
    protected void prepareConnection(HttpURLConnection connection, String httpMethod) throws IOException {
        if (connection instanceof HttpsURLConnection) {
            ((HttpsURLConnection) connection).setHostnameVerifier(new NoopHostnameVerifier());
        }
        super.prepareConnection(connection, httpMethod);
    }
});
user1986251
  • 71
  • 1
  • 1
  • 2
    The dependency on (Apache) `NoopHostnameVerifier` shouldn't be necessary. Just provide a (simple) custom implementation of [`HostnameVerifier`](https://docs.oracle.com/javase/6/docs/api/javax/net/ssl/HostnameVerifier.html) of as shown in other answers. This can even be a [lambda](https://docs.oracle.com/javase/8/docs/api/java/lang/FunctionalInterface.html) like: `(hostname, session) -> true`. – Brent Bradburn Apr 29 '20 at 22:06
  • how to write the junit for this ? – Mateen Nov 15 '21 at 03:25
3

To overrule the default strategy you can create a simple method in the class where you are wired your restTemplate:

 protected void acceptEveryCertificate() throws KeyStoreException, NoSuchAlgorithmException, KeyManagementException {

    TrustStrategy acceptingTrustStrategy = new TrustStrategy() {
        @Override
        public boolean isTrusted(X509Certificate[] x509Certificates, String s) throws CertificateException {
            return true;
        }
    };

    restTemplate.setRequestFactory(new HttpComponentsClientHttpRequestFactory(
            HttpClientBuilder
                    .create()
                    .setSSLContext(SSLContexts.custom().loadTrustMaterial(null, acceptingTrustStrategy).build())
                    .build()));
}

Note: Surely you need to handle exceptions since this method only throws them further!

sagaris
  • 31
  • 1
  • 2
    this worked for me with SB 2.2.4 in connection with a `RestTemplateBuilder` within a `@Configuration`. Plus: - instead of the nested class, i used the `TrustAllStrategy` – elonderin Mar 26 '20 at 07:14
3

This problem is about SSL connection. When you try to connect to some resource https protocol requires to create secured connection. That means only your browser and website server know what data is being sent in requests bodies. This security is achieved by ssl certificates that stored on website and are being downloaded by your browser (or any other client, Spring RestTemplate with Apache Http Client behind in our case) with first connection to host. There are RSA256 encryption and many other cool things around. But in the end of a day: In case certificate is not registered or is invalid you will see certificate error (HTTPS connection is not secure). To fix certificate error website provider need to buy it for particular website or fix somehow e.g. https://www.register.com/ssl-certificates

Right way how problem can be solved

  • Register SSL certificate

Not a right way how problem can be solved

  • download broken SSL certificate from website

  • import SSL certificate to Java cacerts (certificate storage)

    keytool -importcert -trustcacerts -noprompt -storepass changeit -alias name -keystore "C:\Program Files\Java\jdk-11.0.2\lib\security\cacerts" -file file.cer

Dirty (Insecure) way how problem can be solved

  • make RestTemplate to ignore SSL verification

      @Bean
      public RestTemplateBuilder restTemplateBuilder(@Autowired SSLContext sslContext) {
          return new RestTemplateBuilder() {
              @Override
              public ClientHttpRequestFactory buildRequestFactory() {
                  return new HttpComponentsClientHttpRequestFactory(
                          HttpClients.custom().setSSLSocketFactory(
                                  new SSLConnectionSocketFactory(sslContext
                                          , NoopHostnameVerifier.INSTANCE)).build());
              }
          };
      }
    
      @Bean
          public SSLContext insecureSslContext() throws KeyStoreException, NoSuchAlgorithmException, KeyManagementException {
             return SSLContexts.custom()
                      .loadTrustMaterial(null, (x509Certificates, s) -> true)
                      .build();
          }
    
Oleg Maksymuk
  • 441
  • 4
  • 10
  • 2
    While this code may provide a solution to problem, it is highly recommended that you provide additional context regarding why and/or how this code answers the question. Code only answers typically become useless in the long-run because future viewers experiencing similar problems cannot understand the reasoning behind the solution. – palaѕн May 09 '20 at 05:53
  • Thank you for taking time to explain things ! – Abhi.P Jan 23 '22 at 00:15
1

Security: disable https/TLS certificate hostname check,the following code worked in spring boot rest template

*HttpsURLConnection.setDefaultHostnameVerifier(
        //SSLConnectionSocketFactory.ALLOW_ALL_HOSTNAME_VERIFIER
        // * @deprecated (4.4) Use {@link org.apache.http.conn.ssl.NoopHostnameVerifier}
        new NoopHostnameVerifier()
);*
Sats
  • 115
  • 1
  • 3
  • 13
1

Java code example for HttpClient > 4.3

package com.example.teocodownloader;

import org.apache.http.conn.ssl.NoopHostnameVerifier;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.springframework.http.client.HttpComponentsClientHttpRequestFactory;
import org.springframework.web.client.RestTemplate;

public class Example {
    public static void main(String[] args) {
        CloseableHttpClient httpClient
                = HttpClients.custom()
                .setSSLHostnameVerifier(new NoopHostnameVerifier())
                .build();
        HttpComponentsClientHttpRequestFactory requestFactory
                = new HttpComponentsClientHttpRequestFactory();
        requestFactory.setHttpClient(httpClient);
        RestTemplate restTemplate = new RestTemplate(requestFactory);
    }
}

By the way, don't forget to add the following dependencies to the pom file:

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-security</artifactId>
</dependency>
<dependency>
    <groupId>org.apache.httpcomponents</groupId>
    <artifactId>httpclient</artifactId>
</dependency>

You could find Java code example for HttpClient < 4.3 as well.

Maks
  • 2,808
  • 4
  • 30
  • 31
1
@Bean
public RestTemplate restTemplate() throws Exception {
    SSLContext sslContext = new SSLContextBuilder()
            .loadKeyMaterial(keyStore, keyStorePassword.toCharArray(), keyStorePassword.toCharArray())
            .loadTrustMaterial(trustStore, trustStorePassword.toCharArray(), (cert, authType) -> sslTrustStrategy)
            .build();
    HostnameVerifier hostnameVerifier = sslTrustStrategy ? new NoopHostnameVerifier() :
            SSLConnectionSocketFactory.getDefaultHostnameVerifier();
    SSLConnectionSocketFactory sslSocketFactory = new SSLConnectionSocketFactory(sslContext, hostnameVerifier);
    HttpClient httpClient = HttpClients.custom().setSSLSocketFactory(sslSocketFactory).build();
    HttpComponentsClientHttpRequestFactory httpComponentsHttpClientFactory =
            new HttpComponentsClientHttpRequestFactory(httpClient);
    RestTemplate restTemplate = new RestTemplate(httpComponentsHttpClientFactory);
    return restTemplate;
}

If sslTrustStrategy = true,

  1. trust all certificates due to (cert, authType) -> sslTrustStrategy
  2. Dont just trust hosts in the certificate but trust all (due to NoopHostnameVerifier, else trust only the hosts in the cerificate)
abhihello123
  • 1,668
  • 1
  • 22
  • 38
0

If you are using rest template, you can use this piece of code

    fun getClientHttpRequestFactory(): ClientHttpRequestFactory {
        val timeout = envTimeout.toInt()
        val config = RequestConfig.custom()
            .setConnectTimeout(timeout)
            .setConnectionRequestTimeout(timeout)
            .setSocketTimeout(timeout)
            .build()

        val acceptingTrustStrategy = TrustStrategy { chain: Array<X509Certificate?>?, authType: String? -> true }

        val sslContext: SSLContext = SSLContexts.custom()
            .loadTrustMaterial(null, acceptingTrustStrategy)
            .build()

        val csf = SSLConnectionSocketFactory(sslContext)

        val client = HttpClientBuilder
            .create()
            .setDefaultRequestConfig(config)
            .setSSLSocketFactory(csf)
            .setSSLHostnameVerifier(NoopHostnameVerifier.INSTANCE)
            .build()
        return HttpComponentsClientHttpRequestFactory(client)
    }

    @Bean
    fun getRestTemplate(): RestTemplate {
        return RestTemplate(getClientHttpRequestFactory())
    }
0

Here is the answer for Spring 3.1.2

Dependency

<dependency>
    <groupId>org.apache.httpcomponents.client5</groupId>
    <artifactId>httpclient5</artifactId>
</dependency>

Code

@Bean
public RestTemplate getRestTemplate() throws NoSuchAlgorithmException, KeyManagementException, KeyStoreException {
    TrustStrategy acceptingTrustStrategy = (x509Certificates, s) -> true;
    SSLContext sslContext = SSLContexts.custom().loadTrustMaterial(null, acceptingTrustStrategy).build();
    SSLConnectionSocketFactory csf = new SSLConnectionSocketFactory(sslContext, new NoopHostnameVerifier());
    HttpComponentsClientHttpRequestFactory requestFactory = new HttpComponentsClientHttpRequestFactory();
    HttpClientConnectionManager connectionManager = PoolingHttpClientConnectionManagerBuilder.create().setSSLSocketFactory(csf).build();
    CloseableHttpClient httpClient = HttpClients.custom().setConnectionManager(connectionManager).build();
    requestFactory.setHttpClient(httpClient);
    return new RestTemplate(requestFactory);
}

Imports

import org.apache.hc.client5.http.impl.classic.CloseableHttpClient;
import org.apache.hc.client5.http.impl.classic.HttpClients;
import org.apache.hc.client5.http.impl.io.PoolingHttpClientConnectionManagerBuilder;
import org.apache.hc.client5.http.io.HttpClientConnectionManager;
import org.apache.hc.client5.http.ssl.NoopHostnameVerifier;
import org.apache.hc.client5.http.ssl.SSLConnectionSocketFactory;
import org.apache.hc.core5.ssl.SSLContexts;
import org.apache.hc.core5.ssl.TrustStrategy;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Bean;
import org.springframework.http.client.HttpComponentsClientHttpRequestFactory;
import org.springframework.web.client.RestTemplate;
import org.springframework.web.util.DefaultUriBuilderFactory;

import javax.net.ssl.SSLContext;
import java.security.KeyManagementException;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
Ilya Bystrov
  • 2,902
  • 2
  • 14
  • 21