View Javadoc
1   /*
2    * ====================================================================
3    * Licensed to the Apache Software Foundation (ASF) under one
4    * or more contributor license agreements.  See the NOTICE file
5    * distributed with this work for additional information
6    * regarding copyright ownership.  The ASF licenses this file
7    * to you under the Apache License, Version 2.0 (the
8    * "License"); you may not use this file except in compliance
9    * with the License.  You may obtain a copy of the License at
10   *
11   *   http://www.apache.org/licenses/LICENSE-2.0
12   *
13   * Unless required by applicable law or agreed to in writing,
14   * software distributed under the License is distributed on an
15   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
16   * KIND, either express or implied.  See the License for the
17   * specific language governing permissions and limitations
18   * under the License.
19   * ====================================================================
20   *
21   * This software consists of voluntary contributions made by many
22   * individuals on behalf of the Apache Software Foundation.  For more
23   * information on the Apache Software Foundation, please see
24   * <http://www.apache.org/>.
25   *
26   */
27  package org.apache.http.impl.auth;
28  
29  import java.io.ByteArrayInputStream;
30  import java.io.ByteArrayOutputStream;
31  import java.io.IOException;
32  import java.io.ObjectInputStream;
33  import java.io.ObjectOutputStream;
34  import java.security.MessageDigest;
35  import java.util.HashMap;
36  import java.util.Map;
37  
38  import org.apache.http.Header;
39  import org.apache.http.HeaderElement;
40  import org.apache.http.HttpEntityEnclosingRequest;
41  import org.apache.http.HttpRequest;
42  import org.apache.http.auth.AUTH;
43  import org.apache.http.auth.AuthScheme;
44  import org.apache.http.auth.AuthenticationException;
45  import org.apache.http.auth.Credentials;
46  import org.apache.http.auth.MalformedChallengeException;
47  import org.apache.http.auth.UsernamePasswordCredentials;
48  import org.apache.http.entity.InputStreamEntity;
49  import org.apache.http.entity.StringEntity;
50  import org.apache.http.message.BasicHeader;
51  import org.apache.http.message.BasicHeaderValueParser;
52  import org.apache.http.message.BasicHttpEntityEnclosingRequest;
53  import org.apache.http.message.BasicHttpRequest;
54  import org.apache.http.protocol.BasicHttpContext;
55  import org.apache.http.protocol.HTTP;
56  import org.apache.http.protocol.HttpContext;
57  import org.junit.Assert;
58  import org.junit.Test;
59  
60  /**
61   * Test Methods for DigestScheme Authentication.
62   */
63  public class TestDigestScheme {
64  
65      @Test(expected=MalformedChallengeException.class)
66      public void testDigestAuthenticationEmptyChallenge1() throws Exception {
67          final Header authChallenge = new BasicHeader(AUTH.WWW_AUTH, "Digest");
68          final AuthScheme authscheme = new DigestScheme();
69          authscheme.processChallenge(authChallenge);
70      }
71  
72      @Test(expected=MalformedChallengeException.class)
73      public void testDigestAuthenticationEmptyChallenge2() throws Exception {
74          final Header authChallenge = new BasicHeader(AUTH.WWW_AUTH, "Digest ");
75          final AuthScheme authscheme = new DigestScheme();
76          authscheme.processChallenge(authChallenge);
77      }
78  
79      @Test
80      public void testDigestAuthenticationWithDefaultCreds() throws Exception {
81          final String challenge = "Digest realm=\"realm1\", nonce=\"f2a3f18799759d4f1a1c068b92b573cb\"";
82          final Header authChallenge = new BasicHeader(AUTH.WWW_AUTH, challenge);
83          final HttpRequest request = new BasicHttpRequest("Simple", "/");
84          final Credentials cred = new UsernamePasswordCredentials("username","password");
85          final DigestScheme authscheme = new DigestScheme();
86          final HttpContext context = new BasicHttpContext();
87          authscheme.processChallenge(authChallenge);
88          final Header authResponse = authscheme.authenticate(cred, request, context);
89          Assert.assertTrue(authscheme.isComplete());
90          Assert.assertFalse(authscheme.isConnectionBased());
91  
92          final Map<String, String> table = parseAuthResponse(authResponse);
93          Assert.assertEquals("username", table.get("username"));
94          Assert.assertEquals("realm1", table.get("realm"));
95          Assert.assertEquals("/", table.get("uri"));
96          Assert.assertEquals("f2a3f18799759d4f1a1c068b92b573cb", table.get("nonce"));
97          Assert.assertEquals("e95a7ddf37c2eab009568b1ed134f89a", table.get("response"));
98      }
99  
100     @Test
101     public void testDigestAuthentication() throws Exception {
102         final String challenge = "Digest realm=\"realm1\", nonce=\"f2a3f18799759d4f1a1c068b92b573cb\"";
103         final Header authChallenge = new BasicHeader(AUTH.WWW_AUTH, challenge);
104         final HttpRequest request = new BasicHttpRequest("Simple", "/");
105         final Credentials cred = new UsernamePasswordCredentials("username","password");
106         final DigestScheme authscheme = new DigestScheme();
107         final HttpContext context = new BasicHttpContext();
108         authscheme.processChallenge(authChallenge);
109         final Header authResponse = authscheme.authenticate(cred, request, context);
110 
111         final Map<String, String> table = parseAuthResponse(authResponse);
112         Assert.assertEquals("username", table.get("username"));
113         Assert.assertEquals("realm1", table.get("realm"));
114         Assert.assertEquals("/", table.get("uri"));
115         Assert.assertEquals("f2a3f18799759d4f1a1c068b92b573cb", table.get("nonce"));
116         Assert.assertEquals("e95a7ddf37c2eab009568b1ed134f89a", table.get("response"));
117     }
118 
119     @Test
120     public void testDigestAuthenticationInvalidInput() throws Exception {
121         final String challenge = "Digest realm=\"realm1\", nonce=\"f2a3f18799759d4f1a1c068b92b573cb\"";
122         final Header authChallenge = new BasicHeader(AUTH.WWW_AUTH, challenge);
123         final HttpRequest request = new BasicHttpRequest("Simple", "/");
124         final Credentials cred = new UsernamePasswordCredentials("username","password");
125         final DigestScheme authscheme = new DigestScheme();
126         final HttpContext context = new BasicHttpContext();
127         authscheme.processChallenge(authChallenge);
128         try {
129             authscheme.authenticate(null, request, context);
130             Assert.fail("IllegalArgumentException should have been thrown");
131         } catch (final IllegalArgumentException ex) {
132         }
133         try {
134             authscheme.authenticate(cred, null, context);
135             Assert.fail("IllegalArgumentException should have been thrown");
136         } catch (final IllegalArgumentException ex) {
137         }
138     }
139 
140     @Test
141     public void testDigestAuthenticationOverrideParameter() throws Exception {
142         final String challenge = "Digest realm=\"realm1\", nonce=\"f2a3f18799759d4f1a1c068b92b573cb\"";
143         final Header authChallenge = new BasicHeader(AUTH.WWW_AUTH, challenge);
144         final HttpRequest request = new BasicHttpRequest("Simple", "/");
145         final Credentials cred = new UsernamePasswordCredentials("username","password");
146         final DigestScheme authscheme = new DigestScheme();
147         final HttpContext context = new BasicHttpContext();
148         authscheme.processChallenge(authChallenge);
149         authscheme.overrideParamter("realm", "other realm");
150         final Header authResponse = authscheme.authenticate(cred, request, context);
151 
152         final Map<String, String> table = parseAuthResponse(authResponse);
153         Assert.assertEquals("username", table.get("username"));
154         Assert.assertEquals("other realm", table.get("realm"));
155         Assert.assertEquals("/", table.get("uri"));
156         Assert.assertEquals("f2a3f18799759d4f1a1c068b92b573cb", table.get("nonce"));
157         Assert.assertEquals("3f211de10463cbd055ab4cd9c5158eac", table.get("response"));
158     }
159 
160     @Test
161     public void testDigestAuthenticationWithSHA() throws Exception {
162         final String challenge = "Digest realm=\"realm1\", " +
163                 "nonce=\"f2a3f18799759d4f1a1c068b92b573cb\", " +
164                 "algorithm=SHA";
165         final Header authChallenge = new BasicHeader(AUTH.WWW_AUTH, challenge);
166         final HttpRequest request = new BasicHttpRequest("Simple", "/");
167         final Credentials cred = new UsernamePasswordCredentials("username","password");
168         final HttpContext context = new BasicHttpContext();
169         final DigestScheme authscheme = new DigestScheme();
170         authscheme.processChallenge(authChallenge);
171         final Header authResponse = authscheme.authenticate(cred, request, context);
172 
173         final Map<String, String> table = parseAuthResponse(authResponse);
174         Assert.assertEquals("username", table.get("username"));
175         Assert.assertEquals("realm1", table.get("realm"));
176         Assert.assertEquals("/", table.get("uri"));
177         Assert.assertEquals("f2a3f18799759d4f1a1c068b92b573cb", table.get("nonce"));
178         Assert.assertEquals("8769e82e4e28ecc040b969562b9050580c6d186d", table.get("response"));
179     }
180 
181     @Test
182     public void testDigestAuthenticationWithQueryStringInDigestURI() throws Exception {
183         final String challenge = "Digest realm=\"realm1\", nonce=\"f2a3f18799759d4f1a1c068b92b573cb\"";
184         final Header authChallenge = new BasicHeader(AUTH.WWW_AUTH, challenge);
185         final HttpRequest request = new BasicHttpRequest("Simple", "/?param=value");
186         final Credentials cred = new UsernamePasswordCredentials("username","password");
187         final HttpContext context = new BasicHttpContext();
188         final DigestScheme authscheme = new DigestScheme();
189         authscheme.processChallenge(authChallenge);
190         final Header authResponse = authscheme.authenticate(cred, request, context);
191 
192         final Map<String, String> table = parseAuthResponse(authResponse);
193         Assert.assertEquals("username", table.get("username"));
194         Assert.assertEquals("realm1", table.get("realm"));
195         Assert.assertEquals("/?param=value", table.get("uri"));
196         Assert.assertEquals("f2a3f18799759d4f1a1c068b92b573cb", table.get("nonce"));
197         Assert.assertEquals("a847f58f5fef0bc087bcb9c3eb30e042", table.get("response"));
198     }
199 
200     @Test
201     public void testDigestAuthenticationWithMultipleRealms() throws Exception {
202         final String challenge1 = "Digest realm=\"realm1\", nonce=\"abcde\"";
203         final String challenge2 = "Digest realm=\"realm2\", nonce=\"123546\"";
204         final Credentials cred = new UsernamePasswordCredentials("username","password");
205         final Credentials cred2 = new UsernamePasswordCredentials("uname2","password2");
206 
207         Header authChallenge = new BasicHeader(AUTH.WWW_AUTH, challenge1);
208         final HttpRequest request = new BasicHttpRequest("Simple", "/");
209         final HttpContext context = new BasicHttpContext();
210         final DigestScheme authscheme = new DigestScheme();
211         authscheme.processChallenge(authChallenge);
212         Header authResponse = authscheme.authenticate(cred, request, context);
213 
214         Map<String, String> table = parseAuthResponse(authResponse);
215         Assert.assertEquals("username", table.get("username"));
216         Assert.assertEquals("realm1", table.get("realm"));
217         Assert.assertEquals("/", table.get("uri"));
218         Assert.assertEquals("abcde", table.get("nonce"));
219         Assert.assertEquals("786f500303eac1478f3c2865e676ed68", table.get("response"));
220 
221         authChallenge = new BasicHeader(AUTH.WWW_AUTH, challenge2);
222         final DigestScheme authscheme2 = new DigestScheme();
223         authscheme2.processChallenge(authChallenge);
224         authResponse = authscheme2.authenticate(cred2, request, context);
225 
226         table = parseAuthResponse(authResponse);
227         Assert.assertEquals("uname2", table.get("username"));
228         Assert.assertEquals("realm2", table.get("realm"));
229         Assert.assertEquals("/", table.get("uri"));
230         Assert.assertEquals("123546", table.get("nonce"));
231         Assert.assertEquals("0283edd9ef06a38b378b3b74661391e9", table.get("response"));
232     }
233 
234     @Test(expected=AuthenticationException.class)
235     public void testDigestAuthenticationNoRealm() throws Exception {
236         final String challenge = "Digest no-realm=\"realm1\", nonce=\"f2a3f18799759d4f1a1c068b92b573cb\"";
237         final Header authChallenge = new BasicHeader(AUTH.WWW_AUTH, challenge);
238         final HttpContext context = new BasicHttpContext();
239         final DigestScheme authscheme = new DigestScheme();
240         authscheme.processChallenge(authChallenge);
241 
242         final Credentials cred = new UsernamePasswordCredentials("username","password");
243         final HttpRequest request = new BasicHttpRequest("Simple", "/");
244         authscheme.authenticate(cred, request, context);
245     }
246 
247     @Test(expected=AuthenticationException.class)
248     public void testDigestAuthenticationNoNonce() throws Exception {
249         final String challenge = "Digest realm=\"realm1\", no-nonce=\"f2a3f18799759d4f1a1c068b92b573cb\"";
250         final Header authChallenge = new BasicHeader(AUTH.WWW_AUTH, challenge);
251         final HttpContext context = new BasicHttpContext();
252         final DigestScheme authscheme = new DigestScheme();
253         authscheme.processChallenge(authChallenge);
254 
255         final Credentials cred = new UsernamePasswordCredentials("username","password");
256         final HttpRequest request = new BasicHttpRequest("Simple", "/");
257         authscheme.authenticate(cred, request, context);
258     }
259 
260     /**
261      * Test digest authentication using the MD5-sess algorithm.
262      */
263     @Test
264     public void testDigestAuthenticationMD5Sess() throws Exception {
265         // Example using Digest auth with MD5-sess
266 
267         final String realm="realm";
268         final String username="username";
269         final String password="password";
270         final String nonce="e273f1776275974f1a120d8b92c5b3cb";
271 
272         final String challenge="Digest realm=\"" + realm + "\", "
273             + "nonce=\"" + nonce + "\", "
274             + "opaque=\"SomeString\", "
275             + "stale=false, "
276             + "algorithm=MD5-sess, "
277             + "qop=\"auth,auth-int\""; // we pass both but expect auth to be used
278 
279         final Header authChallenge = new BasicHeader(AUTH.WWW_AUTH, challenge);
280 
281         final Credentials cred = new UsernamePasswordCredentials(username, password);
282         final HttpRequest request = new BasicHttpRequest("Simple", "/");
283         final HttpContext context = new BasicHttpContext();
284 
285         final DigestScheme authscheme = new DigestScheme();
286         authscheme.processChallenge(authChallenge);
287         final Header authResponse = authscheme.authenticate(cred, request, context);
288         final String response = authResponse.getValue();
289 
290         Assert.assertTrue(response.indexOf("nc=00000001") > 0); // test for quotes
291         Assert.assertTrue(response.indexOf("qop=auth") > 0); // test for quotes
292 
293         final Map<String, String> table = parseAuthResponse(authResponse);
294         Assert.assertEquals(username, table.get("username"));
295         Assert.assertEquals(realm, table.get("realm"));
296         Assert.assertEquals("MD5-sess", table.get("algorithm"));
297         Assert.assertEquals("/", table.get("uri"));
298         Assert.assertEquals(nonce, table.get("nonce"));
299         Assert.assertEquals(1, Integer.parseInt(table.get("nc"),16));
300         Assert.assertTrue(null != table.get("cnonce"));
301         Assert.assertEquals("SomeString", table.get("opaque"));
302         Assert.assertEquals("auth", table.get("qop"));
303         //@TODO: add better check
304         Assert.assertTrue(null != table.get("response"));
305     }
306 
307     /**
308      * Test digest authentication using the MD5-sess algorithm.
309      */
310     @Test
311     public void testDigestAuthenticationMD5SessNoQop() throws Exception {
312         // Example using Digest auth with MD5-sess
313 
314         final String realm="realm";
315         final String username="username";
316         final String password="password";
317         final String nonce="e273f1776275974f1a120d8b92c5b3cb";
318 
319         final String challenge="Digest realm=\"" + realm + "\", "
320             + "nonce=\"" + nonce + "\", "
321             + "opaque=\"SomeString\", "
322             + "stale=false, "
323             + "algorithm=MD5-sess";
324 
325         final Header authChallenge = new BasicHeader(AUTH.WWW_AUTH, challenge);
326 
327         final Credentials cred = new UsernamePasswordCredentials(username, password);
328 
329         final HttpRequest request = new BasicHttpRequest("Simple", "/");
330         final HttpContext context = new BasicHttpContext();
331 
332         final DigestScheme authscheme = new DigestScheme();
333         authscheme.processChallenge(authChallenge);
334         final Header authResponse = authscheme.authenticate(cred, request, context);
335 
336         final Map<String, String> table = parseAuthResponse(authResponse);
337         Assert.assertEquals(username, table.get("username"));
338         Assert.assertEquals(realm, table.get("realm"));
339         Assert.assertEquals("MD5-sess", table.get("algorithm"));
340         Assert.assertEquals("/", table.get("uri"));
341         Assert.assertEquals(nonce, table.get("nonce"));
342         Assert.assertTrue(null == table.get("nc"));
343         Assert.assertEquals("SomeString", table.get("opaque"));
344         Assert.assertTrue(null == table.get("qop"));
345         //@TODO: add better check
346         Assert.assertTrue(null != table.get("response"));
347     }
348 
349     /**
350      * Test digest authentication with unknown qop value
351      */
352     @Test(expected=AuthenticationException.class)
353     public void testDigestAuthenticationMD5SessUnknownQop() throws Exception {
354         // Example using Digest auth with MD5-sess
355 
356         final String realm="realm";
357         final String username="username";
358         final String password="password";
359         final String nonce="e273f1776275974f1a120d8b92c5b3cb";
360 
361         final String challenge="Digest realm=\"" + realm + "\", "
362             + "nonce=\"" + nonce + "\", "
363             + "opaque=\"SomeString\", "
364             + "stale=false, "
365             + "algorithm=MD5-sess, "
366             + "qop=\"stuff\"";
367 
368         final Header authChallenge = new BasicHeader(AUTH.WWW_AUTH, challenge);
369 
370         final DigestScheme authscheme = new DigestScheme();
371         authscheme.processChallenge(authChallenge);
372 
373         final Credentials cred = new UsernamePasswordCredentials(username, password);
374         final HttpRequest request = new BasicHttpRequest("Simple", "/");
375         final HttpContext context = new BasicHttpContext();
376         authscheme.authenticate(cred, request, context);
377     }
378 
379     /**
380      * Test digest authentication with unknown qop value
381      */
382     @Test(expected=AuthenticationException.class)
383     public void testDigestAuthenticationUnknownAlgo() throws Exception {
384         // Example using Digest auth with MD5-sess
385 
386         final String realm="realm";
387         final String username="username";
388         final String password="password";
389         final String nonce="e273f1776275974f1a120d8b92c5b3cb";
390 
391         final String challenge="Digest realm=\"" + realm + "\", "
392             + "nonce=\"" + nonce + "\", "
393             + "opaque=\"SomeString\", "
394             + "stale=false, "
395             + "algorithm=stuff, "
396             + "qop=\"auth\"";
397 
398         final Header authChallenge = new BasicHeader(AUTH.WWW_AUTH, challenge);
399 
400         final DigestScheme authscheme = new DigestScheme();
401         authscheme.processChallenge(authChallenge);
402 
403         final Credentials cred = new UsernamePasswordCredentials(username, password);
404         final HttpRequest request = new BasicHttpRequest("Simple", "/");
405         final HttpContext context = new BasicHttpContext();
406         authscheme.authenticate(cred, request, context);
407     }
408 
409     @Test
410     public void testDigestAuthenticationWithStaleNonce() throws Exception {
411         final String challenge = "Digest realm=\"realm1\", " +
412                 "nonce=\"f2a3f18799759d4f1a1c068b92b573cb\", stale=\"true\"";
413         final Header authChallenge = new BasicHeader(AUTH.WWW_AUTH, challenge);
414         final AuthScheme authscheme = new DigestScheme();
415         authscheme.processChallenge(authChallenge);
416 
417         Assert.assertFalse(authscheme.isComplete());
418     }
419 
420     private static Map<String, String> parseAuthResponse(final Header authResponse) {
421         final String s = authResponse.getValue();
422         if (!s.startsWith("Digest ")) {
423             return null;
424         }
425         final HeaderElement[] elements = BasicHeaderValueParser.parseElements(s.substring(7), null);
426         final Map<String, String> map = new HashMap<String, String>(elements.length);
427         for (final HeaderElement element : elements) {
428             map.put(element.getName(), element.getValue());
429         }
430         return map;
431     }
432 
433     @Test
434     public void testDigestNouceCount() throws Exception {
435         final String challenge1 = "Digest realm=\"realm1\", nonce=\"f2a3f18799759d4f1a1c068b92b573cb\", qop=auth";
436         final Header authChallenge1 = new BasicHeader(AUTH.WWW_AUTH, challenge1);
437         final HttpRequest request = new BasicHttpRequest("GET", "/");
438         final Credentials cred = new UsernamePasswordCredentials("username","password");
439         final HttpContext context = new BasicHttpContext();
440         final DigestScheme authscheme = new DigestScheme();
441         authscheme.processChallenge(authChallenge1);
442         final Header authResponse1 = authscheme.authenticate(cred, request, context);
443         final Map<String, String> table1 = parseAuthResponse(authResponse1);
444         Assert.assertEquals("00000001", table1.get("nc"));
445         final Header authResponse2 = authscheme.authenticate(cred, request, context);
446         final Map<String, String> table2 = parseAuthResponse(authResponse2);
447         Assert.assertEquals("00000002", table2.get("nc"));
448         final String challenge2 = "Digest realm=\"realm1\", nonce=\"f2a3f18799759d4f1a1c068b92b573cb\", qop=auth";
449         final Header authChallenge2 = new BasicHeader(AUTH.WWW_AUTH, challenge2);
450         authscheme.processChallenge(authChallenge2);
451         final Header authResponse3 = authscheme.authenticate(cred, request, context);
452         final Map<String, String> table3 = parseAuthResponse(authResponse3);
453         Assert.assertEquals("00000003", table3.get("nc"));
454         final String challenge3 = "Digest realm=\"realm1\", nonce=\"e273f1776275974f1a120d8b92c5b3cb\", qop=auth";
455         final Header authChallenge3 = new BasicHeader(AUTH.WWW_AUTH, challenge3);
456         authscheme.processChallenge(authChallenge3);
457         final Header authResponse4 = authscheme.authenticate(cred, request, context);
458         final Map<String, String> table4 = parseAuthResponse(authResponse4);
459         Assert.assertEquals("00000001", table4.get("nc"));
460     }
461 
462     @Test
463     public void testDigestMD5SessA1AndCnonceConsistency() throws Exception {
464         final String challenge1 = "Digest qop=\"auth\", algorithm=MD5-sess, nonce=\"1234567890abcdef\", " +
465                 "charset=utf-8, realm=\"subnet.domain.com\"";
466         final Header authChallenge1 = new BasicHeader(AUTH.WWW_AUTH, challenge1);
467         final HttpRequest request = new BasicHttpRequest("GET", "/");
468         final Credentials cred = new UsernamePasswordCredentials("username","password");
469         final HttpContext context = new BasicHttpContext();
470         final DigestScheme authscheme = new DigestScheme();
471         authscheme.processChallenge(authChallenge1);
472         final Header authResponse1 = authscheme.authenticate(cred, request, context);
473         final Map<String, String> table1 = parseAuthResponse(authResponse1);
474         Assert.assertEquals("00000001", table1.get("nc"));
475         final String cnonce1 = authscheme.getCnonce();
476         final String sessionKey1 = authscheme.getA1();
477 
478         final Header authResponse2 = authscheme.authenticate(cred, request, context);
479         final Map<String, String> table2 = parseAuthResponse(authResponse2);
480         Assert.assertEquals("00000002", table2.get("nc"));
481         final String cnonce2 = authscheme.getCnonce();
482         final String sessionKey2 = authscheme.getA1();
483 
484         Assert.assertEquals(cnonce1, cnonce2);
485         Assert.assertEquals(sessionKey1, sessionKey2);
486 
487         final String challenge2 = "Digest qop=\"auth\", algorithm=MD5-sess, nonce=\"1234567890abcdef\", " +
488             "charset=utf-8, realm=\"subnet.domain.com\"";
489         final Header authChallenge2 = new BasicHeader(AUTH.WWW_AUTH, challenge2);
490         authscheme.processChallenge(authChallenge2);
491         final Header authResponse3 = authscheme.authenticate(cred, request, context);
492         final Map<String, String> table3 = parseAuthResponse(authResponse3);
493         Assert.assertEquals("00000003", table3.get("nc"));
494 
495         final String cnonce3 = authscheme.getCnonce();
496         final String sessionKey3 = authscheme.getA1();
497 
498         Assert.assertEquals(cnonce1, cnonce3);
499         Assert.assertEquals(sessionKey1, sessionKey3);
500 
501         final String challenge3 = "Digest qop=\"auth\", algorithm=MD5-sess, nonce=\"fedcba0987654321\", " +
502             "charset=utf-8, realm=\"subnet.domain.com\"";
503         final Header authChallenge3 = new BasicHeader(AUTH.WWW_AUTH, challenge3);
504         authscheme.processChallenge(authChallenge3);
505         final Header authResponse4 = authscheme.authenticate(cred, request, context);
506         final Map<String, String> table4 = parseAuthResponse(authResponse4);
507         Assert.assertEquals("00000001", table4.get("nc"));
508 
509         final String cnonce4 = authscheme.getCnonce();
510         final String sessionKey4 = authscheme.getA1();
511 
512         Assert.assertFalse(cnonce1.equals(cnonce4));
513         Assert.assertFalse(sessionKey1.equals(sessionKey4));
514     }
515 
516     @Test
517     public void testHttpEntityDigest() throws Exception {
518         final HttpEntityDigester digester = new HttpEntityDigester(MessageDigest.getInstance("MD5"));
519         Assert.assertNull(digester.getDigest());
520         digester.write('a');
521         digester.write('b');
522         digester.write('c');
523         digester.write(0xe4);
524         digester.write(0xf6);
525         digester.write(0xfc);
526         digester.write(new byte[] { 'a', 'b', 'c'});
527         Assert.assertNull(digester.getDigest());
528         digester.close();
529         Assert.assertEquals("acd2b59cd01c7737d8069015584c6cac", DigestScheme.encode(digester.getDigest()));
530         try {
531             digester.write('a');
532             Assert.fail("IOException should have been thrown");
533         } catch (final IOException ex) {
534         }
535         try {
536             digester.write(new byte[] { 'a', 'b', 'c'});
537             Assert.fail("IOException should have been thrown");
538         } catch (final IOException ex) {
539         }
540     }
541 
542     @Test
543     public void testDigestAuthenticationQopAuthInt() throws Exception {
544         final String challenge = "Digest realm=\"realm1\", nonce=\"f2a3f18799759d4f1a1c068b92b573cb\", " +
545                 "qop=\"auth,auth-int\"";
546         final Header authChallenge = new BasicHeader(AUTH.WWW_AUTH, challenge);
547         final HttpEntityEnclosingRequest request = new BasicHttpEntityEnclosingRequest("Post", "/");
548         request.setEntity(new StringEntity("abc\u00e4\u00f6\u00fcabc", HTTP.DEF_CONTENT_CHARSET));
549         final Credentials cred = new UsernamePasswordCredentials("username","password");
550         final DigestScheme authscheme = new DigestScheme();
551         final HttpContext context = new BasicHttpContext();
552         authscheme.processChallenge(authChallenge);
553         final Header authResponse = authscheme.authenticate(cred, request, context);
554 
555         Assert.assertEquals("Post:/:acd2b59cd01c7737d8069015584c6cac", authscheme.getA2());
556 
557         final Map<String, String> table = parseAuthResponse(authResponse);
558         Assert.assertEquals("username", table.get("username"));
559         Assert.assertEquals("realm1", table.get("realm"));
560         Assert.assertEquals("/", table.get("uri"));
561         Assert.assertEquals("auth-int", table.get("qop"));
562         Assert.assertEquals("f2a3f18799759d4f1a1c068b92b573cb", table.get("nonce"));
563     }
564 
565     @Test
566     public void testDigestAuthenticationQopAuthIntNullEntity() throws Exception {
567         final String challenge = "Digest realm=\"realm1\", nonce=\"f2a3f18799759d4f1a1c068b92b573cb\", " +
568                 "qop=\"auth,auth-int\"";
569         final Header authChallenge = new BasicHeader(AUTH.WWW_AUTH, challenge);
570         final HttpRequest request = new BasicHttpEntityEnclosingRequest("Post", "/");
571         final Credentials cred = new UsernamePasswordCredentials("username","password");
572         final DigestScheme authscheme = new DigestScheme();
573         final HttpContext context = new BasicHttpContext();
574         authscheme.processChallenge(authChallenge);
575         final Header authResponse = authscheme.authenticate(cred, request, context);
576 
577         Assert.assertEquals("Post:/:d41d8cd98f00b204e9800998ecf8427e", authscheme.getA2());
578 
579         final Map<String, String> table = parseAuthResponse(authResponse);
580         Assert.assertEquals("username", table.get("username"));
581         Assert.assertEquals("realm1", table.get("realm"));
582         Assert.assertEquals("/", table.get("uri"));
583         Assert.assertEquals("auth-int", table.get("qop"));
584         Assert.assertEquals("f2a3f18799759d4f1a1c068b92b573cb", table.get("nonce"));
585     }
586 
587     @Test
588     public void testDigestAuthenticationQopAuthOrAuthIntNonRepeatableEntity() throws Exception {
589         final String challenge = "Digest realm=\"realm1\", nonce=\"f2a3f18799759d4f1a1c068b92b573cb\", " +
590                 "qop=\"auth,auth-int\"";
591         final Header authChallenge = new BasicHeader(AUTH.WWW_AUTH, challenge);
592         final HttpEntityEnclosingRequest request = new BasicHttpEntityEnclosingRequest("Post", "/");
593         request.setEntity(new InputStreamEntity(new ByteArrayInputStream(new byte[] {'a'}), -1));
594         final Credentials cred = new UsernamePasswordCredentials("username","password");
595         final DigestScheme authscheme = new DigestScheme();
596         final HttpContext context = new BasicHttpContext();
597         authscheme.processChallenge(authChallenge);
598         final Header authResponse = authscheme.authenticate(cred, request, context);
599 
600         Assert.assertEquals("Post:/", authscheme.getA2());
601 
602         final Map<String, String> table = parseAuthResponse(authResponse);
603         Assert.assertEquals("username", table.get("username"));
604         Assert.assertEquals("realm1", table.get("realm"));
605         Assert.assertEquals("/", table.get("uri"));
606         Assert.assertEquals("auth", table.get("qop"));
607         Assert.assertEquals("f2a3f18799759d4f1a1c068b92b573cb", table.get("nonce"));
608     }
609 
610     @Test
611     public void testParameterCaseSensitivity() throws Exception {
612         final String challenge = "Digest Realm=\"-\", " +
613                 "nonce=\"YjYuNGYyYmJhMzUuY2I5ZDhlZDE5M2ZlZDM 1Mjk3NGJkNTIyYjgyNTcwMjQ=\", " +
614                 "opaque=\"98700A3D9CE17065E2246B41035C6609\", qop=\"auth\"";
615         final Header authChallenge = new BasicHeader(AUTH.PROXY_AUTH, challenge);
616         final HttpRequest request = new BasicHttpRequest("GET", "/");
617         final Credentials cred = new UsernamePasswordCredentials("username","password");
618         final DigestScheme authscheme = new DigestScheme();
619         final HttpContext context = new BasicHttpContext();
620         authscheme.processChallenge(authChallenge);
621         Assert.assertEquals("-", authscheme.getRealm());
622 
623         authscheme.authenticate(cred, request, context);
624     }
625 
626     @Test(expected=AuthenticationException.class)
627     public void testDigestAuthenticationQopIntOnlyNonRepeatableEntity() throws Exception {
628         final String challenge = "Digest realm=\"realm1\", nonce=\"f2a3f18799759d4f1a1c068b92b573cb\", " +
629                 "qop=\"auth-int\"";
630         final Header authChallenge = new BasicHeader(AUTH.WWW_AUTH, challenge);
631         final HttpEntityEnclosingRequest request = new BasicHttpEntityEnclosingRequest("Post", "/");
632         request.setEntity(new InputStreamEntity(new ByteArrayInputStream(new byte[] {'a'}), -1));
633         final Credentials cred = new UsernamePasswordCredentials("username","password");
634         final DigestScheme authscheme = new DigestScheme();
635         final HttpContext context = new BasicHttpContext();
636         authscheme.processChallenge(authChallenge);
637         authscheme.authenticate(cred, request, context);
638     }
639 
640     @Test
641     public void testSerialization() throws Exception {
642         final String challenge = "Digest realm=\"realm1\", nonce=\"f2a3f18799759d4f1a1c068b92b573cb\", " +
643                 "qop=\"auth,auth-int\"";
644         final Header authChallenge = new BasicHeader(AUTH.WWW_AUTH, challenge);
645         final DigestScheme digestScheme = new DigestScheme();
646         digestScheme.processChallenge(authChallenge);
647 
648         final ByteArrayOutputStream buffer = new ByteArrayOutputStream();
649         final ObjectOutputStream out = new ObjectOutputStream(buffer);
650         out.writeObject(digestScheme);
651         out.flush();
652         final byte[] raw = buffer.toByteArray();
653         final ObjectInputStream in = new ObjectInputStream(new ByteArrayInputStream(raw));
654         final DigestScheme authScheme = (DigestScheme) in.readObject();
655 
656         Assert.assertEquals(digestScheme.getSchemeName(), authScheme.getSchemeName());
657         Assert.assertEquals(digestScheme.getRealm(), authScheme.getRealm());
658         Assert.assertEquals(digestScheme.isComplete(), authScheme.isComplete());
659         Assert.assertEquals(digestScheme.getA1(), authScheme.getA1());
660         Assert.assertEquals(digestScheme.getA2(), authScheme.getA2());
661         Assert.assertEquals(digestScheme.getCnonce(), authScheme.getCnonce());
662     }
663 
664 }