35
35
import static org .asynchttpclient .util .MiscUtils .isNonEmpty ;
36
36
import static org .asynchttpclient .util .StringUtils .appendBase16 ;
37
37
import static org .asynchttpclient .util .StringUtils .toHexString ;
38
+ import org .asynchttpclient .util .MessageDigestUtils ;
38
39
39
40
/**
40
41
* This class is required when authentication is needed. The class support
@@ -452,62 +453,65 @@ public Builder parseProxyAuthenticateHeader(String headerLine) {
452
453
return this ;
453
454
}
454
455
456
+ /**
457
+ * Extracts the value of a token from a WWW-Authenticate or Proxy-Authenticate header line.
458
+ * Example: match('Digest realm="test", nonce="abc"', "realm") returns "test"
459
+ */
460
+ private static @ Nullable String match (String headerLine , String token ) {
461
+ if (headerLine == null || token == null ) return null ;
462
+ String pattern = token + "=\" " ;
463
+ int start = headerLine .indexOf (pattern );
464
+ if (start == -1 ) return null ;
465
+ start += pattern .length ();
466
+ int end = headerLine .indexOf ('"' , start );
467
+ if (end == -1 ) return null ;
468
+ return headerLine .substring (start , end );
469
+ }
470
+
455
471
private void newCnonce (MessageDigest md ) {
456
472
byte [] b = new byte [8 ];
457
473
ThreadLocalRandom .current ().nextBytes (b );
458
474
b = md .digest (b );
459
475
cnonce = toHexString (b );
460
476
}
461
477
462
- /**
463
- * TODO: A Pattern/Matcher may be better.
464
- */
465
- private static @ Nullable String match (String headerLine , String token ) {
466
- if (headerLine == null ) {
467
- return null ;
468
- }
469
-
470
- int match = headerLine .indexOf (token );
471
- if (match <= 0 ) {
472
- return null ;
473
- }
474
-
475
- // = to skip
476
- match += token .length () + 1 ;
477
- int trailingComa = headerLine .indexOf (',' , match );
478
- String value = headerLine .substring (match , trailingComa > 0 ? trailingComa : headerLine .length ());
479
- value = value .length () > 0 && value .charAt (value .length () - 1 ) == '"'
480
- ? value .substring (0 , value .length () - 1 )
481
- : value ;
482
- return value .charAt (0 ) == '"' ? value .substring (1 ) : value ;
483
- }
484
-
485
- private static byte [] md5FromRecycledStringBuilder (StringBuilder sb , MessageDigest md ) {
478
+ private static byte [] digestFromRecycledStringBuilder (StringBuilder sb , MessageDigest md ) {
486
479
md .update (StringUtils .charSequence2ByteBuffer (sb , ISO_8859_1 ));
487
480
sb .setLength (0 );
488
481
return md .digest ();
489
482
}
490
483
484
+ private static MessageDigest getDigestInstance (String algorithm ) {
485
+ if (algorithm == null || "MD5" .equalsIgnoreCase (algorithm ) || "MD5-sess" .equalsIgnoreCase (algorithm )) {
486
+ return MessageDigestUtils .pooledMd5MessageDigest ();
487
+ } else if ("SHA-256" .equalsIgnoreCase (algorithm ) || "SHA-256-sess" .equalsIgnoreCase (algorithm )) {
488
+ return MessageDigestUtils .pooledSha256MessageDigest ();
489
+ } else if ("SHA-512-256" .equalsIgnoreCase (algorithm ) || "SHA-512-256-sess" .equalsIgnoreCase (algorithm )) {
490
+ return MessageDigestUtils .pooledSha512_256MessageDigest ();
491
+ } else {
492
+ throw new UnsupportedOperationException ("Digest algorithm not supported: " + algorithm );
493
+ }
494
+ }
495
+
491
496
private byte [] ha1 (StringBuilder sb , MessageDigest md ) {
492
497
// if algorithm is "MD5" or is unspecified => A1 = username ":" realm-value ":"
493
498
// passwd
494
499
// if algorithm is "MD5-sess" => A1 = MD5( username-value ":" realm-value ":"
495
500
// passwd ) ":" nonce-value ":" cnonce-value
496
501
497
502
sb .append (principal ).append (':' ).append (realmName ).append (':' ).append (password );
498
- byte [] core = md5FromRecycledStringBuilder (sb , md );
503
+ byte [] core = digestFromRecycledStringBuilder (sb , md );
499
504
500
- if (algorithm == null || "MD5" .equals (algorithm )) {
505
+ if (algorithm == null || "MD5" .equalsIgnoreCase ( algorithm ) || "SHA-256" . equalsIgnoreCase ( algorithm ) || "SHA-512-256" . equalsIgnoreCase (algorithm )) {
501
506
// A1 = username ":" realm-value ":" passwd
502
507
return core ;
503
508
}
504
- if ("MD5-sess" .equals (algorithm )) {
505
- // A1 = MD5 (username ":" realm-value ":" passwd ) ":" nonce ":" cnonce
509
+ if ("MD5-sess" .equalsIgnoreCase ( algorithm ) || "SHA-256-sess" . equalsIgnoreCase ( algorithm ) || "SHA-512-256-sess" . equalsIgnoreCase (algorithm )) {
510
+ // A1 = HASH (username ":" realm-value ":" passwd ) ":" nonce ":" cnonce
506
511
appendBase16 (sb , core );
507
512
sb .append (':' ).append (nonce ).append (':' ).append (cnonce );
508
- return md5FromRecycledStringBuilder (sb , md );
513
+ return digestFromRecycledStringBuilder (sb , md );
509
514
}
510
-
511
515
throw new UnsupportedOperationException ("Digest algorithm not supported: " + algorithm );
512
516
}
513
517
@@ -526,7 +530,7 @@ private byte[] ha2(StringBuilder sb, String digestUri, MessageDigest md) {
526
530
throw new UnsupportedOperationException ("Digest qop not supported: " + qop );
527
531
}
528
532
529
- return md5FromRecycledStringBuilder (sb , md );
533
+ return digestFromRecycledStringBuilder (sb , md );
530
534
}
531
535
532
536
private void appendMiddlePart (StringBuilder sb ) {
@@ -553,7 +557,7 @@ private void newResponse(MessageDigest md) {
553
557
appendMiddlePart (sb );
554
558
appendBase16 (sb , ha2 );
555
559
556
- byte [] responseDigest = md5FromRecycledStringBuilder (sb , md );
560
+ byte [] responseDigest = digestFromRecycledStringBuilder (sb , md );
557
561
response = toHexString (responseDigest );
558
562
}
559
563
}
@@ -567,7 +571,9 @@ public Realm build() {
567
571
568
572
// Avoid generating
569
573
if (isNonEmpty (nonce )) {
570
- MessageDigest md = pooledMd5MessageDigest ();
574
+ // Defensive: if algorithm is null, default to MD5
575
+ String algo = (algorithm != null ) ? algorithm : "MD5" ;
576
+ MessageDigest md = getDigestInstance (algo );
571
577
newCnonce (md );
572
578
newResponse (md );
573
579
}
0 commit comments