HTTP Digest Access Authentication using MD5 and HttpClient 4

| No TrackBacks
Dealing with HTTP's Digest authentication mechanism isn't too bad once you have the basic building blocks in place.  Luckily HttpClient 4 can automatically solve many types of authentication challenges for you, if used correctly.  Using HttpClient 4, I built an app that authenticates against a SOAP based web-service requiring WWW-Authenticate Digest authentication.  In a nutshell, the fundamental principal behind HTTP Digest authentication is simple:

  • The client asks for a page that requires authentication.
  • The server responds with an HTTP 401 response code, providing the authentication realm and a randomly-generated, single-use value called a "nonce".  The authentication "challenge" itself is encapsulated inside of the WWW-Authenticate HTTP response header.
  • The client "solves" the authentication challenge and a solution is sent back to the web-server via the HTTP Authorization header on a subsequent request.  The solution usually contains some type of MD5 hashed mess of your username, password, and "nonce".
  • Assuming the solution is acceptable the server responds with a successful type response, usually an HTTP 200 OK.

Here's a sample with a bit of pseudo code mixed in (so, you get the idea):

// A org.apache.http.impl.auth.DigestScheme instance is
// what will process the challenge from the web-server
final DigestScheme md5Auth = new DigestScheme();

// This should return an HTTP 401 Unauthorized with
// a challenge to solve.
final HttpResponse authResponse =
doPost(url, postBody, contentType);

// Validate that we got an HTTP 401 back
if(authResponse.getStatusLine().getStatusCode() ==
HttpStatus.SC_UNAUTHORIZED) {
if(authResponse.containsHeader("WWW-Authenticate")) {
// Get the challenge.
final Header challenge =
authResponse.getHeaders("WWW-Authenticate")[0];
// Solve it.
md5Auth.processChallenge(challenge);
// Generate a solution Authentication header using your
// username and password.
final Header solution = md5Auth.authenticate(
new UsernamePasswordCredentials(username, password),
new BasicHttpRequest(HttpPost.METHOD_NAME,
new URL(url).getPath()));
// Do another POST, but this time include the solution
// Authentication header as generated by HttpClient.
final HttpResponse goodResponse =
doPost(url, postBody, contentType, solution);
// ... do something useful with goodResponse, which assuming
// your credentials were valid, should contain the data you
// requested.
} else {
throw new Error("Web-service responded with Http 401, " +
"but didn't send us a usable WWW-Authenticate header.");
}
} else {
throw new Error("Didn't get an Http 401 " +
"like we were expecting.");
}

Enjoy.

Did You Find this Helpful?

Did you find this post helpful, or at least, interesting?

  

About Mark

A Silicon Valley native, Mark Kolich is a full-time Software Engineer and a consultant for hire. A web technologies expert, his current focus is on building powerful and robust cloud-driven web-applications using Java, PHP, Perl, AJAX, DHTML, CSS, and JavaScript. His favorite programming languages are PHP, Java and JavaScript. He uses Linux, enjoys biking to work, loves building great software, and always writes elegant, readable, and maintainable code.

No TrackBacks

No trackbacks attached to this entry.

Twitter (@markkolich)

Translate

About this Entry

This page contains a single entry by Mark Kolich published on May 4, 2010 1:30 PM.

Pingdom Incorrectly Reports Page Load Time was the previous entry in this blog.

Formatting a Java Date into a Specific TimeZone and Conversion Between TimeZone's is the next entry in this blog.

Find recent content on the main index or look in the archives to find all content.