resttemplate with 2 way ssl certificate - spring-boot

I am using rettemplate to hit a third party url which is using 2 way ssl certificate. i have set httpclient with truststore and keystore still getting ssl handshake exception. I have also tried after importing certificate in my cacerts but still no luck. Any one who has used 2 way ssl with resttemplate
private HttpClient createHttpClient(final String keyAlias) {
logger.info("Creating HTTP client using keystore={} and alias={}", keyStorePath, keyAlias);
final KeyStore trustStore = new KeyStoreFactoryBean(makeResource(keyStorePath), keyStoreType, keyStorePassword)
.newInstance();
KeyStore keyStore =
new KeyStoreFactoryBean(makeResource(keyStorePath), keyStoreType, keyStorePassword)
.newInstance();
final SSLContext sslContext;
HttpHost proxyNew = null;
proxyNew = new HttpHost(proxyURL, proxyPort);
HttpClient httpClient=null;
try {
sslContext = SSLContexts.custom()
.loadKeyMaterial(keyStore, keyStorePassword.toCharArray(), (aliases, socket) -> keyAlias)
.loadTrustMaterial(trustStore, (x509Certificates, s) -> false).build();
SSLConnectionSocketFactory socketFactory = new SSLConnectionSocketFactory(new SSLContextBuilder().loadTrustMaterial(null, new TrustSelfSignedStrategy()).build());
} catch (NoSuchAlgorithmException | KeyManagementException | KeyStoreException | UnrecoverableKeyException e) {
throw new IllegalStateException("Error loading key or trust material", e);
}
final SSLConnectionSocketFactory sslSocketFactory = new SSLConnectionSocketFactory(sslContext,
new String[] { "TLSv1.2", "TLSv1.1" }, null, SSLConnectionSocketFactory.getDefaultHostnameVerifier());
final Registry<ConnectionSocketFactory> registry = RegistryBuilder.<ConnectionSocketFactory> create()
.register("http", PlainConnectionSocketFactory.getSocketFactory()).register("https", sslSocketFactory)
.build();
final PoolingHttpClientConnectionManager connectionManager = new PoolingHttpClientConnectionManager(registry);
connectionManager.setMaxTotal(httpClientPoolSize);
connectionManager.setDefaultMaxPerRoute(httpClientPoolSize);
HttpHost proxy = null;
proxy = new HttpHost(proxyURL, proxyPort);
if(isProxy)
return HttpClients.custom().setSSLSocketFactory(sslSocketFactory).setConnectionManager(connectionManager).setProxy(proxy).build();
return HttpClients.custom().setSSLSocketFactory(sslSocketFactory).setConnectionManager(connectionManager).build();
}

Related

How to configure sslContext with p12 store (and password) for client authentication with https endpoint in Spring/Kotlin

I want to call a https protected endpoint using a certificate and private key. I received a .p12 keystore which is protected with a password.
For testing purposes I extracted the .cer file and private key using openssl.
I could verify locally that communication works by setting the ssl context like this:
fun test(): WebClient.RequestHeadersSpec<*> {
val sslContext = SslContextBuilder
.forClient()
.keyManager(ClassPathResource("cert").inputStream, ClassPathResource("key").inputStream)
.build()
val httpClient: HttpClient = HttpClient.create().secure { sslSpec -> sslSpec.sslContext(sslContext) }
val webClient = WebClient
.builder()
.baseUrl("baseUrl")
.clientConnector(ReactorClientHttpConnector(httpClient))
.build()
return webClient
.post()
.uri("uri")
.contentType(MediaType.APPLICATION_JSON)
.bodyValue("test")
}
However, I do not want to version the cert and private key in my repository. How would I set the sslContext with the .p12 keystore and the password? I did not find any examples for this scenario.
load file from disc
this is java but it should be easy to adapt to kotlin
KeyStore keyStore = KeyStore.getInstance("pkcs12");
keyStore.load(new FileInputStream(keyStoreLocation), keyStorePassword.toCharArray());
SSLConnectionSocketFactory socketFactory = new SSLConnectionSocketFactory(
new SSLContextBuilder()
.loadKeyMaterial(keyStore, keyStorePassword.toCharArray())
.build(),
NoopHostnameVerifier.INSTANCE);
HttpClient httpClient = HttpClients.custom().setSSLSocketFactory(socketFactory).build();
where file location and password can provided from configuration
#Value("${keyStore.location}")
private String keyStoreLocation;
#Value("${keyStore.password}")
private String keyStorePassword;

How to handle big response body in in EntityUtils toString

