We have a generic application which delivers message to different POST endpoints. And we are using
CloseableHttpAsyncClient for this purpose. Its been built/initialized as follows,
private static CloseableHttpAsyncClient get() {
CloseableHttpAsyncClient lInstance;
IOReactorConfig ioReactorConfig = IOReactorConfig.custom()
.setIoThreadCount(100)
.setConnectTimeout(10000)
.setSoTimeout(10000).build();
ConnectingIOReactor ioReactor = null;
try {
ioReactor = new DefaultConnectingIOReactor(ioReactorConfig);
} catch (IOReactorException e) {
logger_.logIfEnabled(Level.ERROR, e);
}
PoolingNHttpClientConnectionManager connManager = new PoolingNHttpClientConnectionManager(ioReactor);
connManager.setDefaultMaxPerRoute(50);
connManager.setMaxTotal(5000);
connManager.closeIdleConnections(10000, TimeUnit.MILLISECONDS);
baseRequestConfig = RequestConfig.custom().setConnectTimeout(10000)
.setConnectionRequestTimeout(10000)
.setSocketTimeout(10000).build();
lInstance = HttpAsyncClients.custom().setDefaultRequestConfig(baseRequestConfig)
.setConnectionManager(connManager).build();
lInstance.start();
return lInstance;
}
This is prebuilt and initialized. As an when a new request arrives to our application, based on message, authentication type, a new postRequest is built httpPost = new HttpPost(builder.build());
After setting the required header, payload etc. exiting httpClient is used to send the request.
httpClient.execute(httpPost, httpContext, null);
Now, the question is based on the our new requirement to support client certificate based authentication. And since our current approach is to create httpClient in the beginning, the question is how to change the behaviour of httpClient to send client certificate to some endpoints and work as it is for other endpoints which doesn't require certificate to be send?
I know I can introduce SSLContext to CloseableHttpAsyncClient while creating, but at the time of creating I don't have any information that we have any endpoint which requires certificate based authentication. And we can have many endpoints which would be supporting client certificate and that would be known at runtime.
Related
I have a requirement to use springboot rest template which calls 3rd party and connects over 2-way ssl but it should go through the proxy, but I am getting "
Encountered connectivity issue while reaching APIsun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target
" exception. This is very generic exception. Keystore and certificate are accessible. Without proxy I can able to call same 3rd party API with same set of certificates in different environement. So no issue with certs and location.
Looks like proxy is not able to forward/find certificates to server. Anyone knows how to solve this? following is the code for creating rest template.
HttpClientBuilder httpClientBuilder = null;
if(proxyEnabled){
httpClientBuilder = getHttpClientBuilderWithProxy();
} else{
httpClientBuilder = getHttpClientBuilderWithoutProxy();
}
CloseableHttpClient client = null;
if(isSslEnabled){
logger.info("SSL enabled for closable http client");
SSLConnectionSocketFactory csf = new SSLConnectionSocketFactory( new SSLContextBuilder()
.loadKeyMaterial( ResourceUtils.getFile(keyStore) , keyStorePassword.toCharArray(), keyStorePassword.toCharArray())
.loadTrustMaterial(ResourceUtils.getFile(trustStore), trustStorePassword.toCharArray()) .build());
client = httpClientBuilder
.setSSLSocketFactory(csf)
.setProxyAuthenticationStrategy(new ProxyAuthenticationStrategy())
.build();
} else{
logger.info("SSL disabled for closable http client");
client = httpClientBuilder
.build();
}
clientHttpRequestFactory = new HttpComponentsClientHttpRequestFactory(client);
private HttpClientBuilder getHttpClientBuilderWithoutProxy(){
return HttpClientBuilder.create()
.disableAutomaticRetries();
}
private HttpClientBuilder getHttpClientBuilderWithProxy(){
HttpHost proxy = new HttpHost(httpProxyHost, httpProxyPort);
return HttpClientBuilder.create()
.setProxy(proxy)
.disableAutomaticRetries();
}
I am expecting it to call 3rd party API with proxy and ssl.
I'm using the Apache HTTP client (version 4.5.13) in Java 8 to perform a POST call that requires the client to authenticate using a certificate certificate, that I have stored in a .PFX file.
This is the code I'm using:
public static void performClientRequest() throws Exception {
//Trust Strategy to accept any server certificate
TrustStrategy trustStrategy = new TrustStrategy() {
public boolean isTrusted(X509Certificate[] chain, String authType)
throws CertificateException {
return true;
}
};
//Load PFX client certificate
KeyStore clientStore = KeyStore.getInstance("PKCS12");
InputStream instream = new FileInputStream("C:\\client.pfx");
try {
clientStore.load(instream, null);
} finally {
instream.close();
}
//Create ssl context with key store and trust strategy
SSLContext sslContext = SSLContexts.custom()
.loadKeyMaterial(clientStore, null)
.loadTrustMaterial(trustStrategy)
.build();
//Create ssl socket factory from context
SSLConnectionSocketFactory sslSocketFactory = new SSLConnectionSocketFactory(sslContext, NoopHostnameVerifier.INSTANCE);
//Create HTTP client
HttpClient httpClient = HttpClients.custom()
.setSSLSocketFactory(sslSocketFactory)
.build();
//Perform call
URI url = new URI("https://mysite.foo");
HttpPost request = new HttpPost(url);
request.setHeader("Content-Type","application/json");
request.setHeader("Accept", "application/json");
String body = "...";
StringEntity bodyEntity = new StringEntity(body);
request.setEntity(bodyEntity);
HttpResponse response = httpClient.execute(request);
HttpEntity entity = response.getEntity();
System.out.println("----------------------------------------");
System.out.println(response.getStatusLine());
EntityUtils.consume(entity);
}
I've used this code in in the past and it worked perfectly back then, but now I'm trying to reuse it and it just doesn't send the certificate, the server replies with:
HTTP/1.1 403 No client certificate supplied
How can I debug this and discover why the certificate is not being sent?
Note: I implemented a similar call both in C# and using Postman, and in both cases it works perfectly, so the client certificate authentication to the server is working, it's just not working in my Java implementation.
So, I don't know if this is a bug or intended behavior (if so, why?), but apparently the PFX file must be password-protected, then it gets sent correctly. I could not make this work with a non-protected PFX file and passing null as the password like I was doing in the code I posted in the question.
So the problem is solved, but I would be curious if anyone could comment on WHY this happens.
I'm trying to hit a REST API link using Apache HttpClient but I keep getting a 401 error returned. I can login when I go to the URL in browser, after being prompted for a password. The code I'm using is below:
CredentialsProvider provider = new BasicCredentialsProvider();
UsernamePasswordCredentials credentials = new UsernamePasswordCredentials(creds.get(0), creds.get(1));
provider.setCredentials(AuthScope.ANY, credentials);
AuthCache authCache = new BasicAuthCache();
authCache.put(new HttpHost(uri.getHost(), uri.getPort(), "https"), new BasicScheme());
BasicHttpContext context = new BasicHttpContext();
context.setAttribute(ClientContext.CREDS_PROVIDER, provider);
context.setAttribute(ClientContext.AUTH_CACHE, authCache);
DefaultHttpClient client = new DefaultHttpClient();
client.setHttpRequestRetryHandler(new DefaultHttpRequestRetryHandler());
client.setCredentialsProvider(provider);
HttpResponse response = null;
try
{
// response = client.execute(new HttpGet(uri));
response = client.execute(new HttpGet(uri), context);
}
catch(IOException e)
{
logger.error("Error running authenticated get request: " + e);
}
I'm using HttpClient 4.2.3 and unfortunately I'm not able to upgrade this.
Any help would be appreciated! Thanks!
EDIT: turns out I need to supply the certificate, like using -cacert in curl, however I can't find an example of this!
Since you need to provide a certificate maybe this can help:
http://hc.apache.org/httpcomponents-client-4.2.x/httpclient/examples/org/apache/http/examples/client/ClientCustomSSL.java
I think that example complies with 4.2.3 .
i want to do a HTTP POST request, which is secured by ssl.
the client is my java program, which is posting to a https-url (https:// ...). the certificate of the website is verified, i am using Apache HttpClient 4.5.1
with a normal post-request and a custom httpclient.
HttpContext context = HttpClientContext.create();
PoolingHttpClientConnectionManager cm = new PoolingHttpClientConnectionManager();
cm.setDefaultMaxPerRoute(MAX_CONNECTIONS_PER_ROUTE);
cm.setMaxTotal(MAX_TOTAL_CONNECTIONS);
CloseableHttpClient client = HttpClients.custom()
.setConnectionManager(cm)
.build();
HttpContext context = HttpClientContext.create();
HttpPost login = new HttpPost("https://example.org"); // example url
login.addHeader("Content-Type", "application/json; charset=UTF-8");
login.addHeader("Accept", "application/json; charset=UTF-
login.setEntity(new StringEntity(loginData, ContentType.create("application/json", "UTF-8")));
JSONResponseHandler<JSONObject> rh = new JSONResponseHandler<>();
JSONObject test = client.execute(login, rh, context);
is this sufficend to get a ssl-secured connection or do i have to work with KeyStore, SSLContext and SSLConnectionFactory?
if i have to use those, how would i do this in an efficent way. i only saw examples how to allow self-signed certificates.
I have a linux\java6 client that will authenticate to sharepoint2010 with NTLM and then send HTTP REST web services using Apache Commons HttpClient.
I can do this with NTLM , but I want to use the same REST API to access sharepoint 2010 that uses kerberos auth.
Any examples how to authenticate and send REST over HTTP with a kerberos sharepoint?
(preferably using HttpClient)
p.s.
I dont have access to sharepoint code, but i do have access to sharepoint admin configurations.
This is roughly how I authenticate with NTLM:
HttpClient httpClient = new HttpClient(new SimpleHttpConnectionManager(true));
AuthPolicy.registerAuthScheme(AuthPolicy.NTLM, JCIFS_NTLMScheme.class);
String localHostName = Inet4Address.getLocalHost().getHostName();
authscope = new AuthScope(uri.getHost(), AuthScope.ANY_PORT);
httpClient.getState().setCredentials(authscope,new NTCredentials(
getUsername(),getPassword(),localHostName,getDomain()));
// after the initial ntlm auth I can call my REST service with "httpClient.executeMethod"
int status = httpClient.executeMethod(new GetMethod(accessURI + "/sitecollection/info"));
Please confirm that your environment is correctly setup for Kerberos, this can be achieved by running kinit. If this fails you will need to ensure that your krb5.ini (windows) or krb5.conf (linux) are setup to point to your domain controller correctly.
Once you have confirmed that Kerberos is functional you can use the example code from HttpClient as pasted below.
Please note that there are many issues that can cause Kerberos to fail, such as time synchronisation, supported encryption types, trust relationships across domain forests and it's also worth ensuring that your client is on a seperate box to the server.
Here is the example code which is available in the HttpClient download, you will need to ensure your JAAS configuration and krb5.conf or ini are correct!
public class ClientKerberosAuthentication {
public static void main(String[] args) throws Exception {
System.setProperty("java.security.auth.login.config", "login.conf");
System.setProperty("java.security.krb5.conf", "krb5.conf");
System.setProperty("sun.security.krb5.debug", "true");
System.setProperty("javax.security.auth.useSubjectCredsOnly","false");
DefaultHttpClient httpclient = new DefaultHttpClient();
try {
httpclient.getAuthSchemes().register(AuthPolicy.SPNEGO, new SPNegoSchemeFactory());
Credentials use_jaas_creds = new Credentials() {
public String getPassword() {
return null;
}
public Principal getUserPrincipal() {
return null;
}
};
httpclient.getCredentialsProvider().setCredentials(
new AuthScope(null, -1, null),
use_jaas_creds);
HttpUriRequest request = new HttpGet("http://kerberoshost/");
HttpResponse response = httpclient.execute(request);
HttpEntity entity = response.getEntity();
System.out.println("----------------------------------------");
System.out.println(response.getStatusLine());
System.out.println("----------------------------------------");
if (entity != null) {
System.out.println(EntityUtils.toString(entity));
}
System.out.println("----------------------------------------");
// This ensures the connection gets released back to the manager
EntityUtils.consume(entity);
} finally {
// When HttpClient instance is no longer needed,
// shut down the connection manager to ensure
// immediate deallocation of all system resources
httpclient.getConnectionManager().shutdown();
}
}
}