public static async Task <string> GetDigestTokenForCredential(NetworkCredential credential, HttpRequestMessage request, DigestResponse digestResponse) { StringBuilder sb = StringBuilderCache.Acquire(); // It is mandatory for servers to implement sha-256 per RFC 7616 // Keep MD5 for backward compatibility. string algorithm; bool isAlgorithmSpecified = digestResponse.Parameters.TryGetValue(Algorithm, out algorithm); if (isAlgorithmSpecified) { if (!algorithm.Equals(Sha256, StringComparison.OrdinalIgnoreCase) && !algorithm.Equals(Md5, StringComparison.OrdinalIgnoreCase) && !algorithm.Equals(Sha256Sess, StringComparison.OrdinalIgnoreCase) && !algorithm.Equals(MD5Sess, StringComparison.OrdinalIgnoreCase)) { if (NetEventSource.IsEnabled) { NetEventSource.Error(digestResponse, "Algorithm not supported: {algorithm}"); } return(null); } } else { algorithm = Md5; } // Check if nonce is there in challenge string nonce; if (!digestResponse.Parameters.TryGetValue(Nonce, out nonce)) { if (NetEventSource.IsEnabled) { NetEventSource.Error(digestResponse, "Nonce missing"); } return(null); } // opaque token may or may not exist string opaque; digestResponse.Parameters.TryGetValue(Opaque, out opaque); string realm; if (!digestResponse.Parameters.TryGetValue(Realm, out realm)) { if (NetEventSource.IsEnabled) { NetEventSource.Error(digestResponse, "Realm missing"); } return(null); } // Add username string userhash; if (digestResponse.Parameters.TryGetValue(UserHash, out userhash) && userhash == "true") { sb.AppendKeyValue(Username, ComputeHash(credential.UserName + ":" + realm, algorithm)); sb.AppendKeyValue(UserHash, userhash, includeQuotes: false); } else { if (HeaderUtilities.ContainsNonAscii(credential.UserName)) { string usernameStar = HeaderUtilities.Encode5987(credential.UserName); sb.AppendKeyValue(UsernameStar, usernameStar, includeQuotes: false); } else { sb.AppendKeyValue(Username, credential.UserName); } } // Add realm if (realm != string.Empty) { sb.AppendKeyValue(Realm, realm); } // Add nonce sb.AppendKeyValue(Nonce, nonce); // Add uri sb.AppendKeyValue(Uri, request.RequestUri.PathAndQuery); // Set qop, default is auth string qop = Auth; bool isQopSpecified = digestResponse.Parameters.ContainsKey(Qop); if (isQopSpecified) { // Check if auth-int present in qop string int index1 = digestResponse.Parameters[Qop].IndexOf(AuthInt, StringComparison.Ordinal); if (index1 != -1) { // Get index of auth if present in qop string int index2 = digestResponse.Parameters[Qop].IndexOf(Auth, StringComparison.Ordinal); // If index2 < index1, auth option is available // If index2 == index1, check if auth option available later in string after auth-int. if (index2 == index1) { index2 = digestResponse.Parameters[Qop].IndexOf(Auth, index1 + AuthInt.Length, StringComparison.Ordinal); if (index2 == -1) { qop = AuthInt; } } } } // Set cnonce string cnonce = GetRandomAlphaNumericString(); // Calculate response string a1 = credential.UserName + ":" + realm + ":" + credential.Password; if (algorithm.EndsWith("sess", StringComparison.OrdinalIgnoreCase)) { a1 = ComputeHash(a1, algorithm) + ":" + nonce + ":" + cnonce; } string a2 = request.Method.Method + ":" + request.RequestUri.PathAndQuery; if (qop == AuthInt) { string content = request.Content == null ? string.Empty : await request.Content.ReadAsStringAsync().ConfigureAwait(false); a2 = a2 + ":" + ComputeHash(content, algorithm); } string response; if (isQopSpecified) { response = ComputeHash(ComputeHash(a1, algorithm) + ":" + nonce + ":" + DigestResponse.NonceCount + ":" + cnonce + ":" + qop + ":" + ComputeHash(a2, algorithm), algorithm); } else { response = ComputeHash(ComputeHash(a1, algorithm) + ":" + nonce + ":" + ComputeHash(a2, algorithm), algorithm); } // Add response sb.AppendKeyValue(Response, response, includeComma: opaque != null || isAlgorithmSpecified || isQopSpecified); // Add opaque if (opaque != null) { sb.AppendKeyValue(Opaque, opaque, includeComma: isAlgorithmSpecified || isQopSpecified); } if (isAlgorithmSpecified) { // Add algorithm sb.AppendKeyValue(Algorithm, algorithm, includeQuotes: false, includeComma: isQopSpecified); } if (isQopSpecified) { // Add qop sb.AppendKeyValue(Qop, qop, includeQuotes: false); // Add nc sb.AppendKeyValue(NC, DigestResponse.NonceCount, includeQuotes: false); // Add cnonce sb.AppendKeyValue(CNonce, cnonce, includeComma: false); } return(StringBuilderCache.GetStringAndRelease(sb)); }