Digest authentication using apache httpclient - java

I am implementing a simple (not preemptive, not through proxy) digest authentication using apache HttpClient5. Documentation is quite sparse, but it should be fairly simple.
However, I keep getting 401 as a response, and I am not clear on how I can establish if this is because of implementation mistakes on my part, or if the 401 is genuine. The used credentials are definately correct, I checked.
So my questions are:
Is this code basically correct or am I missing something (do I have to process the challenge myself for example. I am assuming the httpClient does this for me).
If processing the challenge fails because it is done incorrectly, should I not get some more information in the log other than a 401.
digest{} authentication error: missing realm: Is this normal in the first fase of the authencation process, or should I preemptively provide the realm? I am assuming the httpClient gets this from the challenge from the server and can provide it in the second call. On the other hand it is marked as 'severe'...
.. this is the code segment ..
.. I cleaned up the code a bit so it only represents the lines for the digest authentication.
.. maybe the closeconnection statement wreaks havock?
HttpRequestBase request = request = new HttpGet(params.getRequestUrl());
// .....
// then setting requestconfig, headers, body, contenttype etc.
// .....
HttpClientContext localContext = HttpClientContext.create();
// set authentication (just a POJO with a username and a password)
final HttpBasicAuthentication authentication = params.getAuthentication();
URI uri = request.getURI();
String host = uri.getHost();
int port = uri.getPort();
String scheme = uri.getScheme();
HttpHost target = new HttpHost(host,port,scheme);
char[] password = authentication.getPassword().toCharArray();
final org.apache.hc.client5.http.auth.Credentials creds = new org.apache.hc.client5.http.auth.UsernamePasswordCredentials(authentication.getUsername(), password);
BasicCredentialsProvider cP = new BasicCredentialsProvider();
cP.setCredentials(org.apache.hc.client5.http.auth.AuthScope.ANY, creds);
localContext.setCredentialsProvider(cP);
AuthCache authCache = new BasicAuthCache();
DigestScheme digestScheme = new DigestScheme();
authCache.put(target, digestScheme);
localContext.setAuthCache(authCache);
// Tell the HTTP server that we will close the connection after the response
request.addHeader("Connection", "close");
// http response
final HttpServiceResponse response = new HttpServiceResponse();
// Execute the method.
CloseableHttpResponse closeableResponse = httpClient5.execute(request, localContext);
Logging...
... sorry... I had to remove the logging. because of security conserns.

Related

Passing username to a HttpGet request

I need to access an API which works like this:
curl https://api.com/ratings/v1/ -u [your token here]:
The token is the username that should be passed to the HttpGet request. I am trying to do the same in the following way using java:
CredentialsProvider credentialsProvider = new BasicCredentialsProvider();
credentialsProvider.setCredentials(AuthScope.ANY, new UsernamePasswordCredentials("usrname", "passwrd"));
HttpHost proxy = new HttpHost("proxy.com", 8080, "http");
HttpClient httpClient = HttpClients.custom().setProxy(proxy).setDefaultCredentialsProvider(credentialsProvider).build();
HttpGet toesGet = new HttpGet("https://api.com/ratings/v1/");
toesGet.setHeader("Accept", "Application/Json");
toesGet.addHeader("Username", "[your token here]");
try {
HttpResponse toes = httpClient.execute(toesGet);
System.out.println(toes.getStatusLine());
System.out.println(toes.getEntity().toString());
} catch (Exception e) {
e.printStackTrace();
}
I am a behind proxy, so I am creating a HttpHost with the proxy details, setting the proxy for the HttpClient object and passing the credentials for proxy authentication using credentialsProvider in the following lines of code:
HttpHost proxy = new HttpHost("proxy.com", 8080, "http");
HttpClient httpClient = HttpClients.custom().setProxy(proxy).setDefaultCredentialsProvider(credentialsProvider).build();
I am passing the username to the HttpGet by adding the header like this:
toesGet.addHeader("Username", "[your token here]");
when I run the code, I get this response: HTTP/1.1 401 UNAUTHORIZED
This indicates that I am not passing the username to the HttpGet request in the right way(Or does this mean something else?). So what's the right way of passing the username to the get request?
Any help would be appreciated, Thank you!
Note: The usrname and passwrd I set in the credentialsProvider are for the proxy authentication. They have nothing to do with the HttpGet request itself. the token I need to pass is different from the usrname provided in the credentials.
I guess, your server uses Basic Authentication, then you need to add the "Authorization" header instead of "Username":
String user = "[your token here]";
String pwd = ""; // blank
toesGet.addHeader("Authorization", "Basic " + Base64.encodeToString((user + ":" + pwd).getBytes(), Base64.NO_WRAP));
or if your token contains user and pwd, then try it like that:
String token = "[your token here]";
toesGet.addHeader("Authorization", "Basic " + Base64.encodeToString(token.getBytes(), Base64.NO_WRAP));
I haven’t used Apache HttpComponents, but my understanding is that you have to set the credentials for specific hosts:
CredentialsProvider credentialsProvider = new BasicCredentialsProvider();
HttpHost proxy = new HttpHost("proxy.com", 8080, "http");
credentialsProvider.setCredentials(new AuthScope(proxy),
new UsernamePasswordCredentials("usrname", "passwrd"));
credentialsProvider.setCredentials(new AuthScope("api.com", AuthScope.ANY_PORT),
new UsernamePasswordCredentials("apiuser", "apipassword"));
Note: Do not actually type "apiuser" or "apipassword" in your code. I am showing those only as placeholders. Replace them with the correct user and password for accessing api.com. (I am pointing this out because, based on the code in your question, I’m not sure if you understood that you were not supposed to use the literal string "[your token here]".)

