Only two parallel https calls are going outside with SSLConnectionSocketFactory - java

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

Need help to Bypass SSL certificate validation with rest template

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

How to get the attached self sign certificate in the server spring boot application

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

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;
}

How to add client certificates to the Spring WebClient?

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();

Add Custom ClientHttpRequestInterceptor in RestTemplateBuilder with HttpComponentsClientHttpRequestFactory

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;
}

Categories