Implementing HTTP Digest Authentication in Java

I recently had to implement my own version of RFC 2617 which addresses the digest authentication in http protocol. This RFC is an improvement over RFC 2069 which was the previous version of Digest Access authentication. However RFC 2617 adds some additional security features such as quality of protection (qop) to try to counter re-play attacks.

Generally in java world, people don’t implement this themselves, most commonly used HTTP authentication is container managed. So you just configure your web.xml to identify that you want to use BASIC or DIGEST method, and then you configure a JNDI or other data-sources where the container picks up the passwords from, and that’s it.

Most people don’t have to write any code. The container managed Digest Access Authentication is pretty limited. It doesn’t allow you too much room to customize the behaviour, for example if you want to filter by IP addresses and only apply Digest Authentication to specific IP addresses only. Most applications don’t have these kind of customized requirements, however some do.

Since Digest Authentication is not that widely used yet, its hard to find a good implementation of it. Even Tomcat up-till 6.0 has not supported for example a pre-emptive digest authentication (I heard in Tomcat 7.x it’s supported but i haven’t checked it out yet). Most clients at the time of this writing have broken digest auth support (cURL comes to mind)

So when i needed to implement this in a specific way i had to write my own implementation of the RFC.  i am just blogging about it to help you get it done faster if you have such a need.

Lets start with a brief introduction Here is how a typical HTTP Digest Auth would work. As you know in HTTP Digest Auth , unlike the Basic Authentication, no password is every exchanged between server and the client, instead the server sends a challenge to the client, the client sends back a encoded response based on username, password and server challenge, and the server then verifies if the client’s response is correct according to the challenge

1) Client would request a resource i.e.

GET /HTTPDigestServer/HttpDigestAuth?username=usm

2) Server would send back an HTTP 401 indicating it’s a protected resource and needs authorization. In addition to sending back a 401 the Server will include an HTTP header called WWW-Authenticate, in this header there will be a field called “nonce” which basically is a challenge from the server. In addition to nonce , server optionally include a field called  qop, which basically determines which algorithm will be used by client to calculate the response.

The possible values of qop are auth and auth-int. Server can also decide to not include this field, in which case the response will be calculated by the client in the old RFC 2069 style.

HTTP/1.1 401 Unauthorized
Server: Apache-Coyote/1.1
WWW-Authenticate: Digest realm="abc.com",qop=auth,nonce="e986ce80f6d95a4cd4958d78e381fac0",opaque="b7d414d7663f03f9aa8eccf7204a0d43"
Content-Type: text/html;charset=utf-8
Content-Length: 954
Date: Mon, 11 Jun 2012 18:05:09 GMT
<html>
 <head>
 <title>Apache Tomcat/6.0.18 - Error report</title>
 <style>
 <!--H1 {font-family:Tahoma,Arial,sans-serif;color:white;background-color:#525D76;font-size:22px;} 
H2 {font-family:Tahoma,Arial,sans-serif;color:white;background-color:#525D76;font-size:16px;} 
H3 {font-family:Tahoma,Arial,sans-serif;color:white;background-color:#525D76;font-size:14px;} 
BODY {font-family:Tahoma,Arial,sans-serif;color:black;background-color:white;} 
B {font-family:Tahoma,Arial,sans-serif;color:white;background-color:#525D76;} 
P {font-family:Tahoma,Arial,sans-serif;background:white;color:black;font-size:12px;}
A {color : black;}A.name {color : black;}HR {color : #525D76;}-->
</style> 
 </head>
 <body>
 <h1>HTTP Status 401 - </h1>
 <HR size="1" noshade="noshade">
 <p>
 <b>type</b> Status report
 </p>
 <p>
 <b>message</b> 
 <u></u>
 </p>
 <p>
 <b>description</b> 
 <u>This request requires HTTP authentication ().</u>
 </p>
 <HR size="1" noshade="noshade">
 <h3>Apache Tomcat/6.0.18</h3>
 </body>
 </html>

3) Client will then calculate a few hashes, combine them to create another hash called “response” this will be sent in a HTTP header called Authorization along with some other fields for example

GET /HTTPDigestServer/HttpDigestAuth?username=usm HTTP/1.1
Host: 127.0.0.1:8888
Connection: keep-alive
Authorization: Digest username="usm", realm="abc.com", nonce="464586d93e858f45e59b4cb8e83ce89f", 
uri="/HTTPDigestServer/HttpDigestAuth?username=usm",
response="598efaca64f9e7f02d92a13c50e74ad0", opaque="9dfa6fe2f0325895ece2bbab4a4837bd", 
qop=auth, nc=00000003, cnonce="c4020839a1c2ccb6"
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_7_4) AppleWebKit/536.5 (KHTML, like Gecko) Chrome/19.0.1084.54 Safari/536.5
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Encoding: gzip,deflate,sdch
Accept-Language: en-US,en;q=0.8
Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.3
Cookie: JSESSIONID=272EAF671C64242C240D2D08F690839C

The server then verifies if the response value is correct or not. You can read the details of how the calculations are done on the nice wikipedia article here.

You can find below a sample Java Servlet which does the server side work of digest authentication. This servlet supports the qop = auth, auth-int and also not specifying the qop.

It also supports pre-emptive digest authentication which allows you to use a nonce for upto a minute, without having to re-authenticate, this can save you network traffic even though it can be a bad idea at times.

Also the strength of the HTTP digest authentication is how strong your nonce calculation is, for this sample implementation i am using a very simple nonce generation scheme. You can also download the code below as the java file here, the gist link to the file is https://gist.github.com/2912088

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s