Solr Import via ModifiableSolrParams with security credentials

I'm trying to execute import via java using ModifiableSolrParams:
Can you please point me on the right direction or reference on how to add security credentials (username / password) to trigger the import.
Current code
SolrServer server = new HttpSolrServer(baseurl);
ModifiableSolrParams params = new ModifiableSolrParams();
params.set("command", "full-import");
params.set("clean", "true");
params.set("commit", "true");
QueryRequest request = new QueryRequest(params);
request.setPath("/dataimport");
server.request(request);
You need to add HttpRequestInterceptor to you HttpServer. This interceptor will be able to add authorization header to every your request.
For cloud Solr the util class that allow to do this is HttpClientUtil. You can start from this class, or check where in HttpSolrServer is actually HttpClient present.
I veered away from Solrj and went with this approach instead.
HttpClient Client = new DefaultHttpClient();
String userpass = usr + ":" + pwd;
HttpPost httpGet = new HttpPost(dataimport_cmd);
String encoding =
DatatypeConverter.printBase64Binary(userpass.getBytes("UTF-8"));
httpGet.setHeader("Authorization", "Basic " + encoding);
Client.execute(httpGet);

HttpClient exception: java.lang.IllegalArgumentException: host parameter is null while sending a request using using GetMethod()

First of all my question is not duplicate to others. I am querying my question from last 2 days but didn't found any solution.The solutions I found were for the PostMethod but I have issue is GetMethod.
Due to some confidential data issue I cannot share the exact endpointUrl but I am giving a dummy endpointUrl.
below code was working fine and returning a csv file until the endPointUrl was https://abcxyz.com/incident.do?CSV (abcxyz is the only dummy part here), :
HttpClient client = new HttpClient();
client.getParams().setAuthenticationPreemptive(true);
Credentials creds = new UsernamePasswordCredentials(userName, password);
client.getState().setCredentials(AuthScope.ANY, creds);
GetMethod method=new GetMethod(endPointUrl);
int status = client.executeMethod(method);
Then I came in need to change endPointUrl which became https://abcxyz.com/incident.do?CSV&sys_param_query=active=true^sys_updated_onBETWEENjavascript:gs.dateGenerate(%272016-11-20%27,%2700:10:00%27)#javascript:gs.dateGenerate(%272016-11-24%27,%2712:59:59%27) and then the above code started giving java.lang.IllegalArgumentException: Invalid uri exception. Then I googled this and found that for larger urls or complex url we need to encode(UTF-8) them correctly using URLEncoder.encode(endPointUrl, UTF-8). And the code became :
HttpClient client = new HttpClient();
client.getParams().setAuthenticationPreemptive(true);
Credentials creds = new UsernamePasswordCredentials(userName, password);
client.getState().setCredentials(AuthScope.ANY, creds);
GetMethod method=new GetMethod(URLEncoder.encode(endPointUrl, UTF-8));
int status = client.executeMethod(method);
Now this code started complaining about java.lang.IllegalArgumentException: host parameter is null
Again I tried to solve this and came up with some solution as below:
HttpClient client = new HttpClient();
client.getParams().setAuthenticationPreemptive(true);
Credentials creds = new UsernamePasswordCredentials(userName, password);
client.getState().setCredentials(AuthScope.ANY, creds);
HostConfiguration hf=new HostConfiguration();
hf.setHost("ven01623.service-now.com", 443);
GetMethod method=new GetMethod(URLEncoder.encode(endPointUrl, UTF-8));
method.setHostConfiguration(hf);
int status = client.executeMethod(method);
But now, it is complaining org.apache.commons.httpclient.URIException: invalid port number and I have no clue what to do now because it is a https connection to which has port number 443.
I am having one more question here, https://abcxyz.com/incident.do?CSV is running fine under GetMethod method=new GetMethod(endPointUrl); then why is it complaining host parameter is null under GetMethod method=new GetMethod(URLEncoder.encode(endPointUrl, UTF-8)); ?
In Very much need of help.
Thanks

