24
24
25
25
import java .nio .charset .Charset ;
26
26
import java .security .MessageDigest ;
27
+ import java .util .Arrays ;
27
28
import java .util .Map ;
28
29
import java .util .concurrent .ThreadLocalRandom ;
29
30
30
31
import static java .nio .charset .StandardCharsets .ISO_8859_1 ;
31
32
import static java .nio .charset .StandardCharsets .UTF_8 ;
32
33
import static java .util .Objects .requireNonNull ;
33
34
import static org .asynchttpclient .util .HttpConstants .Methods .GET ;
34
- import static org .asynchttpclient .util .MessageDigestUtils .pooledMd5MessageDigest ;
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 ;
@@ -272,17 +272,19 @@ public static class Builder {
272
272
private String methodName = GET ;
273
273
private boolean usePreemptive ;
274
274
private String ntlmDomain = System .getProperty ("http.auth.ntlm.domain" );
275
- private Charset charset = UTF_8 ;
275
+ private static Charset charset = UTF_8 ;
276
276
private String ntlmHost = "localhost" ;
277
277
private boolean useAbsoluteURI ;
278
278
private boolean omitQuery ;
279
+ private Charset digestCharset = ISO_8859_1 ; // RFC default
279
280
/**
280
281
* Kerberos/Spnego properties
281
282
*/
282
283
private @ Nullable Map <String , String > customLoginConfig ;
283
284
private @ Nullable String servicePrincipalName ;
284
285
private boolean useCanonicalHostname ;
285
286
private @ Nullable String loginContextName ;
287
+ private @ Nullable String cs ;
286
288
287
289
public Builder () {
288
290
principal = null ;
@@ -425,6 +427,10 @@ public Builder parseWWWAuthenticateHeader(String headerLine) {
425
427
.setOpaque (match (headerLine , "opaque" ))
426
428
.setScheme (isNonEmpty (nonce ) ? AuthScheme .DIGEST : AuthScheme .BASIC );
427
429
String algorithm = match (headerLine , "algorithm" );
430
+ String cs = match (headerLine , "charset" );
431
+ if ("UTF-8" .equalsIgnoreCase (cs )) {
432
+ this .digestCharset = UTF_8 ;
433
+ }
428
434
if (isNonEmpty (algorithm )) {
429
435
setAlgorithm (algorithm );
430
436
}
@@ -471,17 +477,20 @@ public Builder parseProxyAuthenticateHeader(String headerLine) {
471
477
private void newCnonce (MessageDigest md ) {
472
478
byte [] b = new byte [8 ];
473
479
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 );
476
484
}
477
485
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 ));
480
488
sb .setLength (0 );
481
489
return md .digest ();
482
490
}
483
491
484
492
private static MessageDigest getDigestInstance (String algorithm ) {
493
+ if ("SHA-512/256" .equalsIgnoreCase (algorithm )) algorithm = "SHA-512-256" ;
485
494
if (algorithm == null || "MD5" .equalsIgnoreCase (algorithm ) || "MD5-sess" .equalsIgnoreCase (algorithm )) {
486
495
return MessageDigestUtils .pooledMd5MessageDigest ();
487
496
} else if ("SHA-256" .equalsIgnoreCase (algorithm ) || "SHA-256-sess" .equalsIgnoreCase (algorithm )) {
@@ -500,7 +509,7 @@ private byte[] ha1(StringBuilder sb, MessageDigest md) {
500
509
// passwd ) ":" nonce-value ":" cnonce-value
501
510
502
511
sb .append (principal ).append (':' ).append (realmName ).append (':' ).append (password );
503
- byte [] core = digestFromRecycledStringBuilder (sb , md );
512
+ byte [] core = digestFromRecycledStringBuilder (sb , md , digestCharset );
504
513
505
514
if (algorithm == null || "MD5" .equalsIgnoreCase (algorithm ) || "SHA-256" .equalsIgnoreCase (algorithm ) || "SHA-512-256" .equalsIgnoreCase (algorithm )) {
506
515
// A1 = username ":" realm-value ":" passwd
@@ -510,7 +519,7 @@ private byte[] ha1(StringBuilder sb, MessageDigest md) {
510
519
// A1 = HASH(username ":" realm-value ":" passwd ) ":" nonce ":" cnonce
511
520
appendBase16 (sb , core );
512
521
sb .append (':' ).append (nonce ).append (':' ).append (cnonce );
513
- return digestFromRecycledStringBuilder (sb , md );
522
+ return digestFromRecycledStringBuilder (sb , md , digestCharset );
514
523
}
515
524
throw new UnsupportedOperationException ("Digest algorithm not supported: " + algorithm );
516
525
}
@@ -530,7 +539,7 @@ private byte[] ha2(StringBuilder sb, String digestUri, MessageDigest md) {
530
539
throw new UnsupportedOperationException ("Digest qop not supported: " + qop );
531
540
}
532
541
533
- return digestFromRecycledStringBuilder (sb , md );
542
+ return digestFromRecycledStringBuilder (sb , md , digestCharset );
534
543
}
535
544
536
545
private void appendMiddlePart (StringBuilder sb ) {
@@ -557,7 +566,7 @@ private void newResponse(MessageDigest md) {
557
566
appendMiddlePart (sb );
558
567
appendBase16 (sb , ha2 );
559
568
560
- byte [] responseDigest = digestFromRecycledStringBuilder (sb , md );
569
+ byte [] responseDigest = digestFromRecycledStringBuilder (sb , md , digestCharset );
561
570
response = toHexString (responseDigest );
562
571
}
563
572
}
@@ -591,7 +600,7 @@ public Realm build() {
591
600
cnonce ,
592
601
uri ,
593
602
usePreemptive ,
594
- charset ,
603
+ ( scheme == AuthScheme . DIGEST ? digestCharset : charset ) ,
595
604
ntlmDomain ,
596
605
ntlmHost ,
597
606
useAbsoluteURI ,
0 commit comments