KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > com > maverick > http > DigestAuthentication


1 /*
2  * SSL-Explorer
3  *
4  * Copyright (C) 2003-2006 3SP LTD. All Rights Reserved
5  *
6  * This program is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU General Public License
8  * as published by the Free Software Foundation; either version 2 of
9  * the License, or (at your option) any later version.
10  * This program is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13  * GNU General Public License for more details.
14  *
15  * You should have received a copy of the GNU General Public
16  * License along with this program; if not, write to the Free Software
17  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
18  */

19             
20 package com.maverick.http;
21
22 import java.io.IOException JavaDoc;
23 import java.util.Hashtable JavaDoc;
24 import java.util.StringTokenizer JavaDoc;
25
26 import com.maverick.crypto.digests.Hash;
27 import com.maverick.crypto.digests.MD5Digest;
28
29 /**
30  *
31  * @author Lee David Painter <a HREF="mailto:lee@3sp.com">&lt;lee@3sp.com&gt;</a>
32  */

33 public class DigestAuthentication extends HttpAuthenticator {
34
35     Hashtable JavaDoc params;
36
37     /**
38      * Hexa values used when creating 32 character long digest in HTTP
39      * DigestScheme in case of authentication.
40      *
41      * @see #encode(byte[])
42      */

43     private static final char[] HEXADECIMAL = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f' };
44
45     /** Whether the digest authentication process is complete */
46     private boolean complete;
47
48     // TODO: supply a real nonce-count, currently a server will interprete a
49
// repeated request as a replay
50
private static final String JavaDoc NC = "00000001"; // nonce-count is always 1 //$NON-NLS-1$
51
// //$NON-NLS-1$
52
private static final int QOP_MISSING = 0;
53     private static final int QOP_AUTH_INT = 1;
54     private static final int QOP_AUTH = 2;
55
56     private int qopVariant = QOP_MISSING;
57     private String JavaDoc cnonce;
58     boolean isAuthenticated = false;
59
60     public DigestAuthentication(String JavaDoc uri, String JavaDoc host, int port, boolean secure) {
61         super("Digest", uri, host, port, secure); //$NON-NLS-1$
62
}
63
64     public void setChallenge(String JavaDoc challenge) {
65         try {
66             params = ParameterParser.extractParams(challenge);
67
68             boolean unsupportedQop = false;
69             // qop parsing
70
String JavaDoc qop = (String JavaDoc) params.get("qop"); //$NON-NLS-1$
71
if (qop != null) {
72                 StringTokenizer JavaDoc tok = new StringTokenizer JavaDoc(qop, ","); //$NON-NLS-1$
73
while (tok.hasMoreTokens()) {
74                     String JavaDoc variant = tok.nextToken().trim();
75                     if (variant.equals("auth")) { //$NON-NLS-1$
76
qopVariant = QOP_AUTH;
77                         break; // that's our favourite, because auth-int is
78
// unsupported
79
} else if (variant.equals("auth-int")) { //$NON-NLS-1$
80
qopVariant = QOP_AUTH_INT;
81                     } else {
82                         unsupportedQop = true;
83                     }
84                 }
85             }
86
87             this.cnonce = createCnonce();
88         } catch (IOException JavaDoc ex) {
89         }
90     }
91
92     public boolean isStateless() {
93         return false;
94     }
95
96     /**
97      * authenticate
98      *
99      * @param request HttpRequest
100      * @throws IOException
101      * @todo Implement this com.maverick.proxy.http.HttpAuthenticator method
102      */

103     public void authenticate(HttpRequest request, HttpMethod method) throws IOException JavaDoc {
104         params.put("methodname", method.getName()); //$NON-NLS-1$
105
params.put("uri", method.getURI()); //$NON-NLS-1$
106

107         String JavaDoc serverDigest = createDigest(credentials.getUsername(), credentials.getPassword(), "US-ASCII"); //$NON-NLS-1$
108

109         request.setHeaderField(authorizationHeader, "Digest " //$NON-NLS-1$
110
+ createDigestHeader(credentials.getUsername(), serverDigest));
111     }
112
113     /**
114      * processResponse
115      *
116      * @param response HttpResponse
117      * @todo Implement this com.maverick.proxy.http.HttpAuthenticator method
118      */

119     public int processResponse(HttpResponse response) {
120         return (hasCompleted = response.getStatus() >= 200 && response.getStatus() < 400) ? AUTHENTICATION_COMPLETED
121             : AUTHENTICATION_FAILED;
122     }
123
124     private String JavaDoc createDigest(String JavaDoc uname, String JavaDoc pwd, String JavaDoc charset) throws IOException JavaDoc {
125
126         final String JavaDoc digAlg = "MD5"; //$NON-NLS-1$
127

128         // Collecting required tokens
129
String JavaDoc uri = (String JavaDoc) params.get("uri"); //$NON-NLS-1$
130
String JavaDoc realm = (String JavaDoc) params.get("realm"); //$NON-NLS-1$
131
String JavaDoc nonce = (String JavaDoc) params.get("nonce"); //$NON-NLS-1$
132
String JavaDoc qop = (String JavaDoc) params.get("qop"); //$NON-NLS-1$
133
String JavaDoc method = (String JavaDoc) params.get("methodname"); //$NON-NLS-1$
134
String JavaDoc algorithm = (String JavaDoc) params.get("algorithm"); //$NON-NLS-1$
135

136         // If an algorithm is not specified, default to MD5.
137
if (algorithm == null) {
138             algorithm = "MD5"; //$NON-NLS-1$
139
}
140
141         if (qopVariant == QOP_AUTH_INT) {
142             throw new IOException JavaDoc(Messages.getString("DigestAuthentication.unsupportedQop")); //$NON-NLS-1$
143
}
144
145         Hash hash = new Hash(new MD5Digest());
146
147         // 3.2.2.2: Calculating digest
148
StringBuffer JavaDoc tmp = new StringBuffer JavaDoc(uname.length() + realm.length() + pwd.length() + 2);
149         tmp.append(uname);
150         tmp.append(':');
151         tmp.append(realm);
152         tmp.append(':');
153         tmp.append(pwd);
154         // unq(username-value) ":" unq(realm-value) ":" passwd
155
String JavaDoc a1 = tmp.toString();
156         // a1 is suitable for MD5 algorithm
157
if (algorithm.equals("MD5-sess")) { //$NON-NLS-1$
158
// H( unq(username-value) ":" unq(realm-value) ":" passwd )
159
// ":" unq(nonce-value)
160
// ":" unq(cnonce-value)
161

162             hash.putBytes(a1.getBytes("US-ASCII")); //$NON-NLS-1$
163
String JavaDoc tmp2 = encode(hash.doFinal());
164             StringBuffer JavaDoc tmp3 = new StringBuffer JavaDoc(tmp2.length() + nonce.length() + cnonce.length() + 2);
165             tmp3.append(tmp2);
166             tmp3.append(':');
167             tmp3.append(nonce);
168             tmp3.append(':');
169             tmp3.append(cnonce);
170             a1 = tmp3.toString();
171         } else if (!algorithm.equals("MD5")) { //$NON-NLS-1$
172

173         }
174
175         hash.reset();
176         hash.putBytes(a1.getBytes("US-ASCII")); //$NON-NLS-1$
177
String JavaDoc md5a1 = encode(hash.doFinal());
178
179         String JavaDoc a2 = null;
180         if (qopVariant == QOP_AUTH_INT) {
181             // we do not have access to the entity-body or its hash
182
// TODO: add Method ":" digest-uri-value ":" H(entity-body)
183
} else {
184             a2 = method + ":" + uri; //$NON-NLS-1$
185
}
186
187         hash.reset();
188         hash.putBytes(a2.getBytes("US-ASCII")); //$NON-NLS-1$
189
String JavaDoc md5a2 = encode(hash.doFinal());
190
191         // 3.2.2.1
192
String JavaDoc serverDigestValue;
193         if (qopVariant == QOP_MISSING) {
194
195             StringBuffer JavaDoc tmp2 = new StringBuffer JavaDoc(md5a1.length() + nonce.length() + md5a2.length());
196             tmp2.append(md5a1);
197             tmp2.append(':');
198             tmp2.append(nonce);
199             tmp2.append(':');
200             tmp2.append(md5a2);
201             serverDigestValue = tmp2.toString();
202         } else {
203
204             String JavaDoc qopOption = getQopVariantString();
205             StringBuffer JavaDoc tmp2 = new StringBuffer JavaDoc(md5a1.length() + nonce.length() + NC.length() + cnonce.length()
206                 + qopOption.length() + md5a2.length() + 5);
207             tmp2.append(md5a1);
208             tmp2.append(':');
209             tmp2.append(nonce);
210             tmp2.append(':');
211             tmp2.append(NC);
212             tmp2.append(':');
213             tmp2.append(cnonce);
214             tmp2.append(':');
215             tmp2.append(qopOption);
216             tmp2.append(':');
217             tmp2.append(md5a2);
218             serverDigestValue = tmp2.toString();
219         }
220
221         hash.reset();
222         hash.putBytes(serverDigestValue.getBytes("US-ASCII")); //$NON-NLS-1$
223
String JavaDoc serverDigest = encode(hash.doFinal());
224
225         return serverDigest;
226     }
227
228     public static String JavaDoc createCnonce() {
229
230         String JavaDoc cnonce;
231         Hash hash = new Hash(new MD5Digest());
232
233         cnonce = Long.toString(System.currentTimeMillis());
234
235         hash.putBytes(cnonce.getBytes());
236         cnonce = encode(hash.doFinal());
237
238         return cnonce;
239     }
240
241     private static String JavaDoc encode(byte[] binaryData) {
242
243         if (binaryData.length != 16) {
244             return null;
245         }
246
247         char[] buffer = new char[32];
248         for (int i = 0; i < 16; i++) {
249             int low = (int) (binaryData[i] & 0x0f);
250             int high = (int) ((binaryData[i] & 0xf0) >> 4);
251             buffer[i * 2] = HEXADECIMAL[high];
252             buffer[(i * 2) + 1] = HEXADECIMAL[low];
253         }
254
255         return new String JavaDoc(buffer);
256     }
257
258     private String JavaDoc createDigestHeader(String JavaDoc uname, String JavaDoc digest) throws IOException JavaDoc {
259
260         StringBuffer JavaDoc sb = new StringBuffer JavaDoc();
261         String JavaDoc uri = (String JavaDoc) params.get("uri"); //$NON-NLS-1$
262
String JavaDoc realm = (String JavaDoc) params.get("realm"); //$NON-NLS-1$
263
String JavaDoc nonce = (String JavaDoc) params.get("nonce"); //$NON-NLS-1$
264
String JavaDoc nc = (String JavaDoc) params.get("nc"); //$NON-NLS-1$
265
String JavaDoc opaque = (String JavaDoc) params.get("opaque"); //$NON-NLS-1$
266
String JavaDoc response = digest;
267         String JavaDoc qop = (String JavaDoc) params.get("qop"); //$NON-NLS-1$
268
String JavaDoc algorithm = (String JavaDoc) params.get("algorithm"); //$NON-NLS-1$
269

270         sb.append("username=\"" + uname + "\"") //$NON-NLS-1$ //$NON-NLS-2$
271
.append(", realm=\"" + realm + "\"") //$NON-NLS-1$ //$NON-NLS-2$
272
.append(", nonce=\"" + nonce + "\"").append(", uri=\"" + uri + "\"") //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$
273
.append(", response=\"" + response + "\""); //$NON-NLS-1$ //$NON-NLS-2$
274
if (qopVariant != QOP_MISSING) {
275             sb.append(", qop=\"" + getQopVariantString() + "\"") //$NON-NLS-1$ //$NON-NLS-2$
276
.append(", nc=" + NC) //$NON-NLS-1$
277
.append(", cnonce=\"" + cnonce + "\""); //$NON-NLS-1$ //$NON-NLS-2$
278
}
279         if (algorithm != null) {
280             sb.append(", algorithm=\"" + algorithm + "\""); //$NON-NLS-1$ //$NON-NLS-2$
281
}
282         if (opaque != null) {
283             sb.append(", opaque=\"" + opaque + "\""); //$NON-NLS-1$ //$NON-NLS-2$
284
}
285         return sb.toString();
286     }
287
288     private String JavaDoc getQopVariantString() {
289         String JavaDoc qopOption;
290         if (qopVariant == QOP_AUTH_INT) {
291             qopOption = "auth-int"; //$NON-NLS-1$
292
} else {
293             qopOption = "auth"; //$NON-NLS-1$
294
}
295         return qopOption;
296     }
297
298     public String JavaDoc getInformation() {
299         return (String JavaDoc)params.get("realm");
300     }
301
302 }
303
Popular Tags