When I I am making outgoing requests with SSLConnectionSocketFactory request factory, only two parallel outgoing calls are getting response at a time. Subsequent calls reaches outside endpoint only when first two requests finish responding.
This is the RestTemplate config,
private RestTemplate createSSLRestTemplate(String keyStorePath, String keyStorePassword) throws KeyStoreException, IOException, NoSuchAlgorithmException, CertificateException, KeyManagementException, UnrecoverableKeyException {
KeyStore keyStore = KeyStore.getInstance(KeyStore.getDefaultType());
keyStore.load(new ClassPathResource(keyStorePath).getInputStream(), keyStorePassword.toCharArray());
SSLConnectionSocketFactory socketFactory = new SSLConnectionSocketFactory(
new SSLContextBuilder()
.loadTrustMaterial(null, new TrustSelfSignedStrategy())
.loadKeyMaterial(keyStore, keyStorePassword.toCharArray())
.build(),
NoopHostnameVerifier.INSTANCE);
HttpClient httpClient = HttpClients.custom().setSSLSocketFactory(
socketFactory).build();
ClientHttpRequestFactory requestFactory = new HttpComponentsClientHttpRequestFactory(
httpClient);
BufferingClientHttpRequestFactory bufferingRequestFactory = new BufferingClientHttpRequestFactory(requestFactory);
RestTemplate restTemplate = new RestTemplate(bufferingRequestFactory);
restTemplate.setInterceptors(Collections.singletonList(restTemplateLoggingInterceptor));
return restTemplate;
}
However if I replaced the RestTemplate with simple RestTeamplate all the calls goes to outside and responds in parallel.
public RestTemplate restTemplateDefault() {
return new RestTemplate();
}
Code that make the outgoing http request
HttpEntity<Req> request = new HttpEntity(req, this.prepareHttpHeaders());
return this.xmlUnmarshaller.unmarshall((String)this.restTemplate.postForObject(host, request, String.class, new Object[0]), responseClass);
How do I have sslcontext and have achieve parallelism?
Related
I have one requirement i.e. to bypass SSL verification while connecting to webservice using rest template.
Currently I am implementing proxy to rest template via below code.
SimpleClientHttpRequestFactory clientHttpRequestFactory = new
SimpleClientHttpRequestFactory();
Proxy proxy = new Proxy(Proxy.Type.HTTP,new InetSocketAddress(proxyHost,
Integer.parseInt(proxyPort)));
clientHttpRequestFactory.setProxy(proxy);
RestTemplate restTemplate = new RestTemplate(clientHttpRequestFactory);
ResponseEntity<String> responseEntity= restTemplate.exchange(url,HttpMethod.POST,
entity,String.class);
response = responseEntity.getBody();
How can I modify my Rest template so while connecting to URL it doesn't look for SSL certificate Validation.
I do checked couple of solutions online for this but all are implemented with
HttpComponentsClientHttpRequestFactory
which don't have provision to pass proxy host and server (Java.net.proxy object).
Can anyone help me here, I am stuck from long on this.
Solution I found online is below
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 = new RestTemplate(requestFactory);
Thanks,
Anshu
my client side i have attached the certificate in the restTemplate code below . using this rest template i am calling other API(server side) . how to get the certificate in that server side
#Bean(name="custRest")
#Primary
public RestTemplate restTemplate(RestTemplateBuilder builder) throws Exception {
char[] password = "changeit".toCharArray();
TrustStrategy acceptingTrustStrategy = (X509Certificate[] chain, String authType) -> true;
SSLContext sslContext = SSLContextBuilder
.create().loadKeyMaterial(new File("C:\\java\\java-certificate.der"),null,null)
.loadTrustMaterial(ResourceUtils.getFile("C:\\java\\java-certificate.der"), null,
acceptingTrustStrategy)
.build();
HttpClient client = HttpClients.custom().setSSLContext(sslContext).build();
return builder.requestFactory(new HttpComponentsClientHttpRequestFactory(client))
.build();
}
You can find all step for configuring HTTPS for Spring Boot in article:
https://www.baeldung.com/spring-boot-https-self-signed-certificate
is it possible to attach the certificate without store in trust store in sslcontext
This is my code while running my code its not x509 format exception i am getting
#Bean(name="custRest")
#Primary
public RestTemplate restTemplate() throws Exception {
char[] password = "password".toCharArray();
SSLContext sslContext = SSLContextBuilder
.create()
.loadTrustMaterial(ResourceUtils.getFile("C:\\test\\test.der"), password,
new TrustSelfSignedStrategy())
.build();
HttpClient client = HttpClients.custom().setSSLContext(sslContext).build();
return new RestTemplateBuilder().requestFactory(new HttpComponentsClientHttpRequestFactory(client))
.build();
}
how to config in conf\nginx.conf of nginx.
server {
listen 10000; // nginx port
server_name localhost;
charset utf-8;
location /certs/ {
# where your cert files to be stored in nginx server machine. I suppose it is under /data/certs.
alias /data/certs/;
autoindex off;
}
}
url of access = http://[nginx server ip ]:10000/certs/test.cer
The below is sample of building restTemplate
import org.apache.http.conn.ssl.NoopHostnameVerifier;
import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.protocol.HttpContext;
import org.springframework.web.client.RestTemplate;
// suppose your server ip is 10.0.0.1
final static String URL = "http://10.0.0.1:10000/certs/test.cer";
final static String PWD = "123456";
private RestTemplate sslTemplate() throws Exception {
SSLContext sslContext = org.apache.http.ssl.SSLContexts.custom()
.loadTrustMaterial(new URL(URL), PWD.toCharArray())
// .loadTrustMaterial(new File(FILE_PATH), PWD.toCharArray())
.build();
SSLConnectionSocketFactory csf = new SSLConnectionSocketFactory(sslContext, NoopHostnameVerifier.INSTANCE);
HttpRequestRetryHandler retryHandler = new HttpRequestRetryHandler() {
#Override
public boolean retryRequest(IOException exception, int executionCount, HttpContext context) {
return false;
}
};
CloseableHttpClient httpClient = HttpClients.custom().setSSLSocketFactory(csf).setRetryHandler(retryHandler)
.build();
HttpComponentsClientHttpRequestFactory requestFactory = new HttpComponentsClientHttpRequestFactory();
requestFactory.setHttpClient(httpClient);
RestTemplate restTemplate = new RestTemplate(requestFactory);
return restTemplate;
}
I'm building a Spring WebClient which internally calls to REST API's which are hosted in different server. To do that I need to send public key (.cert) and private key (.key) to every request for the handshake.
I'm not sure how to do that with Spring WebClient.
I tried setting up WebClient, but struck at adding this peace of code
WebClient Builder
this.webCLient = WebClient.builder()
.baseUrl("https://some-rest-api.com")
.defaultHeader(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON.toString())
.build();
Actual Call
this.webClient.get()
.uri("/getData")
.exchange()
.flatMap(clientResponse -> {
System.out.println(clientResponse);
return clientResponse.bodyToMono(MyClass.class);
});
Since there were no certificates added to the request, I'm getting the handshake error on the log
javax.net.ssl.SSLException: Received fatal alert: handshake_failure
How to add those certificates to the WebClient requests, so I don't get this error ? I have the certificates, but not sure how to add it.
It took me some time to find the missing piece in Thomas' answer.
Here it is:
public static SslContext getTwoWaySslContext() {
try(FileInputStream keyStoreFileInputStream = new FileInputStream(ResourceUtils.getFile(clientSslKeyStoreClassPath));
FileInputStream trustStoreFileInputStream = new FileInputStream(ResourceUtils.getFile(clientSslTrustStoreClassPath));
) {
KeyStore keyStore = KeyStore.getInstance("jks");
keyStore.load(keyStoreFileInputStream, clientSslKeyStorePassword.toCharArray());
KeyManagerFactory keyManagerFactory = KeyManagerFactory.getInstance("SunX509");
keyManagerFactory.init(keyStore, clientSslKeyStorePassword.toCharArray());
KeyStore trustStore = KeyStore.getInstance("jks");
trustStore.load(trustStoreFileInputStream, clientSslTrustStorePassword.toCharArray());
TrustManagerFactory trustManagerFactory = TrustManagerFactory.getInstance("SunX509");
trustManagerFactory.init(trustStore);
return SslContextBuilder.forClient()
.keyManager(keyManagerFactory)
.trustManager(trustManagerFactory)
.build();
} catch (Exception e) {
log.error("An error has occurred: ", e);
}
return null;
}
HttpClient httpClient = HttpClient.create().secure(sslSpec -> sslSpec.sslContext(SslUtil.getTwoWaySslContext()));
ClientHttpConnector clientHttpConnector = new ReactorClientHttpConnector(httpClient);
WebClient webClient = webClientBuilder
.clientConnector(clientHttpConnector)
.baseUrl(baseUrl)
.build();
Enjoy!
taken from the documentation Spring Webclient - Reactor Netty
To access the ssl configurations you need to supply a custom netty HttpClient with a custom sslContext.
SslContext sslContext = SslContextBuilder
.forClient()
// build your ssl context here
.build();
HttpClient httpClient = HttpClient.create().secure(sslSpec -> sslSpec.sslContext(sslContext));
WebClient webClient = WebClient.builder()
.clientConnector(new ReactorClientHttpConnector(httpClient))
.build();
I need to add a Custom Header in all my RestTemplate Client requests. So I implemented ClientHttpRequestInterceptor. And I add the interceptor in my RestTemplateBuilder config like shown below. The problem is that when the RestTemplate makes the HTTP call it throws following exception:
java.lang.ClassCastException: org.springframework.http.client.InterceptingClientHttpRequestFactory cannot be cast to org.springframework.http.client.HttpComponentsClientHttpRequestFactory
RestTemplate Bean Creation :
#Bean
public RestTemplate restTemplate(RestTemplateBuilder restTemplateBuilder) {
PoolingHttpClientConnectionManager poolingConnectionManager = new PoolingHttpClientConnectionManager();
poolingConnectionManager.setMaxTotal(restTemplateProps.getMaxConnectionsPerPool());
poolingConnectionManager.setDefaultMaxPerRoute(restTemplateProps.getMaxDefaultConnectionPerRoute());
CloseableHttpClient client = HttpClientBuilder.create().setConnectionManager(poolingConnectionManager).build();
ClientHttpRequestFactory clientHttpRequestFactory = new HttpComponentsClientHttpRequestFactory(client);
restTemplateBuilder = restTemplateBuilder.additionalInterceptors(new MyClientHttpRequestInterceptor());
return restTemplateBuilder.requestFactory(clientHttpRequestFactory).build();
}
Also, I am updating the timeouts later in below code:
protected void setRestTemplateTimeouts() {
HttpComponentsClientHttpRequestFactory rf =
(HttpComponentsClientHttpRequestFactory) restTemplate.getRequestFactory();
rf.setConnectTimeout(restTemplateProps.getConnectionTimeout());
rf.setReadTimeout(restTemplateProps.getSocketTimeout());
}
Can anyone help me fix this?
The problem was, I was trying to set the connect and read timeouts after setting the ClientHttpRequestInterceptor.
In my setRestTemplateTimeouts() method when I try to fetch & typecast requestFactory to HttpComponentsClientHttpRequestFactory, I get the ClassCastException exception because restTemplate.getRequestFactory() returns InterceptingClientHttpRequestFactory instead of HttpComponentsClientHttpRequestFactory. This is because I added an interceptor in my restTemplate object.
Solution is to set the timeouts before setting interceptor because you can't set timeouts after setting an interceptor. Refer the code below:
#Bean
public RestTemplate restTemplate(RestTemplateBuilder restTemplateBuilder) {
PoolingHttpClientConnectionManager poolingConnectionManager = new PoolingHttpClientConnectionManager();
poolingConnectionManager.setMaxTotal(restTemplateProps.getMaxConnectionsPerPool());
poolingConnectionManager.setDefaultMaxPerRoute(restTemplateProps.getMaxDefaultConnectionPerRoute());
CloseableHttpClient client = HttpClientBuilder.create().setConnectionManager(poolingConnectionManager).build();
HttpComponentsClientHttpRequestFactory clientHttpRequestFactory = new HttpComponentsClientHttpRequestFactory(client);
clientHttpRequestFactory.setConnectTimeout(restTemplateProps.getConnectionTimeout());
clientHttpRequestFactory.setReadTimeout(restTemplateProps.getSocketTimeout());
restTemplateBuilder = restTemplateBuilder.additionalInterceptors(new MyClientHttpRequestInterceptor());
return restTemplateBuilder.requestFactory(clientHttpRequestFactory).build();
}
This is how i manage to get the interceptor to log both request and response without throwing exception - Attempted read from closed stream.
#Bean
public RestTemplate getRestTemplateConfig()
throws KeyStoreException, IOException, UnrecoverableKeyException, CertificateException, NoSuchAlgorithmException, KeyManagementException {
SSLContext context = SSLContextBuilder
.create()
.loadKeyMaterial(ResourceUtils.getFile("/opt/cert/keystore.jks"),
"password".toCharArray(),
"password".toCharArray())
.build();
HttpClient client = HttpClients
.custom()
.setSSLContext(context)
.build();
HttpComponentsClientHttpRequestFactory requestFactory = new HttpComponentsClientHttpRequestFactory();
requestFactory.setHttpClient(client);
RestTemplate restTemplate = new RestTemplate(requestFactory);
//Provide a buffer for the outgoing/incoming stream, allowing the response body to be read multiple times
// (if not configured, the interceptor reads the Response stream, and then returns body=null when responding to the data)
restTemplate.setRequestFactory(new BufferingClientHttpRequestFactory(requestFactory));
restTemplate.setErrorHandler(new RestTemplateResponseErrorHandler());
restTemplate.setInterceptors(Collections.<ClientHttpRequestInterceptor>singletonList(
new RestTemplateInterceptor()));
restTemplate.getMessageConverters().add(jacksonSupportsMoreTypes());
return restTemplate;
}
private HttpMessageConverter jacksonSupportsMoreTypes() {
MappingJackson2HttpMessageConverter converter = new MappingJackson2HttpMessageConverter();
converter.setSupportedMediaTypes(Arrays.asList( MediaType.APPLICATION_OCTET_STREAM));
return converter;
}