Disabling SSL Certificate Validation in Spring RestTemplate

后端 未结 12 1455
花落未央
花落未央 2020-11-28 20:05

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

I want to make a https call from web app A to web app B, however I am using a self-signed c

相关标签:
12条回答
  • 2020-11-28 20:22

    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;
               }
            }
    
    0 讨论(0)
  • 2020-11-28 20:22

    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!

    0 讨论(0)
  • 2020-11-28 20:22

    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 citrix -keystore "C:\Program Files\Java\jdk-11.0.2\lib\security\cacerts" -file citrix.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();
          }
      
    0 讨论(0)
  • 2020-11-28 20:24

    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 = new TrustStrategy() {
            @Override
            public boolean isTrusted(X509Certificate[] x509Certificates, String s) throws CertificateException {
                return 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;
    }
    
    0 讨论(0)
  • 2020-11-28 20:24

    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);
        }
    });
    
    0 讨论(0)
  • 2020-11-28 20:26

    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.

    0 讨论(0)
提交回复
热议问题