Java httpClient 4.3.6 basic Authentication with complete URI and scheme

What I want:
Send a GET request with a preemtive bassic authentication.
The request looks about like this:
<startURL>/app/process?job=doSomething&param=value1,value2
whereas startURL is always a https link depends on the enviroment.
Looks something like this:
https://testABC.com
https://prodABC.com
startURL is also placed in a properties file as is for the diffrent enviroments.
What I looked into:
http://www.baeldung.com/httpclient-4-basic-authentication
http://www.java-tips.org/other-api-tips/httpclient/how-to-use-basic-authentication.html
http://hc.apache.org/httpcomponents-client-ga/httpclient/examples/org/apache/http/examples/client/ClientPreemptiveBasicAuthentication.java
http://hc.apache.org/httpcomponents-client-ga/tutorial/html/authentication.html
It all contains a
HttpHost targetHost = new HttpHost("hostname", portnumber, "scheme");
Which is what I am having trouble with. This method is also the only one that lets you specify the scheme as "https".
One issue is, hat I don't know the portnumber. I think (?) I probably could just specify -1 for the default port, to make it work, but even aside that I also don't have the hostname, only the above mentioned startURL. I don't really want to parse this extra each time, while I also don't really want to add another property, just for the hostname.
I digged around and found this snippet, which looks like just what I want:
HttpClient httpClient = new DefaultHttpClient();
HttpGet httpGet = new HttpGet("http://foo.com/bar");
httpGet.addHeader(BasicScheme.authenticate(
new UsernamePasswordCredentials("user", "password"),
"UTF-8", false));
HttpResponse httpResponse = httpClient.execute(httpGet);
HttpEntity responseEntity = httpResponse.getEntity();
from HTTP requests with basic authentication
It gives the complete request URL and simply adds the basic header and does not need any port specified. Only that this is now deprecated since Version 4.2:
Deprecated. (4.2) Use ContextAwareAuthScheme.authenticate( Credentials, HttpRequest, org.apache.http.protocol.HttpContext)
I couldn't find a single example for this method to return the basic auth header. It also wants a context as a parameter, which above snipped doesn't have. I really have no real clue how this is supposed to be used.
So, what i want to know concretely:
I just want to set up a request with the complete link, that contains all that there is, like:
https://testABC.com/app/process?job=doSomething&param=value1,value2
and just give this as a parameter for a request that does preemptive basic authentication.
Is there any way to do this without digging up the deprecated methods and how does it look like?
I ran into the same problem as yours.
What worked for me is the following:
UsernamePasswordCredentials creds = new UsernamePasswordCredentials("user", "12345");
HttpGet get = new HttpGet("https://foo.bar.com/rest");
HttpHost targetHost = new HttpHost("foo.bar.com", 443, "https");
CredentialsProvider credsProvider = new BasicCredentialsProvider();
credsProvider.setCredentials(
new AuthScope(targetHost.getHostName(), targetHost.getPort()),
creds);
credsProvider.setCredentials(AuthScope.ANY,creds);
// Create AuthCache instance
AuthCache authCache = new BasicAuthCache();
// Generate BASIC scheme object and add it to the local auth cache
BasicScheme basicAuth = new BasicScheme();
authCache.put(targetHost, basicAuth);
// Add AuthCache to the execution context
HttpClientContext context = HttpClientContext.create();
context.setCredentialsProvider(credsProvider);
context.setAuthCache(authCache);
HttpResponse response = client.execute(targetHost, get, context);
And I found this solution on: HttpClientBuilder basic auth
In the end I wound up writing the header manually on my own and sending things with that:
String header = "Basic ";
String headerValue = "username" + ":" + "password";
String encodedHeaderValue = Base64.encodeBase64String(headerValue.getBytes());
String headerBasic = header + encodedHeaderValue;
Header authHeader = new BasicHeader("Authorization", headerBasic);
ArrayList<Header> headers = new ArrayList<Header>();
headers.add(authHeader);
ArrayList<Header> headers = getHttpHeaders();
HttpClient client = HttpClients.custom().setDefaultHeaders(headers).build();
HttpUriRequest request = RequestBuilder.get().setUri(uri).build();
HttpResponse response = client.execute(request);
int responseCode = response.getStatusLine().getStatusCode();

