2323import java .util .stream .Collectors ;
2424import java .util .stream .Stream ;
2525
26+ import static com .azure .core .util .CoreUtils .bytesToHexString ;
27+
2628/**
2729 * This class handles Basic and Digest authorization challenges, complying to RFC 2617 and RFC 7616.
2830 */
2931public class AuthorizationChallengeHandler {
30- /*
31- * RFC 2617 and 7616 specifies these characters to use when creating a hex string.
32- */
33- private static final char [] HEX_CHARACTERS = "0123456789abcdef" .toCharArray ();
34-
3532 private static final String BASIC = "Basic " ;
3633 private static final String DIGEST = "Digest " ;
3734
@@ -274,8 +271,8 @@ private String createDigestAuthorizationHeader(String method, String uri, Map<St
274271 }
275272
276273 String ha1 = algorithm .endsWith (SESS )
277- ? calculateHa1Sess (digestFunction , realm , nonce , clientNonce )
278- : calculateHa1NoSess (digestFunction , realm );
274+ ? calculateHa1Sess (digestFunction , username , realm , password , nonce , clientNonce )
275+ : calculateHa1NoSess (digestFunction , username , realm , password );
279276
280277 String ha2 = AUTH_INT .equals (qop )
281278 ? calculateHa2AuthIntQop (digestFunction , method , uri , entityBodySupplier .get ())
@@ -285,7 +282,7 @@ private String createDigestAuthorizationHeader(String method, String uri, Map<St
285282 ? calculateResponseKnownQop (digestFunction , ha1 , nonce , nc , clientNonce , qop , ha2 )
286283 : calculateResponseUnknownQop (digestFunction , ha1 , nonce , ha2 );
287284
288- String headerUsername = (hashUsername ) ? calculateUserhash (digestFunction , realm ) : username ;
285+ String headerUsername = (hashUsername ) ? calculateUserhash (digestFunction , username , realm ) : username ;
289286
290287 return buildAuthorizationHeader (headerUsername , realm , uri , algorithm , nonce , nc , clientNonce , qop , response ,
291288 opaque , hashUsername );
@@ -329,9 +326,10 @@ private String getQop(String qopHeader) {
329326 * - Create the digest of (username + ":" + realm + ":" password).
330327 * - Return the resulting bytes as a hex string.
331328 */
332- private String calculateHa1NoSess (Function <byte [], byte []> digestFunction , String realm ) {
333- return hexStringOf (digestFunction .apply (String .format ("%s:%s:%s" , username , realm , password )
334- .getBytes (StandardCharsets .UTF_8 )));
329+ private static String calculateHa1NoSess (Function <byte [], byte []> digestFunction , String username , String realm ,
330+ String password ) {
331+ return bytesToHexString (digestFunction .apply ((
332+ username + ":" + realm + ":" + password ).getBytes (StandardCharsets .UTF_8 )));
335333 }
336334
337335 /*
@@ -343,10 +341,12 @@ private String calculateHa1NoSess(Function<byte[], byte[]> digestFunction, Strin
343341 * - Create the digest of (userPassHex + ":" nonce + ":" + cnonce).
344342 * - Return the resulting bytes as a hex string.
345343 */
346- private String calculateHa1Sess (Function <byte [], byte []> digestFunction , String realm , String nonce ,
347- String cnonce ) {
348- return hexStringOf (digestFunction .apply (String .format ("%s:%s:%s" , calculateHa1NoSess (digestFunction , realm ),
349- nonce , cnonce ).getBytes (StandardCharsets .UTF_8 )));
344+ private static String calculateHa1Sess (Function <byte [], byte []> digestFunction , String username , String realm ,
345+ String password , String nonce , String cnonce ) {
346+ String ha1NoSess = calculateHa1NoSess (digestFunction , username , realm , password );
347+
348+ return bytesToHexString (digestFunction .apply (
349+ (ha1NoSess + ":" + nonce + ":" + cnonce ).getBytes (StandardCharsets .UTF_8 )));
350350 }
351351
352352 /*
@@ -356,9 +356,9 @@ private String calculateHa1Sess(Function<byte[], byte[]> digestFunction, String
356356 * - Create the digest of (httpMethod + ":" + uri).
357357 * - Return the resulting bytes as a hex string.
358358 */
359- private String calculateHa2AuthQopOrEmpty (Function <byte [], byte []> digestFunction , String httpMethod , String uri ) {
360- return hexStringOf ( digestFunction . apply ( String . format ( "%s:%s" , httpMethod , uri )
361- .getBytes (StandardCharsets .UTF_8 )));
359+ private static String calculateHa2AuthQopOrEmpty (Function <byte [], byte []> digestFunction , String httpMethod ,
360+ String uri ) {
361+ return bytesToHexString ( digestFunction . apply (( httpMethod + ":" + uri ) .getBytes (StandardCharsets .UTF_8 )));
362362 }
363363
364364 /*
@@ -375,10 +375,12 @@ private String calculateHa2AuthQopOrEmpty(Function<byte[], byte[]> digestFunctio
375375 * the body being replay-able this runs into risks when the body is very large and potentially consuming large
376376 * amounts of memory.
377377 */
378- private String calculateHa2AuthIntQop (Function <byte [], byte []> digestFunction , String httpMethod , String uri ,
378+ private static String calculateHa2AuthIntQop (Function <byte [], byte []> digestFunction , String httpMethod , String uri ,
379379 byte [] requestEntityBody ) {
380- return hexStringOf (digestFunction .apply (String .format ("%s:%s:%s" , httpMethod , uri ,
381- hexStringOf (digestFunction .apply (requestEntityBody ))).getBytes (StandardCharsets .UTF_8 )));
380+ String bodyHex = bytesToHexString (digestFunction .apply (requestEntityBody ));
381+
382+ return bytesToHexString (digestFunction .apply (
383+ (httpMethod + ":" + uri + ":" + bodyHex ).getBytes (StandardCharsets .UTF_8 )));
382384 }
383385
384386 /*
@@ -388,10 +390,9 @@ private String calculateHa2AuthIntQop(Function<byte[], byte[]> digestFunction, S
388390 * - Create the digest of (ha1 + ":" + nonce + ":" + ha2).
389391 * - Return the resulting bytes as a hex string.
390392 */
391- private String calculateResponseUnknownQop (Function <byte [], byte []> digestFunction , String ha1 , String nonce ,
393+ private static String calculateResponseUnknownQop (Function <byte [], byte []> digestFunction , String ha1 , String nonce ,
392394 String ha2 ) {
393- return hexStringOf (digestFunction .apply (String .format ("%s:%s:%s" , ha1 , nonce , ha2 )
394- .getBytes (StandardCharsets .UTF_8 )));
395+ return bytesToHexString (digestFunction .apply ((ha1 + ":" + nonce + ":" + ha2 ).getBytes (StandardCharsets .UTF_8 )));
395396 }
396397
397398 /*
@@ -403,18 +404,20 @@ private String calculateResponseUnknownQop(Function<byte[], byte[]> digestFuncti
403404 *
404405 * nc, nonce count, is represented in a hexadecimal format.
405406 */
406- private String calculateResponseKnownQop (Function <byte [], byte []> digestFunction , String ha1 , String nonce , int nc ,
407- String cnonce , String qop , String ha2 ) {
408- return hexStringOf (digestFunction .apply (String .format ("%s:%s:%08X:%s:%s:%s" , ha1 , nonce , nc , cnonce , qop , ha2 )
409- .getBytes (StandardCharsets .UTF_8 )));
407+ private static String calculateResponseKnownQop (Function <byte [], byte []> digestFunction , String ha1 , String nonce ,
408+ int nc , String cnonce , String qop , String ha2 ) {
409+ String zeroPadNc = String .format ("%08X" , nc );
410+
411+ return bytesToHexString (digestFunction .apply (
412+ (ha1 + ":" + nonce + ":" + zeroPadNc + ":" + cnonce + ":" + qop + ":" + ha2 )
413+ .getBytes (StandardCharsets .UTF_8 )));
410414 }
411415
412416 /*
413417 * Calculates the hashed username value if the authenticate challenge has 'userhash=true'.
414418 */
415- private String calculateUserhash (Function <byte [], byte []> digestFunction , String realm ) {
416- return hexStringOf (digestFunction .apply (String .format ("%s:%s" , username , realm )
417- .getBytes (StandardCharsets .UTF_8 )));
419+ private static String calculateUserhash (Function <byte [], byte []> digestFunction , String username , String realm ) {
420+ return bytesToHexString (digestFunction .apply ((username + ":" + realm ).getBytes (StandardCharsets .UTF_8 )));
418421 }
419422
420423 /*
@@ -461,7 +464,7 @@ private static Map<String, List<Map<String, String>>> partitionByChallengeType(
461464 String generateNonce () {
462465 byte [] nonce = new byte [16 ];
463466 new SecureRandom ().nextBytes (nonce );
464- return hexStringOf (nonce );
467+ return bytesToHexString (nonce );
465468 }
466469
467470 /*
@@ -479,47 +482,26 @@ private static String buildAuthorizationHeader(String username, String realm, St
479482 .append ("response=\" " ).append (response ).append ("\" " );
480483
481484 if (!CoreUtils .isNullOrEmpty (algorithm )) {
482- authorizationBuilder .append (", " ). append ( " algorithm=" ).append (algorithm );
485+ authorizationBuilder .append (", algorithm=" ).append (algorithm );
483486 }
484487
485488 if (!CoreUtils .isNullOrEmpty (cnonce )) {
486- authorizationBuilder .append (", " ). append ( " cnonce=\" " ).append (cnonce ).append ("\" " );
489+ authorizationBuilder .append (", cnonce=\" " ).append (cnonce ).append ("\" " );
487490 }
488491
489492 if (!CoreUtils .isNullOrEmpty (opaque )) {
490- authorizationBuilder .append (", " ). append ( " opaque=\" " ).append (opaque ).append ("\" " );
493+ authorizationBuilder .append (", opaque=\" " ).append (opaque ).append ("\" " );
491494 }
492495
493496 if (!CoreUtils .isNullOrEmpty (qop )) {
494- authorizationBuilder .append (", " ). append ( " qop=" ).append (qop );
495- authorizationBuilder .append (", " ). append ( " nc=" ).append (String .format ("%08X" , nc ));
497+ authorizationBuilder .append (", qop=" ).append (qop );
498+ authorizationBuilder .append (", nc=" ).append (java . lang . String .format ("%08X" , nc ));
496499 }
497500
498501 if (userhash ) {
499- authorizationBuilder .append (", " ). append ( " userhash=" ). append ( true );
502+ authorizationBuilder .append (", userhash=true" );
500503 }
501504
502505 return authorizationBuilder .toString ();
503506 }
504-
505- /*
506- * Converts the passed byte array into a hex string.
507- */
508- private static String hexStringOf (byte [] bytes ) {
509- // Hex uses 4 bits, converting a byte to hex will double its size.
510- char [] hexCharacters = new char [bytes .length * 2 ];
511-
512- for (int i = 0 ; i < bytes .length ; i ++) {
513- // Convert the byte into an integer, masking all but the last 8 bits (the byte).
514- int b = bytes [i ] & 0xFF ;
515-
516- // Shift 4 times to the right to get the leading 4 bits and get the corresponding hex character.
517- hexCharacters [i * 2 ] = HEX_CHARACTERS [b >>> 4 ];
518-
519- // Mask all but the last 4 bits and get the corresponding hex character.
520- hexCharacters [i * 2 + 1 ] = HEX_CHARACTERS [b & 0x0F ];
521- }
522-
523- return new String (hexCharacters );
524- }
525507}
0 commit comments