Hi im working with apache http client and the response body Content-Length: 14011 ,Due to this there is 3 sec delay added while getting the entity and in the future the response body size may increase even more ,im looking for an efficient solution on how to handle big response body while minimizing the delay by EntityUtils.toString. i am a beginner so please guide me
The code im working with
SSLContext sslContext = SSLContext.getInstance("SSL");
sslContext.init(null, new TrustManager[] { customTm }, null);
// You don't have to set this as the default context,
// it depends on the library you're using.
SSLContext.setDefault(sslContext);
Registry<AuthSchemeProvider> authSchemeRegistry = RegistryBuilder.<AuthSchemeProvider>create()
.register(AuthSchemes.NTLM, new NTLMSchemeFactory())
.build();
BasicCredentialsProvider credsProvider = new BasicCredentialsProvider();
credsProvider.setCredentials(AuthScope.ANY, new NTCredentials(srvAccountUsername, srvAccountPassword, null, "DMN1"));
SSLConnectionSocketFactory sslConSocFactory = new SSLConnectionSocketFactory(sslContext, new NoopHostnameVerifier());
HttpClientBuilder clientbuilder = HttpClients.custom();
clientbuilder =clientbuilder.setDefaultAuthSchemeRegistry(authSchemeRegistry)
.setDefaultCredentialsProvider(credsProvider);
clientbuilder = clientbuilder.setSSLSocketFactory(sslConSocFactory);
CloseableHttpClient httpclient = clientbuilder.build();
HttpGet httpget = new HttpGet(url.toString());
httpget.addHeader("X-API-KEY", ApiKey);
HttpResponse httpresponse = httpclient.execute(httpget);
String responseBody = EntityUtils.toString(httpresponse.getEntity(), StandardCharsets.UTF_8);
ObjectMapper objectMapper = new ObjectMapper();
Object response = objectMapper.readValue(responseBody, Object.class);
return new ResponseEntity<>(httpresponse ,HttpStatus.OK);
Here is the problem where the delay is being added
String responseBody = EntityUtils.toString(httpresponse.getEntity(), StandardCharsets.UTF_8);
ObjectMapper objectMapper = new ObjectMapper();
Object response = objectMapper.readValue(responseBody, Object.class);

HttpClient have both SSL and Proxy authentication configured?

I have two pieces of code using HttpClient,
First part in case that the end point requires SSL
Second is proxy connection with basic authentication
My question Is how can I make this code conditional so in cases i have SSL + Proxy or SSL only
I have hard time figuring out how to set the default credentials for example after I created the client using the client in the SSL part
.setDefaultCredentialsProvider(credsProvider)
This part is how I create the Client when I need SSL
CloseableHttpClient client = null;
if(conf.isUseSslConfig()) {
SSLContext sslcontext = SSLContexts.custom()
.loadTrustMaterial(new File(conf.getTrustStoreLocation()), conf.getTrustStorePassword().toCharArray(), new TrustSelfSignedStrategy()).build();
// Allow protocols
SSLConnectionSocketFactory sslsf = new SSLConnectionSocketFactory(sslcontext,conf.getTlsVersions(), null,
SSLConnectionSocketFactory.getDefaultHostnameVerifier());
client = HttpClients.custom().setSSLSocketFactory(sslsf).build();
}else {
client= HttpClients.createDefault();
}
And this part is how I create the Client when I need Proxy authentication:
if(conf.isUseProxyConfig()){
CredentialsProvider credsProvider = new BasicCredentialsProvider();
credsProvider.setCredentials(
new AuthScope("fakeProxy.xerox.com", 80),
new UsernamePasswordCredentials("xeroxUser","fakePassword123"));
HttpClients.custom()
.setDefaultCredentialsProvider(credsProvider).build();
}
So the bottom line is how to make the two sections work together so in case
Call with SSL + Proxy and authentication
Call with only SSL
Call with only Proxy and authentication
You can write code this way to get multiple conditions resolved :
CloseableHttpClient client = null;
if(conf.isUseSslConfig() && conf.isUseProxyConfig()) {
setSSLSetting(client);
setProxy()
}else if(conf.isUseSslConfig()) {
setSSLSetting(client);
}else {
client= HttpClients.createDefault();
}
private void setProxy(){
CredentialsProvider credsProvider = new BasicCredentialsProvider();
credsProvider.setCredentials(new AuthScope("fakeProxy.xerox.com", 80),new UsernamePasswordCredentials("xeroxUser","fakePassword123"));
}
private void setSSLSetting(CloseableHttpClient client){
SSLContext sslcontext = SSLContexts.custom()
.loadTrustMaterial(new File(conf.getTrustStoreLocation()), conf.getTrustStorePassword().toCharArray(), new TrustSelfSignedStrategy()).build();
// Allow protocols
SSLConnectionSocketFactory sslsf = new SSLConnectionSocketFactory(sslcontext,conf.getTlsVersions(), null,
SSLConnectionSocketFactory.getDefaultHostnameVerifier());
client = HttpClients.custom().setSSLSocketFactory(sslsf).build();
}
or you can create methods that return client with different settings and configs like this :
final Registry<ConnectionSocketFactory> registry = RegistryBuilder.<ConnectionSocketFactory>create().register("http", new PlainConnectionSocketFactory()).register("https", sslsf).build();
final PoolingHttpClientConnectionManager cm = new PoolingHttpClientConnectionManager(registry);
private CloseableHttpClient createHttpClient(String headerName, String value) throws NoSuchAlgorithmException, KeyManagementException,KeyStoreException {
SSLContextBuilder builder = new SSLContextBuilder();
builder.loadTrustMaterial(null, new TrustSelfSignedStrategy());
Header header = new BasicHeader(headerName,value);
List<Header> headers = new ArrayList<>();
headers.add(header);
RequestConfig reqConfig = RequestConfig.custom().setConnectionRequestTimeout(long milli seconds).build();
CloseableHttpClient httpclient = HttpClients.custom().
setDefaultHeaders(headers).
setDefaultRequestConfig(reqConfig).
setConnectionManager(cm).
build();
return httpclient;
}

