1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27 package org.apache.hc.client5.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.nio.charset.StandardCharsets;
35 import java.security.MessageDigest;
36 import java.util.HashMap;
37 import java.util.List;
38 import java.util.Map;
39
40 import org.apache.hc.client5.http.auth.AuthChallenge;
41 import org.apache.hc.client5.http.auth.AuthScheme;
42 import org.apache.hc.client5.http.auth.AuthScope;
43 import org.apache.hc.client5.http.auth.AuthenticationException;
44 import org.apache.hc.client5.http.auth.ChallengeType;
45 import org.apache.hc.client5.http.auth.CredentialsProvider;
46 import org.apache.hc.client5.http.auth.MalformedChallengeException;
47 import org.apache.hc.client5.http.auth.StandardAuthScheme;
48 import org.apache.hc.core5.http.ClassicHttpRequest;
49 import org.apache.hc.core5.http.ContentType;
50 import org.apache.hc.core5.http.HeaderElement;
51 import org.apache.hc.core5.http.HttpHost;
52 import org.apache.hc.core5.http.HttpRequest;
53 import org.apache.hc.core5.http.ParseException;
54 import org.apache.hc.core5.http.io.entity.InputStreamEntity;
55 import org.apache.hc.core5.http.io.entity.StringEntity;
56 import org.apache.hc.core5.http.message.BasicClassicHttpRequest;
57 import org.apache.hc.core5.http.message.BasicHeaderValueParser;
58 import org.apache.hc.core5.http.message.BasicHttpRequest;
59 import org.apache.hc.core5.http.message.ParserCursor;
60 import org.apache.hc.core5.util.CharArrayBuffer;
61 import org.junit.jupiter.api.Assertions;
62 import org.junit.jupiter.api.Test;
63
64
65
66
67 public class TestDigestScheme {
68
69 private static AuthChallenge parse(final String s) throws ParseException {
70 final CharArrayBuffer buffer = new CharArrayBuffer(s.length());
71 buffer.append(s);
72 final ParserCursor cursor = new ParserCursor(0, buffer.length());
73 final List<AuthChallenge> authChallenges = AuthChallengeParser.INSTANCE.parse(ChallengeType.TARGET, buffer, cursor);
74 Assertions.assertEquals(1, authChallenges.size());
75 return authChallenges.get(0);
76 }
77
78 @Test
79 public void testDigestAuthenticationEmptyChallenge1() throws Exception {
80 final AuthChallenge authChallenge = parse(StandardAuthScheme.DIGEST);
81 final AuthScheme authscheme = new DigestScheme();
82 Assertions.assertThrows(MalformedChallengeException.class, () ->
83 authscheme.processChallenge(authChallenge, null));
84 }
85
86 @Test
87 public void testDigestAuthenticationEmptyChallenge2() throws Exception {
88 final AuthChallenge authChallenge = parse(StandardAuthScheme.DIGEST + " ");
89 final AuthScheme authscheme = new DigestScheme();
90 Assertions.assertThrows(MalformedChallengeException.class, () ->
91 authscheme.processChallenge(authChallenge, null));
92 }
93
94 @Test
95 public void testDigestAuthenticationWithDefaultCreds() throws Exception {
96 final HttpRequest request = new BasicHttpRequest("Simple", "/");
97 final HttpHost host = new HttpHost("somehost", 80);
98 final CredentialsProvider credentialsProvider = CredentialsProviderBuilder.create()
99 .add(new AuthScope(host, "realm1", null), "username", "password".toCharArray())
100 .build();
101
102 final String challenge = StandardAuthScheme.DIGEST + " realm=\"realm1\", nonce=\"f2a3f18799759d4f1a1c068b92b573cb\"";
103 final AuthChallenge authChallenge = parse(challenge);
104 final DigestScheme authscheme = new DigestScheme();
105 authscheme.processChallenge(authChallenge, null);
106
107 Assertions.assertTrue(authscheme.isResponseReady(host, credentialsProvider, null));
108 final String authResponse = authscheme.generateAuthResponse(host, request, null);
109 Assertions.assertTrue(authscheme.isChallengeComplete());
110 Assertions.assertFalse(authscheme.isConnectionBased());
111
112 final Map<String, String> table = parseAuthResponse(authResponse);
113 Assertions.assertEquals("username", table.get("username"));
114 Assertions.assertEquals("realm1", table.get("realm"));
115 Assertions.assertEquals("/", table.get("uri"));
116 Assertions.assertEquals("f2a3f18799759d4f1a1c068b92b573cb", table.get("nonce"));
117 Assertions.assertEquals("e95a7ddf37c2eab009568b1ed134f89a", table.get("response"));
118 }
119
120 @Test
121 public void testDigestAuthentication() throws Exception {
122 final HttpRequest request = new BasicHttpRequest("Simple", "/");
123 final HttpHost host = new HttpHost("somehost", 80);
124 final CredentialsProvider credentialsProvider = CredentialsProviderBuilder.create()
125 .add(new AuthScope(host, "realm1", null), "username", "password".toCharArray())
126 .build();
127
128 final String challenge = StandardAuthScheme.DIGEST + " realm=\"realm1\", nonce=\"f2a3f18799759d4f1a1c068b92b573cb\"";
129 final AuthChallenge authChallenge = parse(challenge);
130 final DigestScheme authscheme = new DigestScheme();
131 authscheme.processChallenge(authChallenge, null);
132
133 Assertions.assertTrue(authscheme.isResponseReady(host, credentialsProvider, null));
134 final String authResponse = authscheme.generateAuthResponse(host, request, null);
135
136 final Map<String, String> table = parseAuthResponse(authResponse);
137 Assertions.assertEquals("username", table.get("username"));
138 Assertions.assertEquals("realm1", table.get("realm"));
139 Assertions.assertEquals("/", table.get("uri"));
140 Assertions.assertEquals("f2a3f18799759d4f1a1c068b92b573cb", table.get("nonce"));
141 Assertions.assertEquals("e95a7ddf37c2eab009568b1ed134f89a", table.get("response"));
142 }
143
144 @Test
145 public void testDigestAuthenticationInvalidInput() throws Exception {
146 final HttpHost host = new HttpHost("somehost", 80);
147 final CredentialsProvider credentialsProvider = CredentialsProviderBuilder.create()
148 .add(new AuthScope(host, "realm1", null), "username", "password".toCharArray())
149 .build();
150
151 final String challenge = StandardAuthScheme.DIGEST + " realm=\"realm1\", nonce=\"f2a3f18799759d4f1a1c068b92b573cb\"";
152 final AuthChallenge authChallenge = parse(challenge);
153 final DigestScheme authscheme = new DigestScheme();
154 authscheme.processChallenge(authChallenge, null);
155
156 Assertions.assertThrows(NullPointerException.class, () ->
157 authscheme.isResponseReady(null, credentialsProvider, null));
158 Assertions.assertThrows(NullPointerException.class, () ->
159 authscheme.isResponseReady(host, null, null));
160 Assertions.assertThrows(NullPointerException.class, () ->
161 authscheme.generateAuthResponse(host, null, null));
162 }
163
164 @Test
165 public void testDigestAuthenticationWithSHA() throws Exception {
166 final HttpRequest request = new BasicHttpRequest("Simple", "/");
167 final HttpHost host = new HttpHost("somehost", 80);
168 final CredentialsProvider credentialsProvider = CredentialsProviderBuilder.create()
169 .add(new AuthScope(host, "realm1", null), "username", "password".toCharArray())
170 .build();
171
172 final String challenge = StandardAuthScheme.DIGEST + " realm=\"realm1\", " +
173 "nonce=\"f2a3f18799759d4f1a1c068b92b573cb\", " +
174 "algorithm=SHA";
175 final AuthChallenge authChallenge = parse(challenge);
176 final DigestScheme authscheme = new DigestScheme();
177 authscheme.processChallenge(authChallenge, null);
178
179 Assertions.assertTrue(authscheme.isResponseReady(host, credentialsProvider, null));
180 final String authResponse = authscheme.generateAuthResponse(host, request, null);
181
182 final Map<String, String> table = parseAuthResponse(authResponse);
183 Assertions.assertEquals("username", table.get("username"));
184 Assertions.assertEquals("realm1", table.get("realm"));
185 Assertions.assertEquals("/", table.get("uri"));
186 Assertions.assertEquals("f2a3f18799759d4f1a1c068b92b573cb", table.get("nonce"));
187 Assertions.assertEquals("8769e82e4e28ecc040b969562b9050580c6d186d", table.get("response"));
188 }
189
190 @Test
191 public void testDigestAuthenticationWithQueryStringInDigestURI() throws Exception {
192 final HttpRequest request = new BasicHttpRequest("Simple", "/?param=value");
193 final HttpHost host = new HttpHost("somehost", 80);
194 final CredentialsProvider credentialsProvider = CredentialsProviderBuilder.create()
195 .add(new AuthScope(host, "realm1", null), "username", "password".toCharArray())
196 .build();
197
198 final String challenge = StandardAuthScheme.DIGEST + " realm=\"realm1\", nonce=\"f2a3f18799759d4f1a1c068b92b573cb\"";
199 final AuthChallenge authChallenge = parse(challenge);
200 final DigestScheme authscheme = new DigestScheme();
201 authscheme.processChallenge(authChallenge, null);
202
203 Assertions.assertTrue(authscheme.isResponseReady(host, credentialsProvider, null));
204 final String authResponse = authscheme.generateAuthResponse(host, request, null);
205
206 final Map<String, String> table = parseAuthResponse(authResponse);
207 Assertions.assertEquals("username", table.get("username"));
208 Assertions.assertEquals("realm1", table.get("realm"));
209 Assertions.assertEquals("/?param=value", table.get("uri"));
210 Assertions.assertEquals("f2a3f18799759d4f1a1c068b92b573cb", table.get("nonce"));
211 Assertions.assertEquals("a847f58f5fef0bc087bcb9c3eb30e042", table.get("response"));
212 }
213
214 @Test
215 public void testDigestAuthenticationNoRealm() throws Exception {
216 final HttpRequest request = new BasicHttpRequest("Simple", "/");
217 final HttpHost host = new HttpHost("somehost", 80);
218 final CredentialsProvider credentialsProvider = CredentialsProviderBuilder.create()
219 .add(new AuthScope(host, "realm1", null), "username", "password".toCharArray())
220 .build();
221
222 final String challenge = StandardAuthScheme.DIGEST + " no-realm=\"realm1\", nonce=\"f2a3f18799759d4f1a1c068b92b573cb\"";
223 final AuthChallenge authChallenge = parse(challenge);
224 final DigestScheme authscheme = new DigestScheme();
225 authscheme.processChallenge(authChallenge, null);
226
227 Assertions.assertTrue(authscheme.isResponseReady(host, credentialsProvider, null));
228 Assertions.assertThrows(AuthenticationException.class, () ->
229 authscheme.generateAuthResponse(host, request, null));
230 }
231
232 @Test
233 public void testDigestAuthenticationNoNonce() throws Exception {
234 final HttpRequest request = new BasicHttpRequest("Simple", "/");
235 final HttpHost host = new HttpHost("somehost", 80);
236 final CredentialsProvider credentialsProvider = CredentialsProviderBuilder.create()
237 .add(new AuthScope(host, "realm1", null), "username", "password".toCharArray())
238 .build();
239
240 final String challenge = StandardAuthScheme.DIGEST + " realm=\"realm1\", no-nonce=\"f2a3f18799759d4f1a1c068b92b573cb\"";
241 final AuthChallenge authChallenge = parse(challenge);
242 final DigestScheme authscheme = new DigestScheme();
243 authscheme.processChallenge(authChallenge, null);
244
245 Assertions.assertTrue(authscheme.isResponseReady(host, credentialsProvider, null));
246 Assertions.assertThrows(AuthenticationException.class, () ->
247 authscheme.generateAuthResponse(host, request, null));
248 }
249
250 @Test
251 public void testDigestAuthenticationNoAlgorithm() throws Exception {
252 final HttpRequest request = new BasicHttpRequest("Simple", "/");
253 final HttpHost host = new HttpHost("somehost", 80);
254 final CredentialsProvider credentialsProvider = CredentialsProviderBuilder.create()
255 .add(new AuthScope(host, "realm1", null), "username", "password".toCharArray())
256 .build();
257
258 final String challenge = StandardAuthScheme.DIGEST + " realm=\"realm1\", nonce=\"f2a3f18799759d4f1a1c068b92b573cb\"";
259 final AuthChallenge authChallenge = parse(challenge);
260 final DigestScheme authscheme = new DigestScheme();
261 authscheme.processChallenge(authChallenge, null);
262
263 final String authResponse = authscheme.generateAuthResponse(host, request, null);
264
265 final Map<String, String> table = parseAuthResponse(authResponse);
266 Assertions.assertNull(table.get("algorithm"));
267 }
268
269 @Test
270 public void testDigestAuthenticationMD5Algorithm() throws Exception {
271 final HttpRequest request = new BasicHttpRequest("Simple", "/");
272 final HttpHost host = new HttpHost("somehost", 80);
273 final CredentialsProvider credentialsProvider = CredentialsProviderBuilder.create()
274 .add(new AuthScope(host, "realm1", null), "username", "password".toCharArray())
275 .build();
276
277 final String challenge = StandardAuthScheme.DIGEST
278 + " realm=\"realm1\", nonce=\"f2a3f18799759d4f1a1c068b92b573cb\""
279 + ", algorithm=MD5";
280 final AuthChallenge authChallenge = parse(challenge);
281 final DigestScheme authscheme = new DigestScheme();
282 authscheme.processChallenge(authChallenge, null);
283
284 final String authResponse = authscheme.generateAuthResponse(host, request, null);
285
286 final Map<String, String> table = parseAuthResponse(authResponse);
287 Assertions.assertEquals("MD5", table.get("algorithm"));
288 }
289
290
291
292
293 @Test
294 public void testDigestAuthenticationMD5Sess() throws Exception {
295
296
297 final String realm="realm";
298 final String username="username";
299 final String password="password";
300 final String nonce="e273f1776275974f1a120d8b92c5b3cb";
301
302 final HttpRequest request = new BasicHttpRequest("Simple", "/");
303 final HttpHost host = new HttpHost("somehost", 80);
304 final CredentialsProvider credentialsProvider = CredentialsProviderBuilder.create()
305 .add(new AuthScope(host, realm, null), username, password.toCharArray())
306 .build();
307
308 final String challenge=StandardAuthScheme.DIGEST + " realm=\"" + realm + "\", "
309 + "nonce=\"" + nonce + "\", "
310 + "opaque=\"SomeString\", "
311 + "stale=false, "
312 + "algorithm=MD5-sess, "
313 + "qop=\"auth,auth-int\"";
314
315 final AuthChallenge authChallenge = parse(challenge);
316
317 final DigestScheme authscheme = new DigestScheme();
318 authscheme.processChallenge(authChallenge, null);
319
320 Assertions.assertTrue(authscheme.isResponseReady(host, credentialsProvider, null));
321 final String authResponse = authscheme.generateAuthResponse(host, request, null);
322
323 Assertions.assertTrue(authResponse.indexOf("nc=00000001") > 0);
324 Assertions.assertTrue(authResponse.indexOf("qop=auth") > 0);
325
326 final Map<String, String> table = parseAuthResponse(authResponse);
327 Assertions.assertEquals(username, table.get("username"));
328 Assertions.assertEquals(realm, table.get("realm"));
329 Assertions.assertEquals("MD5-sess", table.get("algorithm"));
330 Assertions.assertEquals("/", table.get("uri"));
331 Assertions.assertEquals(nonce, table.get("nonce"));
332 Assertions.assertEquals(1, Integer.parseInt(table.get("nc"),16));
333 Assertions.assertNotNull(table.get("cnonce"));
334 Assertions.assertEquals("SomeString", table.get("opaque"));
335 Assertions.assertEquals("auth", table.get("qop"));
336
337 Assertions.assertNotNull(table.get("response"));
338 }
339
340
341
342
343 @Test
344 public void testDigestAuthenticationMD5SessNoQop() throws Exception {
345
346
347 final String realm="realm";
348 final String username="username";
349 final String password="password";
350 final String nonce="e273f1776275974f1a120d8b92c5b3cb";
351
352 final HttpRequest request = new BasicHttpRequest("Simple", "/");
353 final HttpHost host = new HttpHost("somehost", 80);
354 final CredentialsProvider credentialsProvider = CredentialsProviderBuilder.create()
355 .add(new AuthScope(host, realm, null), username, password.toCharArray())
356 .build();
357
358 final String challenge=StandardAuthScheme.DIGEST + " realm=\"" + realm + "\", "
359 + "nonce=\"" + nonce + "\", "
360 + "opaque=\"SomeString\", "
361 + "stale=false, "
362 + "algorithm=MD5-sess";
363
364 final AuthChallenge authChallenge = parse(challenge);
365
366 final DigestScheme authscheme = new DigestScheme();
367 authscheme.processChallenge(authChallenge, null);
368 Assertions.assertTrue(authscheme.isResponseReady(host, credentialsProvider, null));
369 final String authResponse = authscheme.generateAuthResponse(host, request, null);
370
371 final Map<String, String> table = parseAuthResponse(authResponse);
372 Assertions.assertEquals(username, table.get("username"));
373 Assertions.assertEquals(realm, table.get("realm"));
374 Assertions.assertEquals("MD5-sess", table.get("algorithm"));
375 Assertions.assertEquals("/", table.get("uri"));
376 Assertions.assertEquals(nonce, table.get("nonce"));
377 Assertions.assertNull(table.get("nc"));
378 Assertions.assertEquals("SomeString", table.get("opaque"));
379 Assertions.assertNull(table.get("qop"));
380
381 Assertions.assertNotNull(table.get("response"));
382 }
383
384
385
386
387 @Test
388 public void testDigestAuthenticationMD5SessUnknownQop() throws Exception {
389
390
391 final String realm="realm";
392 final String username="username";
393 final String password="password";
394 final String nonce="e273f1776275974f1a120d8b92c5b3cb";
395
396 final HttpRequest request = new BasicHttpRequest("Simple", "/");
397 final HttpHost host = new HttpHost("somehost", 80);
398 final CredentialsProvider credentialsProvider = CredentialsProviderBuilder.create()
399 .add(new AuthScope(host, realm, null), username, password.toCharArray())
400 .build();
401
402 final String challenge=StandardAuthScheme.DIGEST + " realm=\"" + realm + "\", "
403 + "nonce=\"" + nonce + "\", "
404 + "opaque=\"SomeString\", "
405 + "stale=false, "
406 + "algorithm=MD5-sess, "
407 + "qop=\"stuff\"";
408
409 final AuthChallenge authChallenge = parse(challenge);
410
411 final DigestScheme authscheme = new DigestScheme();
412 authscheme.processChallenge(authChallenge, null);
413
414 Assertions.assertTrue(authscheme.isResponseReady(host, credentialsProvider, null));
415 Assertions.assertThrows(AuthenticationException.class, () ->
416 authscheme.generateAuthResponse(host, request, null));
417 }
418
419
420
421
422 @Test
423 public void testDigestAuthenticationUnknownAlgo() throws Exception {
424
425
426 final String realm="realm";
427 final String username="username";
428 final String password="password";
429 final String nonce="e273f1776275974f1a120d8b92c5b3cb";
430
431 final HttpRequest request = new BasicHttpRequest("Simple", "/");
432 final HttpHost host = new HttpHost("somehost", 80);
433 final CredentialsProvider credentialsProvider = CredentialsProviderBuilder.create()
434 .add(new AuthScope(host, realm, null), username, password.toCharArray())
435 .build();
436
437 final String challenge=StandardAuthScheme.DIGEST + " realm=\"" + realm + "\", "
438 + "nonce=\"" + nonce + "\", "
439 + "opaque=\"SomeString\", "
440 + "stale=false, "
441 + "algorithm=stuff, "
442 + "qop=\"auth\"";
443
444 final AuthChallenge authChallenge = parse(challenge);
445
446 final DigestScheme authscheme = new DigestScheme();
447 authscheme.processChallenge(authChallenge, null);
448
449 Assertions.assertTrue(authscheme.isResponseReady(host, credentialsProvider, null));
450 Assertions.assertThrows(AuthenticationException.class, () ->
451 authscheme.generateAuthResponse(host, request, null));
452 }
453
454 @Test
455 public void testDigestAuthenticationWithStaleNonce() throws Exception {
456 final String challenge = StandardAuthScheme.DIGEST + " realm=\"realm1\", " +
457 "nonce=\"f2a3f18799759d4f1a1c068b92b573cb\", stale=\"true\"";
458 final AuthChallenge authChallenge = parse(challenge);
459 final AuthScheme authscheme = new DigestScheme();
460 authscheme.processChallenge(authChallenge, null);
461
462 Assertions.assertFalse(authscheme.isChallengeComplete());
463 }
464
465 private static Map<String, String> parseAuthResponse(final String authResponse) {
466 if (!authResponse.startsWith(StandardAuthScheme.DIGEST + " ")) {
467 return null;
468 }
469 final String s = authResponse.substring(7);
470 final ParserCursor cursor = new ParserCursor(0, s.length());
471 final HeaderElement[] elements = BasicHeaderValueParser.INSTANCE.parseElements(s, cursor);
472 final Map<String, String> map = new HashMap<>(elements.length);
473 for (final HeaderElement element : elements) {
474 map.put(element.getName(), element.getValue());
475 }
476 return map;
477 }
478
479 @Test
480 public void testDigestNouceCount() throws Exception {
481 final HttpRequest request = new BasicHttpRequest("GET", "/");
482 final HttpHost host = new HttpHost("somehost", 80);
483 final CredentialsProvider credentialsProvider = CredentialsProviderBuilder.create()
484 .add(new AuthScope(host, "realm1", null), "username", "password".toCharArray())
485 .build();
486
487 final String challenge1 = StandardAuthScheme.DIGEST + " realm=\"realm1\", nonce=\"f2a3f18799759d4f1a1c068b92b573cb\", qop=auth";
488 final AuthChallenge authChallenge1 = parse(challenge1);
489
490 final DigestScheme authscheme = new DigestScheme();
491 authscheme.processChallenge(authChallenge1, null);
492 Assertions.assertTrue(authscheme.isResponseReady(host, credentialsProvider, null));
493 final String authResponse1 = authscheme.generateAuthResponse(host, request, null);
494
495 final Map<String, String> table1 = parseAuthResponse(authResponse1);
496 Assertions.assertEquals("00000001", table1.get("nc"));
497
498 Assertions.assertTrue(authscheme.isResponseReady(host, credentialsProvider, null));
499 final String authResponse2 = authscheme.generateAuthResponse(host, request, null);
500
501 final Map<String, String> table2 = parseAuthResponse(authResponse2);
502 Assertions.assertEquals("00000002", table2.get("nc"));
503 final String challenge2 = StandardAuthScheme.DIGEST + " realm=\"realm1\", nonce=\"f2a3f18799759d4f1a1c068b92b573cb\", qop=auth";
504 final AuthChallenge authChallenge2 = parse(challenge2);
505 authscheme.processChallenge(authChallenge2, null);
506
507 Assertions.assertTrue(authscheme.isResponseReady(host, credentialsProvider, null));
508 final String authResponse3 = authscheme.generateAuthResponse(host, request, null);
509
510 final Map<String, String> table3 = parseAuthResponse(authResponse3);
511 Assertions.assertEquals("00000003", table3.get("nc"));
512 final String challenge3 = StandardAuthScheme.DIGEST + " realm=\"realm1\", nonce=\"e273f1776275974f1a120d8b92c5b3cb\", qop=auth";
513 final AuthChallenge authChallenge3 = parse(challenge3);
514 authscheme.processChallenge(authChallenge3, null);
515
516 Assertions.assertTrue(authscheme.isResponseReady(host, credentialsProvider, null));
517 final String authResponse4 = authscheme.generateAuthResponse(host, request, null);
518
519 final Map<String, String> table4 = parseAuthResponse(authResponse4);
520 Assertions.assertEquals("00000001", table4.get("nc"));
521 }
522
523 @Test
524 public void testDigestMD5SessA1AndCnonceConsistency() throws Exception {
525 final HttpHost host = new HttpHost("somehost", 80);
526 final HttpRequest request = new BasicHttpRequest("GET", "/");
527 final CredentialsProvider credentialsProvider = CredentialsProviderBuilder.create()
528 .add(new AuthScope(host, "subnet.domain.com", null), "username", "password".toCharArray())
529 .build();
530
531 final String challenge1 = StandardAuthScheme.DIGEST + " qop=\"auth\", algorithm=MD5-sess, nonce=\"1234567890abcdef\", " +
532 "charset=utf-8, realm=\"subnet.domain.com\"";
533 final AuthChallenge authChallenge1 = parse(challenge1);
534 final DigestScheme authscheme = new DigestScheme();
535 authscheme.processChallenge(authChallenge1, null);
536 Assertions.assertTrue(authscheme.isResponseReady(host, credentialsProvider, null));
537 final String authResponse1 = authscheme.generateAuthResponse(host, request, null);
538
539 final Map<String, String> table1 = parseAuthResponse(authResponse1);
540 Assertions.assertEquals("00000001", table1.get("nc"));
541 final String cnonce1 = authscheme.getCnonce();
542 final String sessionKey1 = authscheme.getA1();
543
544 Assertions.assertTrue(authscheme.isResponseReady(host, credentialsProvider, null));
545 final String authResponse2 = authscheme.generateAuthResponse(host, request, null);
546 final Map<String, String> table2 = parseAuthResponse(authResponse2);
547 Assertions.assertEquals("00000002", table2.get("nc"));
548 final String cnonce2 = authscheme.getCnonce();
549 final String sessionKey2 = authscheme.getA1();
550
551 Assertions.assertEquals(cnonce1, cnonce2);
552 Assertions.assertEquals(sessionKey1, sessionKey2);
553
554 final String challenge2 = StandardAuthScheme.DIGEST + " qop=\"auth\", algorithm=MD5-sess, nonce=\"1234567890abcdef\", " +
555 "charset=utf-8, realm=\"subnet.domain.com\"";
556 final AuthChallenge authChallenge2 = parse(challenge2);
557 authscheme.processChallenge(authChallenge2, null);
558 Assertions.assertTrue(authscheme.isResponseReady(host, credentialsProvider, null));
559 final String authResponse3 = authscheme.generateAuthResponse(host, request, null);
560 final Map<String, String> table3 = parseAuthResponse(authResponse3);
561 Assertions.assertEquals("00000003", table3.get("nc"));
562
563 final String cnonce3 = authscheme.getCnonce();
564 final String sessionKey3 = authscheme.getA1();
565
566 Assertions.assertEquals(cnonce1, cnonce3);
567 Assertions.assertEquals(sessionKey1, sessionKey3);
568
569 final String challenge3 = StandardAuthScheme.DIGEST + " qop=\"auth\", algorithm=MD5-sess, nonce=\"fedcba0987654321\", " +
570 "charset=utf-8, realm=\"subnet.domain.com\"";
571 final AuthChallenge authChallenge3 = parse(challenge3);
572 authscheme.processChallenge(authChallenge3, null);
573 Assertions.assertTrue(authscheme.isResponseReady(host, credentialsProvider, null));
574 final String authResponse4 = authscheme.generateAuthResponse(host, request, null);
575 final Map<String, String> table4 = parseAuthResponse(authResponse4);
576 Assertions.assertEquals("00000001", table4.get("nc"));
577
578 final String cnonce4 = authscheme.getCnonce();
579 final String sessionKey4 = authscheme.getA1();
580
581 Assertions.assertNotEquals(cnonce1, cnonce4);
582 Assertions.assertNotEquals(sessionKey1, sessionKey4);
583 }
584
585 @Test
586 public void testHttpEntityDigest() throws Exception {
587 final HttpEntityDigester digester = new HttpEntityDigester(MessageDigest.getInstance("MD5"));
588 Assertions.assertNull(digester.getDigest());
589 digester.write('a');
590 digester.write('b');
591 digester.write('c');
592 digester.write(0xe4);
593 digester.write(0xf6);
594 digester.write(0xfc);
595 digester.write(new byte[] { 'a', 'b', 'c'});
596 Assertions.assertNull(digester.getDigest());
597 digester.close();
598 Assertions.assertEquals("acd2b59cd01c7737d8069015584c6cac", DigestScheme.formatHex(digester.getDigest()));
599 Assertions.assertThrows(IOException.class, () -> digester.write('a'));
600 Assertions.assertThrows(IOException.class, () -> digester.write(new byte[] { 'a', 'b', 'c'}));
601 }
602
603 @Test
604 public void testDigestAuthenticationQopAuthInt() throws Exception {
605 final ClassicHttpRequest request = new BasicClassicHttpRequest("Post", "/");
606 request.setEntity(new StringEntity("abc\u00e4\u00f6\u00fcabc", StandardCharsets.ISO_8859_1));
607 final HttpHost host = new HttpHost("somehost", 80);
608 final CredentialsProvider credentialsProvider = CredentialsProviderBuilder.create()
609 .add(new AuthScope(host, "realm1", null), "username", "password".toCharArray())
610 .build();
611
612 final String challenge = StandardAuthScheme.DIGEST + " realm=\"realm1\", nonce=\"f2a3f18799759d4f1a1c068b92b573cb\", " +
613 "qop=\"auth,auth-int\"";
614 final AuthChallenge authChallenge = parse(challenge);
615 final DigestScheme authscheme = new DigestScheme();
616 authscheme.processChallenge(authChallenge, null);
617 Assertions.assertTrue(authscheme.isResponseReady(host, credentialsProvider, null));
618 final String authResponse = authscheme.generateAuthResponse(host, request, null);
619
620 Assertions.assertEquals("Post:/:acd2b59cd01c7737d8069015584c6cac", authscheme.getA2());
621
622 final Map<String, String> table = parseAuthResponse(authResponse);
623 Assertions.assertEquals("username", table.get("username"));
624 Assertions.assertEquals("realm1", table.get("realm"));
625 Assertions.assertEquals("/", table.get("uri"));
626 Assertions.assertEquals("auth-int", table.get("qop"));
627 Assertions.assertEquals("f2a3f18799759d4f1a1c068b92b573cb", table.get("nonce"));
628 }
629
630 @Test
631 public void testDigestAuthenticationQopAuthIntNullEntity() throws Exception {
632 final HttpRequest request = new BasicHttpRequest("Post", "/");
633 final HttpHost host = new HttpHost("somehost", 80);
634 final CredentialsProvider credentialsProvider = CredentialsProviderBuilder.create()
635 .add(new AuthScope(host, "realm1", null), "username", "password".toCharArray())
636 .build();
637
638 final String challenge = StandardAuthScheme.DIGEST + " realm=\"realm1\", nonce=\"f2a3f18799759d4f1a1c068b92b573cb\", " +
639 "qop=\"auth-int\"";
640 final AuthChallenge authChallenge = parse(challenge);
641 final DigestScheme authscheme = new DigestScheme();
642 authscheme.processChallenge(authChallenge, null);
643 Assertions.assertTrue(authscheme.isResponseReady(host, credentialsProvider, null));
644 final String authResponse = authscheme.generateAuthResponse(host, request, null);
645
646 Assertions.assertEquals("Post:/:d41d8cd98f00b204e9800998ecf8427e", authscheme.getA2());
647
648 final Map<String, String> table = parseAuthResponse(authResponse);
649 Assertions.assertEquals("username", table.get("username"));
650 Assertions.assertEquals("realm1", table.get("realm"));
651 Assertions.assertEquals("/", table.get("uri"));
652 Assertions.assertEquals("auth-int", table.get("qop"));
653 Assertions.assertEquals("f2a3f18799759d4f1a1c068b92b573cb", table.get("nonce"));
654 }
655
656 @Test
657 public void testDigestAuthenticationQopAuthOrAuthIntNonRepeatableEntity() throws Exception {
658 final ClassicHttpRequest request = new BasicClassicHttpRequest("Post", "/");
659 request.setEntity(new InputStreamEntity(new ByteArrayInputStream(new byte[] {'a'}), -1, ContentType.DEFAULT_TEXT));
660 final HttpHost host = new HttpHost("somehost", 80);
661 final CredentialsProvider credentialsProvider = CredentialsProviderBuilder.create()
662 .add(new AuthScope(host, "realm1", null), "username", "password".toCharArray())
663 .build();
664
665 final String challenge = StandardAuthScheme.DIGEST + " realm=\"realm1\", nonce=\"f2a3f18799759d4f1a1c068b92b573cb\", " +
666 "qop=\"auth,auth-int\"";
667 final AuthChallenge authChallenge = parse(challenge);
668 final DigestScheme authscheme = new DigestScheme();
669 authscheme.processChallenge(authChallenge, null);
670 Assertions.assertTrue(authscheme.isResponseReady(host, credentialsProvider, null));
671 final String authResponse = authscheme.generateAuthResponse(host, request, null);
672
673 Assertions.assertEquals("Post:/", authscheme.getA2());
674
675 final Map<String, String> table = parseAuthResponse(authResponse);
676 Assertions.assertEquals("username", table.get("username"));
677 Assertions.assertEquals("realm1", table.get("realm"));
678 Assertions.assertEquals("/", table.get("uri"));
679 Assertions.assertEquals("auth", table.get("qop"));
680 Assertions.assertEquals("f2a3f18799759d4f1a1c068b92b573cb", table.get("nonce"));
681 }
682
683 @Test
684 public void testParameterCaseSensitivity() throws Exception {
685 final HttpRequest request = new BasicHttpRequest("GET", "/");
686 final HttpHost host = new HttpHost("somehost", 80);
687 final CredentialsProvider credentialsProvider = CredentialsProviderBuilder.create()
688 .add(new AuthScope(host, "-", null), "username", "password".toCharArray())
689 .build();
690
691 final String challenge = StandardAuthScheme.DIGEST + " Realm=\"-\", " +
692 "nonce=\"YjYuNGYyYmJhMzUuY2I5ZDhlZDE5M2ZlZDM 1Mjk3NGJkNTIyYjgyNTcwMjQ=\", " +
693 "opaque=\"98700A3D9CE17065E2246B41035C6609\", qop=\"auth\"";
694 final AuthChallenge authChallenge = parse(challenge);
695 final DigestScheme authscheme = new DigestScheme();
696 authscheme.processChallenge(authChallenge, null);
697 Assertions.assertEquals("-", authscheme.getRealm());
698
699 Assertions.assertTrue(authscheme.isResponseReady(host, credentialsProvider, null));
700 authscheme.generateAuthResponse(host, request, null);
701 }
702
703 @Test
704 public void testDigestAuthenticationQopIntOnlyNonRepeatableEntity() throws Exception {
705 final ClassicHttpRequest request = new BasicClassicHttpRequest("Post", "/");
706 request.setEntity(new InputStreamEntity(new ByteArrayInputStream(new byte[] {'a'}), -1, ContentType.DEFAULT_TEXT));
707 final HttpHost host = new HttpHost("somehost", 80);
708 final CredentialsProvider credentialsProvider = CredentialsProviderBuilder.create()
709 .add(new AuthScope(host, "realm1", null), "username", "password".toCharArray())
710 .build();
711
712 final String challenge = StandardAuthScheme.DIGEST + " realm=\"realm1\", nonce=\"f2a3f18799759d4f1a1c068b92b573cb\", " +
713 "qop=\"auth-int\"";
714 final AuthChallenge authChallenge = parse(challenge);
715 final DigestScheme authscheme = new DigestScheme();
716 authscheme.processChallenge(authChallenge, null);
717
718 Assertions.assertTrue(authscheme.isResponseReady(host, credentialsProvider, null));
719 Assertions.assertThrows(AuthenticationException.class, () ->
720 authscheme.generateAuthResponse(host, request, null));
721 }
722
723 @Test
724 public void testSerialization() throws Exception {
725 final String challenge = StandardAuthScheme.DIGEST + " realm=\"realm1\", nonce=\"f2a3f18799759d4f1a1c068b92b573cb\", " +
726 "qop=\"auth,auth-int\"";
727 final AuthChallenge authChallenge = parse(challenge);
728 final DigestScheme digestScheme = new DigestScheme();
729 digestScheme.processChallenge(authChallenge, null);
730
731 final ByteArrayOutputStream buffer = new ByteArrayOutputStream();
732 final ObjectOutputStream out = new ObjectOutputStream(buffer);
733 out.writeObject(digestScheme);
734 out.flush();
735 final byte[] raw = buffer.toByteArray();
736 final ObjectInputStream in = new ObjectInputStream(new ByteArrayInputStream(raw));
737 final DigestScheme authScheme = (DigestScheme) in.readObject();
738
739 Assertions.assertEquals(digestScheme.getName(), authScheme.getName());
740 Assertions.assertEquals(digestScheme.getRealm(), authScheme.getRealm());
741 Assertions.assertEquals(digestScheme.isChallengeComplete(), authScheme.isChallengeComplete());
742 Assertions.assertEquals(digestScheme.getA1(), authScheme.getA1());
743 Assertions.assertEquals(digestScheme.getA2(), authScheme.getA2());
744 Assertions.assertEquals(digestScheme.getCnonce(), authScheme.getCnonce());
745 }
746
747 }