HttpClient have both SSL and Proxy authentication configured? - apache-commons-httpclient

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

Related

How to force the Application deployed in WAS servers to use default trust store trust.p12 (Node) rather than refer to java cercets

My Rest Template is referring to java_1.8_192/jre/lib/security/cacerts rather than it is not referring to WAS default trust store. Below are the code snippet i am using. How to force the code to check default WAS node level trust store.
SSLContext sslContext = SSLContexts.custom().build();
SSLConnectionSocketFactory sslConnectionSocketFactory = new SSLConnectionSocketFactory( sslContext, new String[] { "TLSv1.2", "TLSv1.1" },null, SSLConnectionSocketFactory.getDefaultHostnameVerifier() );
CloseableHttpClient client = HttpClients.custom().setSSLSocketFactory( sslConnectionSocketFactory ).build();
ClientHttpRequestFactory requestFactory = new HttpComponentsClientHttpRequestFactory( client );

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

resttemplate with 2 way ssl certificate

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

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.");
}
}
}

Add proxy information and basic auth to the resttemplate using httpClient

My development environment is behind a proxy so i need to set the proxy information to the rest template, that's all good when i use a HttpComponentsClientHttpRequestFactory and set the proxy setting in the httpClient and set it in the template.
But now i have a rest service that needs basic auth. And to set the basic auth credentials, i need to set them in the httpClient on the rest template. But i see that the getparams method in the httpClient is depricated, so i can't just update the existing client in the template, and if i create a new httpclient object, i will overwrite the proxy info that were set during the application bootstrapping.
So is there some way that i could extract the httpClient from the rest template and update it? Or is there any other way to tackle this?
Thanks.
Configure the httpClient as follows:
HttpHost target = new HttpHost("hostname", 80, "http");
CredentialsProvider credsProvider = new BasicCredentialsProvider();
credsProvider.setCredentials(
new AuthScope(target.getHostName(), target.getPort()),
new UsernamePasswordCredentials("user", "passwd"));
HttpHost proxy = new HttpHost("proxy", 12345);
CloseableHttpClient httpclient = HttpClients.custom()
.setProxy(proxy)
.setDefaultCredentialsProvider(credsProvider).build();
HttpComponentsClientHttpRequestFactory requestFactory = new HttpComponentsClientHttpRequestFactory();
requestFactory.setHttpClient(httpclient);
RestTemplate restTemplate = new RestTemplate(requestFactory);
See also HttpClient Examples
The above solution did not work for me i work around the above and finally make it work with small modifications.
RestTemplate restTemplate = new RestTemplate();
HttpHost proxy =null;
RequestConfig config=null;
String credentials = this.env.getProperty("uname") + ":" + this.env.getProperty("pwd");
String encodedAuthorization = Base64.getEncoder().encodeToString(credentials.getBytes());
Header header = new BasicHeader(HttpHeaders.AUTHORIZATION, "Basic " + encodedAuthorization);
List<Header> headers = new ArrayList<>();
headers.add(header);
if(Boolean.valueOf(env.getProperty("proxyFlag"))){
proxy = new HttpHost(this.env.getProperty("proxyHost"), Integer.parseInt(env.getProperty("proxyPort")), "http");
config= RequestConfig.custom().setProxy(proxy).build();
}else{
config= RequestConfig.custom().build();
}
CloseableHttpClient httpClient = HttpClientBuilder.create().setDefaultRequestConfig(config)
.setDefaultHeaders(headers).build();
HttpComponentsClientHttpRequestFactory factory = new HttpComponentsClientHttpRequestFactory(httpClient);
restTemplate.setRequestFactory(factory);
return restTemplate;

Resources