How to set cipher suites with javax.net.ssl.SSLContext

In a spring boot application using java8, I am setting the underlying SSLConext of an httpClient connection as follows:
import javax.net.ssl.SSLContext;
SSLContext sslContext = SSLContext.getInstance("TLSv1.2");
sslContext.init(null, null, null);
CloseableHttpClient httpClient = HttpClientBuilder
.create()
.setConnectionManager(myConnectionManager)
.setDefaultRequestConfig(rqConfig)
.setSSLContext(sslContext)
.build();
I need to set the cipher suites for the underlying TLS1.2 secured connection to something stronger of my choice. I don't see a way to do this with the way I am creation the sslContext in my code.
Can someone help me set up the cipher suites with my sslContext ?
================UPDATE=================
This is how I have now created my HttpClient
CloseableHttpClient httpClient = HttpClientBuilder
.create()
.setConnectionManager(myConnectionManager)
.setDefaultRequestConfig(rqConfig)
.setSSLSocketFactory(new SSLConnectionSocketFactory(
SSLContexts.createSystemDefault(),
new String[]{"TLSv1.2"},
new String[] {"some-gibberish-cipher-suite"},
SSLConnectionSocketFactory.getDefaultHostnameVerifier()))
.build();
Preferred TLS protocol versions and custom ciphers can be specified when creating a custom SSLConnectionSocketFactory instance
CloseableHttpClient client = HttpClients.custom()
.setSSLSocketFactory(new SSLConnectionSocketFactory(
SSLContexts.createSystemDefault(),
new String[]{"TLSv1.2"},
new String[] {"TLS_RSA_WITH_AES_256_CBC_SHA256"},
SSLConnectionSocketFactory.getDefaultHostnameVerifier()))
.build();
try (CloseableHttpResponse response = client.execute(new HttpGet("https://httpbin.org/"))) {
System.out.println(response.getStatusLine());
HttpEntity entity = response.getEntity();
EntityUtils.consume(entity);
}
Alternatively, one can create a custom PoolingHttpClientConnectionManager instance with the desired SSL configuration.
PoolingHttpClientConnectionManager cm = new PoolingHttpClientConnectionManager(RegistryBuilder.<ConnectionSocketFactory>create()
.register("http", PlainConnectionSocketFactory.getSocketFactory())
.register("https", new SSLConnectionSocketFactory(
SSLContexts.createSystemDefault(),
new String[]{"TLSv1.2"},
new String[]{"TLS_RSA_WITH_AES_256_CBC_SHA256"},
SSLConnectionSocketFactory.getDefaultHostnameVerifier()))
.build());
CloseableHttpClient client = HttpClients.custom()
.setConnectionManager(cm)
.build();
try (CloseableHttpResponse response = client.execute(new HttpGet("https://httpbin.org/"))) {
System.out.println(response.getStatusLine());
HttpEntity entity = response.getEntity();
EntityUtils.consume(entity);
}

Proxy configuration in OAuth2RestTemplate

