2424
2525import java .nio .charset .Charset ;
2626import java .security .MessageDigest ;
27+ import java .util .Arrays ;
2728import java .util .Map ;
2829import java .util .concurrent .ThreadLocalRandom ;
2930
3031import static java .nio .charset .StandardCharsets .ISO_8859_1 ;
3132import static java .nio .charset .StandardCharsets .UTF_8 ;
3233import static java .util .Objects .requireNonNull ;
3334import static org .asynchttpclient .util .HttpConstants .Methods .GET ;
34- import static org .asynchttpclient .util .MessageDigestUtils .pooledMd5MessageDigest ;
3535import static org .asynchttpclient .util .MiscUtils .isNonEmpty ;
3636import static org .asynchttpclient .util .StringUtils .appendBase16 ;
3737import static org .asynchttpclient .util .StringUtils .toHexString ;
@@ -272,17 +272,19 @@ public static class Builder {
272272 private String methodName = GET ;
273273 private boolean usePreemptive ;
274274 private String ntlmDomain = System .getProperty ("http.auth.ntlm.domain" );
275- private Charset charset = UTF_8 ;
275+ private static Charset charset = UTF_8 ;
276276 private String ntlmHost = "localhost" ;
277277 private boolean useAbsoluteURI ;
278278 private boolean omitQuery ;
279+ private Charset digestCharset = ISO_8859_1 ; // RFC default
279280 /**
280281 * Kerberos/Spnego properties
281282 */
282283 private @ Nullable Map <String , String > customLoginConfig ;
283284 private @ Nullable String servicePrincipalName ;
284285 private boolean useCanonicalHostname ;
285286 private @ Nullable String loginContextName ;
287+ private @ Nullable String cs ;
286288
287289 public Builder () {
288290 principal = null ;
@@ -425,6 +427,10 @@ public Builder parseWWWAuthenticateHeader(String headerLine) {
425427 .setOpaque (match (headerLine , "opaque" ))
426428 .setScheme (isNonEmpty (nonce ) ? AuthScheme .DIGEST : AuthScheme .BASIC );
427429 String algorithm = match (headerLine , "algorithm" );
430+ String cs = match (headerLine , "charset" );
431+ if ("UTF-8" .equalsIgnoreCase (cs )) {
432+ this .digestCharset = UTF_8 ;
433+ }
428434 if (isNonEmpty (algorithm )) {
429435 setAlgorithm (algorithm );
430436 }
@@ -471,17 +477,20 @@ public Builder parseProxyAuthenticateHeader(String headerLine) {
471477 private void newCnonce (MessageDigest md ) {
472478 byte [] b = new byte [8 ];
473479 ThreadLocalRandom .current ().nextBytes (b );
474- b = md .digest (b );
475- cnonce = toHexString (b );
480+ byte [] full = md .digest (b );
481+ // trim to first 8 bytes → 16 hex chars
482+ byte [] small = Arrays .copyOf (full , Math .min (8 , full .length ));
483+ cnonce = toHexString (small );
476484 }
477485
478- private static byte [] digestFromRecycledStringBuilder (StringBuilder sb , MessageDigest md ) {
479- md .update (StringUtils .charSequence2ByteBuffer (sb , ISO_8859_1 ));
486+ private static byte [] digestFromRecycledStringBuilder (StringBuilder sb , MessageDigest md , Charset enc ) {
487+ md .update (StringUtils .charSequence2ByteBuffer (sb , enc ));
480488 sb .setLength (0 );
481489 return md .digest ();
482490 }
483491
484492 private static MessageDigest getDigestInstance (String algorithm ) {
493+ if ("SHA-512/256" .equalsIgnoreCase (algorithm )) algorithm = "SHA-512-256" ;
485494 if (algorithm == null || "MD5" .equalsIgnoreCase (algorithm ) || "MD5-sess" .equalsIgnoreCase (algorithm )) {
486495 return MessageDigestUtils .pooledMd5MessageDigest ();
487496 } else if ("SHA-256" .equalsIgnoreCase (algorithm ) || "SHA-256-sess" .equalsIgnoreCase (algorithm )) {
@@ -500,7 +509,7 @@ private byte[] ha1(StringBuilder sb, MessageDigest md) {
500509 // passwd ) ":" nonce-value ":" cnonce-value
501510
502511 sb .append (principal ).append (':' ).append (realmName ).append (':' ).append (password );
503- byte [] core = digestFromRecycledStringBuilder (sb , md );
512+ byte [] core = digestFromRecycledStringBuilder (sb , md , digestCharset );
504513
505514 if (algorithm == null || "MD5" .equalsIgnoreCase (algorithm ) || "SHA-256" .equalsIgnoreCase (algorithm ) || "SHA-512-256" .equalsIgnoreCase (algorithm )) {
506515 // A1 = username ":" realm-value ":" passwd
@@ -510,7 +519,7 @@ private byte[] ha1(StringBuilder sb, MessageDigest md) {
510519 // A1 = HASH(username ":" realm-value ":" passwd ) ":" nonce ":" cnonce
511520 appendBase16 (sb , core );
512521 sb .append (':' ).append (nonce ).append (':' ).append (cnonce );
513- return digestFromRecycledStringBuilder (sb , md );
522+ return digestFromRecycledStringBuilder (sb , md , digestCharset );
514523 }
515524 throw new UnsupportedOperationException ("Digest algorithm not supported: " + algorithm );
516525 }
@@ -530,7 +539,7 @@ private byte[] ha2(StringBuilder sb, String digestUri, MessageDigest md) {
530539 throw new UnsupportedOperationException ("Digest qop not supported: " + qop );
531540 }
532541
533- return digestFromRecycledStringBuilder (sb , md );
542+ return digestFromRecycledStringBuilder (sb , md , digestCharset );
534543 }
535544
536545 private void appendMiddlePart (StringBuilder sb ) {
@@ -557,7 +566,7 @@ private void newResponse(MessageDigest md) {
557566 appendMiddlePart (sb );
558567 appendBase16 (sb , ha2 );
559568
560- byte [] responseDigest = digestFromRecycledStringBuilder (sb , md );
569+ byte [] responseDigest = digestFromRecycledStringBuilder (sb , md , digestCharset );
561570 response = toHexString (responseDigest );
562571 }
563572 }
@@ -591,7 +600,7 @@ public Realm build() {
591600 cnonce ,
592601 uri ,
593602 usePreemptive ,
594- charset ,
603+ ( scheme == AuthScheme . DIGEST ? digestCharset : charset ) ,
595604 ntlmDomain ,
596605 ntlmHost ,
597606 useAbsoluteURI ,
0 commit comments