Twitter API status update always returns "Incorrect signature"

I'm using Signpost as OAuth implementation for posting to Twitter. And implemented the GoogleAppEngineOAuthConsumer and GoogleAppEngineOAuthProvider classes, but since they're pretty trivial, so I'm not providing their sources here (yet).
Here's my authentication part, which seems to work just fine.
LoginServlet.java:
// fetching the request token
OAuthConsumer consumer = new GoogleAppEngineOAuthConsumer(CONSUMER_KEY, CONSUMER_SECRET);
OAuthProvider provider = new GoogleAppEngineOAuthProvider(REQUEST_TOKEN_URL, ACCESS_TOKEN_URL, AUTHORIZATION_URL);
String redirectUrl = provider.retrieveRequestToken(consumer, CALLBACK_URL);
// cache the request token and request token secret
response.sendRedirect(redirectUrl);
CallbackServlet.java
// fetching the access token
String verifier = (String) req.getParameter("oauth_verifier");
// retrieve request token and request token secret from cache
OAuthConsumer consumer = new GoogleAppEngineOAuthConsumer(CONSUMER_KEY, CONSUMER_SECRET);
OAuthProvider provider = new GoogleAppEngineOAuthProvider(REQUEST_TOKEN_URL,
consumer.setTokenWithSecret(token, tokenSecret);
provider.setOAuth10a(true);
provider.retrieveAccessToken(consumer, verifier);
// store access token and access token secret
And here's the actual problematic part.
TweetServlet.java
OAuthConsumer consumer = new GoogleAppEngineOAuthConsumer(CONSUMER_KEY, CONSUMER_SECRET);
// retrieve access token and access token secret from storage
consumer.setTokenWithSecret(accessToken, accessTokenSecret);
final HTTPRequest updateStatus = new HTTPRequest(new URL("http://api.twitter.com/1/statuses/update.json"), HTTPMethod.POST);
updateStatus.setPayload(("status=" + URLEncoder.encode(message, "UTF-8")).getBytes());
consumer.sign(updateStatus);
logger.debug(new String(URLFetchServiceFactory.getURLFetchService().fetch(updateStatus).getContent()));
Each and every time it results: {"request":"/1/statuses/update.json","error":"Incorrect signature"}.
I was able to solve this by myself. The problem was that I wasn't setting a Content-Type header to the request, so the signing didn't sign the parameters and it resulted the invalid signature. Once I set it to application/x-www-form-urlencoded it started working.
final HTTPRequest updateStatus = new HTTPRequest(new URL("http://api.twitter.com/1/statuses/update.json"), HTTPMethod.POST);
updateStatus.addHeader(new HTTPHeader("Content-Type", "application/x-www-form-urlencoded"));

Categories

Resources