15

I am consuming json webservice using Spring3.0 restTemplate by calling post method.

        MultiValueMap<String, String> headers = new LinkedMultiValueMap<String, String>();
        headers.add("Content-Type", MediaType.APPLICATION_JSON_VALUE);      
        HttpEntity<Object> entity = new HttpEntity<Object>(requestAsString, headers);
        postForObject = restTemplate.postForObject(url, entity, responseClass );

Our application is deployed in WAS server and trying to connect producer by creating socket connection with TLS1.0. However, now producer only supports TLS1.1 and TLS1.2.

How to enforce restTempate to use TLS1.1 or TLS 1.2.

Normally for apache httpclient code , create custom ProtocolSocketFactory and override createSocket method. However , in case of RestTemplate , how to achieve same.

Panther
  • 3,312
  • 9
  • 27
  • 50

4 Answers4

32

With Spring > 3.1:

import javax.net.ssl.SSLContext;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClientBuilder;
import org.springframework.http.client.HttpComponentsClientHttpRequestFactory;
import org.springframework.web.client.RestTemplate;

SSLContext context = SSLContext.getInstance("TLSv1.2");
context.init(null, null, null);

CloseableHttpClient httpClient = HttpClientBuilder.create().setSSLContext(context)
    .build();
HttpComponentsClientHttpRequestFactory factory = new HttpComponentsClientHttpRequestFactory(httpClient);
RestTemplate restTemplate = new RestTemplate(factory);
.....
Michal Foksa
  • 11,225
  • 9
  • 50
  • 68
  • 1
    How do I do this with Spring 3.0.x ? – abhishekhp Jan 24 '18 at 20:57
  • 1
    @membersound accepted answer is not about better answer but about what works with OP, And anyways question is explicitly about spring 3.0 . I do appreciate this answer also and have upvoted – Panther Feb 27 '18 at 08:03
  • @abhishekhp, have u get the solution? – Panadol Chong Apr 18 '19 at 02:06
  • Totally agree with the solution, little insights into why `context.init(null, null, null)` would have helped. I mean if they are optional, why not overload and give one with no args needed? – bluefalcon May 11 '20 at 17:00
  • 1
    about context.init, the docs say: Either of the first two parameters may be null in which case the installed security providers will be searched for the highest priority implementation of the appropriate factory. Likewise, the secure random parameter may be null in which case the default implementation will be used. – lbenedetto Jun 14 '21 at 17:48
6

You can configure your RestTemplate to use a custom ClientHttpRequestFactory. In particular (since you're using Spring 3.0), there is a CommonsClientHttpRequestFactory. That will enable you to configure commons HTTP in detail, and your RestTemplate will use that for executing its requests.

Please note that the actual implementation classes have changed in later versions of Spring (and if you're still on 3.0 you really should consider updating). From 3.1 on the implementation class is called HttpComponentsClientHttpRequestFactory.

marthursson
  • 3,242
  • 1
  • 18
  • 28
  • Hi @marthursson, if use `CommonsClientHttpRequestFactory` in spring 3, means the `httpClient` must use `org.apache.commons.httpclient.HttpClient`? or we can use `org.apache.http.impl.client.DefaultHttpClient` ? – Panadol Chong Apr 16 '19 at 05:58
4

@abhishekhp If your question is still up.

    RestTemplate restTemplate = new RestTemplate();
    DefaultHttpClient httpClient = new DefaultHttpClient();
    // We're going to try and load and enable TLS version 1.2 standard communication context from JSSE Providers
    // This is enabled only for download media Mirakl as some merchants don't accept communication with TLS versions prior to 1.1
    try {
        SSLContext context;
        context = SSLContext.getInstance("TLSv1.2");
        context.init(null, null, null);

        SSLSocketFactory ssf = new SSLSocketFactory(context);
        ClientConnectionManager ccm = httpClient.getConnectionManager();
        SchemeRegistry sr = ccm.getSchemeRegistry();
        sr.register(new Scheme("http", 80, PlainSocketFactory.getSocketFactory()));
        sr.register(new Scheme("https", 443, ssf));

    } catch (NoSuchAlgorithmException | KeyManagementException e) {
        LOGGER.warn("Could not load the TLS version 1.2 due to => ", e);
    }

    restTemplate.setRequestFactory(new HttpComponentsClientHttpRequestFactory(httpClient));
Med Arrafi
  • 41
  • 3
2

Use the SSLConnectionSocketFactory if you want to set more than one valid TLS version. Eg if you want to support TLSv1.2 and TLSv1.3:

public HttpClient getHttpClient() {
    final var sslContext = SSLContext.getDefault();
    // TLSv1.2 or TLSv1.3
    final var sslConnectionSocketFactory =
        new SSLConnectionSocketFactory(sslContext, new String[]{"TLSv1.2","TLSv1.3"},
            null, javax.net.ssl.HttpsURLConnection.getDefaultHostnameVerifier());

    return  HttpClients.custom().setSSLSocketFactory(sslConnectionSocketFactory).build();
}

and combine it with a RestTemplate like this:

@Bean
public RestTemplate restTemplate() {
    final var factory = new HttpComponentsClientHttpRequestFactory(getHttpClient());
    return new RestTemplate(factory);
}
Datz
  • 3,156
  • 3
  • 22
  • 50