3535import static org .asynchttpclient .util .MiscUtils .isNonEmpty ;
3636import static org .asynchttpclient .util .StringUtils .appendBase16 ;
3737import static org .asynchttpclient .util .StringUtils .toHexString ;
38+ import org .asynchttpclient .util .MessageDigestUtils ;
3839
3940/**
4041 * This class is required when authentication is needed. The class support
@@ -452,62 +453,65 @@ public Builder parseProxyAuthenticateHeader(String headerLine) {
452453 return this ;
453454 }
454455
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+
455471 private void newCnonce (MessageDigest md ) {
456472 byte [] b = new byte [8 ];
457473 ThreadLocalRandom .current ().nextBytes (b );
458474 b = md .digest (b );
459475 cnonce = toHexString (b );
460476 }
461477
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 ) {
486479 md .update (StringUtils .charSequence2ByteBuffer (sb , ISO_8859_1 ));
487480 sb .setLength (0 );
488481 return md .digest ();
489482 }
490483
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+
491496 private byte [] ha1 (StringBuilder sb , MessageDigest md ) {
492497 // if algorithm is "MD5" or is unspecified => A1 = username ":" realm-value ":"
493498 // passwd
494499 // if algorithm is "MD5-sess" => A1 = MD5( username-value ":" realm-value ":"
495500 // passwd ) ":" nonce-value ":" cnonce-value
496501
497502 sb .append (principal ).append (':' ).append (realmName ).append (':' ).append (password );
498- byte [] core = md5FromRecycledStringBuilder (sb , md );
503+ byte [] core = digestFromRecycledStringBuilder (sb , md );
499504
500- if (algorithm == null || "MD5" .equals (algorithm )) {
505+ if (algorithm == null || "MD5" .equalsIgnoreCase ( algorithm ) || "SHA-256" . equalsIgnoreCase ( algorithm ) || "SHA-512-256" . equalsIgnoreCase (algorithm )) {
501506 // A1 = username ":" realm-value ":" passwd
502507 return core ;
503508 }
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
506511 appendBase16 (sb , core );
507512 sb .append (':' ).append (nonce ).append (':' ).append (cnonce );
508- return md5FromRecycledStringBuilder (sb , md );
513+ return digestFromRecycledStringBuilder (sb , md );
509514 }
510-
511515 throw new UnsupportedOperationException ("Digest algorithm not supported: " + algorithm );
512516 }
513517
@@ -526,7 +530,7 @@ private byte[] ha2(StringBuilder sb, String digestUri, MessageDigest md) {
526530 throw new UnsupportedOperationException ("Digest qop not supported: " + qop );
527531 }
528532
529- return md5FromRecycledStringBuilder (sb , md );
533+ return digestFromRecycledStringBuilder (sb , md );
530534 }
531535
532536 private void appendMiddlePart (StringBuilder sb ) {
@@ -553,7 +557,7 @@ private void newResponse(MessageDigest md) {
553557 appendMiddlePart (sb );
554558 appendBase16 (sb , ha2 );
555559
556- byte [] responseDigest = md5FromRecycledStringBuilder (sb , md );
560+ byte [] responseDigest = digestFromRecycledStringBuilder (sb , md );
557561 response = toHexString (responseDigest );
558562 }
559563 }
@@ -567,7 +571,9 @@ public Realm build() {
567571
568572 // Avoid generating
569573 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 );
571577 newCnonce (md );
572578 newResponse (md );
573579 }
0 commit comments