I need to consume an API which is secured by OAuth2. For that I am using OAuth2RestTemplate.
But am getting below error:
java.net.ConnectException: Connection timed out: connect
This is happening due to proxy issue. I Know how to set proxy in RestTemplate :
SimpleClientHttpRequestFactory clientHttpRequestFactory = new SimpleClientHttpRequestFactory();
Proxy proxy = new Proxy(Proxy.Type.HTTP, new InetSocketAddress("Proxy host", 8080));
clientHttpRequestFactory.setProxy(proxy);
RestTemplate restTemplate = new RestTemplate(clientHttpRequestFactory);
The same way I tried to set for OAuth2RestTemplate :
#Bean
public OAuth2RestOperations restTemplate(OAuth2ClientContext oauth2ClientContext) {
OAuth2RestTemplate client = new OAuth2RestTemplate(resource(), oauth2ClientContext);
SimpleClientHttpRequestFactory clientHttpRequestFactory = new SimpleClientHttpRequestFactory();
Proxy proxy = new Proxy(Proxy.Type.HTTP, new InetSocketAddress(PROXY_HOST, PROXY_PORT));
clientHttpRequestFactory.setProxy(proxy);
client.setRequestFactory(clientHttpRequestFactory);
return client;
}
But it is not working and giving "Connection timed out" exception. This is happening because of this first line OAuth2RestTemplate client = new OAuth2RestTemplate(resource(), oauth2ClientContext); which tries to get Access token that means there also it needs proxy setting. if I add below lines then it works:
System.setProperty("https.proxyHost", "urproxy.com");
System.setProperty("https.proxyPort", "8080");
But I can not use System.setProperties("","") option as we do not have permission to set on tomcat server.
I researched but could not find any way to set proxy in OAuth2RestTemplate while creating this object.
Any help would be appreciated. Thanks
OAuth2RestTemplate just creates a set of AccessTokenProvider to retrieve the token from authorization server according to different kinds of grant types. For example AuthorizationCodeAccessTokenProvider is used to retrieve access token with grant type authorization_code. The token providers themselves initiate some RestTemplate to send the request but do not use OAuth2RestTemplate just created. One way might resolve the issue. That is to create you own AccessTokenProvider and set the request factory.
SimpleClientHttpRequestFactory requestFactory = new SimpleClientHttpRequestFactory();
Proxy proxy= new Proxy(Type.HTTP, new InetSocketAddress(PROXY_HOST, PROXY_PORT));
requestFactory.setProxy(proxy);
AuthorizationCodeAccessTokenProvider authorizationCodeAccessTokenProvider = new AuthorizationCodeAccessTokenProvider();
authorizationCodeAccessTokenProvider.setRequestFactory(requestFactory);
ImplicitAccessTokenProvider implicitAccessTokenProvider = new ImplicitAccessTokenProvider();
implicitAccessTokenProvider.setRequestFactory(requestFactory);
AccessTokenProvider accessTokenProvider = new AccessTokenProviderChain(
Arrays.<AccessTokenProvider> asList(authorizationCodeAccessTokenProvider, implicitAccessTokenProvider));
OAuth2RestTemplate client = new OAuth2RestTemplate(github(), oauth2ClientContext);
client.setAccessTokenProvider(accessTokenProvider);
You could also add ResourceOwnerPasswordAccessTokenProvider and ClientCredentialsAccessTokenProvider to the OAuth2RestTemplate.
This RestTemplate provides a workaround:
/**
* An OAuth2RestTemplate with proxy support.
*
* #author E.K. de Lang
*/
public class ProxySupportingOAuth2RestTemplate
extends OAuth2RestTemplate
{
private static final Logger LOG = LogFactory.getLogger(ProxySupportingOAuth2RestTemplate.class);
private final SimpleClientHttpRequestFactory factory;
public ProxySupportingOAuth2RestTemplate(OAuth2ProtectedResourceDetails resource, OAuth2ClientContext context,
AccessTokenProvider accessTokenProvider)
{
super(resource, context);
factory = new SimpleClientHttpRequestFactory();
super.setRequestFactory(factory);
super.setAccessTokenProvider(accessTokenProvider);
// To fix issue: https://github.com/spring-projects/spring-security-oauth/issues/459 also set the factory of the token-provider.
if (accessTokenProvider instanceof OAuth2AccessTokenSupport) {
((OAuth2AccessTokenSupport) accessTokenProvider).setRequestFactory(factory);
}
else {
throw new UnsupportedOperationException("accessTokenProvider must extend OAuth2AccessTokenSupport");
}
}
public void setProxy(Proxy proxy)
{
if (LOG.isDebugEnabled()) {
LOG.debug("setProxy:" + proxy);
}
if (super.getRequestFactory() == factory) {
factory.setProxy(proxy);
}
else {
throw new UnsupportedOperationException("RequestFactory has changed.");
}
}
}

Resources