Esempio n. 1
0
        private byte[] DigestCalcResponse(DigestResponse result, byte[] ha1)
        {
            var md5            = new MD5Digest();
            var ha2            = new byte[md5.GetDigestSize()];
            var digestResponse = new byte[md5.GetDigestSize()];

            // HA2
            md5.BlockUpdate("AUTHENTICATE:".ToUtf8Bytes());
            md5.BlockUpdate(result.DigestUri.ToUtf8Bytes());
            if (result.QOP != QualityOfProtection.AuthenticationOnly)
            {
                md5.BlockUpdate(":00000000000000000000000000000000".ToUtf8Bytes());
            }
            md5.DoFinal(ha2, 0);

            // The actual Digest Response
            md5.BlockUpdate(ha1.ToHexString().ToUtf8Bytes());
            md5.BlockUpdate(Colon);
            md5.BlockUpdate(result.Nonce.ToUtf8Bytes());
            md5.BlockUpdate(Colon);
            md5.BlockUpdate(result.NonceCountString().ToUtf8Bytes());
            md5.BlockUpdate(Colon);
            md5.BlockUpdate(result.CNonce.ToUtf8Bytes());
            md5.BlockUpdate(Colon);
            md5.BlockUpdate(GetQOPString(result.QOP).ToUtf8Bytes());
            md5.BlockUpdate(Colon);
            md5.BlockUpdate(ha2.ToHexString().ToUtf8Bytes());
            md5.DoFinal(digestResponse);
            return(digestResponse);
        }
Esempio n. 2
0
        private static void SetDelegates(DigestResponse parameters)
        {
            if (parameters.algorithm == "MD5-sess")
            {
                HA1Method = HA1_MD5SESS_Method;
            }
            else
            {
                HA1Method = HA1_MD5_Method;
            }

            if (parameters.qop == "auth-int")
            {
                HA2Method = HA2_AuthInt_Method;
            }
            else
            {
                HA2Method = HA2_Auth_Method;
            }

            if (parameters.qop == null)
            {
                ResponseMethod = Response_UnspecifiedQoP_Method;
            }
            else
            {
                ResponseMethod = Response_SpecifiedQoP_Method;
            }
        }
Esempio n. 3
0
        public void CanWriteEscapedSecuence()
        {
            DigestResponse resp = new DigestResponse();

            resp.Username = "******"er";

            string expected = "username=\"us\\\"er\",nc=00000000,maxbuf=0";

            Assert.AreEqual(expected, resp.ToString());
        }
Esempio n. 4
0
        private static async Task <HttpResponseMessage> SendWithAuthAsync(HttpRequestMessage request, Uri authUri, ICredentials credentials, bool preAuthenticate, bool isProxyAuth, bool doRequestAuth, HttpConnectionPool pool, CancellationToken cancellationToken)
        {
            if (preAuthenticate)
            {
                NetworkCredential credential = credentials.GetCredential(authUri, BasicScheme);
                if (credential != null)
                {
                    SetBasicAuthToken(request, credential, isProxyAuth);
                }
            }

            HttpResponseMessage response = await InnerSendAsync(request, isProxyAuth, doRequestAuth, pool, cancellationToken).ConfigureAwait(false);

            if (TryGetAuthenticationChallenge(response, isProxyAuth, authUri, credentials, out AuthenticationChallenge challenge))
            {
                if (challenge.AuthenticationType == AuthenticationType.Digest)
                {
                    var digestResponse = new DigestResponse(challenge.ChallengeData);
                    if (await TrySetDigestAuthToken(request, challenge.Credential, digestResponse, isProxyAuth).ConfigureAwait(false))
                    {
                        response.Dispose();
                        response = await InnerSendAsync(request, isProxyAuth, doRequestAuth, pool, cancellationToken).ConfigureAwait(false);

                        // Retry in case of nonce timeout in server.
                        if (TryGetRepeatedChallenge(response, challenge.SchemeName, isProxyAuth, out string challengeData))
                        {
                            digestResponse = new DigestResponse(challengeData);
                            if (IsServerNonceStale(digestResponse) &&
                                await TrySetDigestAuthToken(request, challenge.Credential, digestResponse, isProxyAuth).ConfigureAwait(false))
                            {
                                response.Dispose();
                                response = await InnerSendAsync(request, isProxyAuth, doRequestAuth, pool, cancellationToken).ConfigureAwait(false);
                            }
                        }
                    }
                }
                else if (challenge.AuthenticationType == AuthenticationType.Basic)
                {
                    if (!preAuthenticate)
                    {
                        SetBasicAuthToken(request, challenge.Credential, isProxyAuth);

                        response.Dispose();
                        response = await InnerSendAsync(request, isProxyAuth, doRequestAuth, pool, cancellationToken).ConfigureAwait(false);
                    }
                }
            }

            return(response);
        }
Esempio n. 5
0
        private byte[] CreateDigestResponse(ChallengeInfo challenge)
        {
            if ((challenge.QOP & QualityOfProtection.AuthenticationOnly) == 0)
            {
                throw new SaslException("Currently, DigestMD5Client only supports \"auth\" QOP value.");
            }

            // cipher is only used in "auth-conf", which we don't support yet.

            if (!challenge.Algorithm.EqualsOrdinalCI("md5-sess"))
            {
                throw new SaslException($"Invalid DIGEST-MD5 Algorithm: '{challenge.Algorithm}' - must be 'md5-sess'");
            }

            if (!challenge.Realms.Contains(_realm))
            {
                // Do we care? The server would reject us anyway if the Realm is invalid
            }

            var result = new DigestResponse
            {
                Username   = _username,
                Realm      = _realm,
                QOP        = QualityOfProtection.AuthenticationOnly,
                Charset    = "utf-8",
                NonceCount = 1,
                MaxBuf     = 65536,
                Nonce      = challenge.Nonce,
                DigestUri  = "ldap/" + _host
            };

            var cnonce = new byte[32];

            _rng.GetBytes(cnonce);
            result.CNonce = Utilclass.Base64.Encode(cnonce);

            var ha1 = DigestCalcHa1(result);

            result.Response = DigestCalcResponse(result, ha1);
            var resultStr = result.ToString();

            return(resultStr.ToUtf8Bytes());
        }
Esempio n. 6
0
        public void CanWriteResponse()
        {
            DigestResponse resp = new DigestResponse();

            resp.Username   = "******";
            resp.Realm      = "nowhere.com";
            resp.Nonce      = "OA9BSXrbuRhWay";
            resp.Cnonce     = "OA9BSuZWMSpW8m";
            resp.NonceCount = 16;
            resp.DigestUri  = "acap/elwood.innosoft.com";
            resp.Response   = "6084c6db3fede7352c551284490fd0fc";
            resp.Qop        = "auth";
            resp.MaxBuffer  = 65536;
            resp.Cipher     = "3des";
            resp.Authzid    = "user2";
            resp.AuthParam  = "ap";
            resp.Charset    = "utf-8";

            string expected = "username=\"user\",realm=\"nowhere.com\",nonce=\"OA9BSXrbuRhWay\",cnonce=\"OA9BSuZWMSpW8m\",nc=00000010,qop=auth,digest-uri=\"acap/elwood.innosoft.com\",response=\"6084c6db3fede7352c551284490fd0fc\",maxbuf=65536,charset=utf-8,cipher=3des,authzid=\"user2\",auth-param=\"ap\"";

            Assert.AreEqual(expected, resp.ToString());
        }
Esempio n. 7
0
        static void Main(string[] args)
        {
            //Simple test with wikipedia page : https://en.wikipedia.org/wiki/Digest_access_authentication

            DigestResponse resp = new DigestResponse
            {
                username  = "******",
                realm     = "*****@*****.**",
                nonce     = "dcd98b7102dd2f0e8b11d0f600bfb0c093",
                uri       = "/dir/index.html",
                qop       = "auth",
                nc        = "00000001",
                cnonce    = "0a4f113b",
                opaque    = "5ccc069c403ebaf9f0171e9517f40e41",
                algorithm = null,
                method    = "GET"
            };

            Console.WriteLine(DigestTool.GetExpectedResponse(resp, "Circle Of Life"));

            Console.ReadKey();
        }
Esempio n. 8
0
        private byte[] DigestCalcHa1(DigestResponse result)
        {
            var md5 = new MD5Digest();

            byte[] hash = new byte[md5.GetDigestSize()];
            byte[] ha1  = new byte[md5.GetDigestSize()];

            md5.BlockUpdate(result.Username.ToUtf8Bytes());
            md5.BlockUpdate(Colon);
            md5.BlockUpdate(result.Realm.ToUtf8Bytes());
            md5.BlockUpdate(Colon);
            md5.BlockUpdate(_password);
            md5.DoFinal(hash);

            md5.BlockUpdate(hash);
            md5.BlockUpdate(Colon);
            md5.BlockUpdate(result.Nonce.ToUtf8Bytes());
            md5.BlockUpdate(Colon);
            md5.BlockUpdate(result.CNonce.ToUtf8Bytes());
            md5.DoFinal(ha1);
            return(ha1);
        }
Esempio n. 9
0
        public static string GetDigestTokenForCredential(NetworkCredential credential, string httpMethod, string pathAndQuery, string content, 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;

            if (digestResponse.Parameters.TryGetValue(Algorithm, out algorithm))
            {
                if (algorithm != Sha256 && algorithm != Md5 && algorithm != Sha256Sess && algorithm != MD5Sess)
                {
                    return(null);
                }
            }
            else
            {
                algorithm = Md5;
            }

            // Check if nonce is there in challenge
            string nonce;

            if (!digestResponse.Parameters.TryGetValue(Nonce, out nonce))
            {
                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))
            {
                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
            {
                string usernameStar;
                if (IsInputEncoded5987(credential.UserName, out usernameStar))
                {
                    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, pathAndQuery);

            // Set qop, default is auth
            string qop = Auth;

            if (digestResponse.Parameters.ContainsKey(Qop))
            {
                // Check if auth-int present in qop string
                int index1 = digestResponse.Parameters[Qop].IndexOf(AuthInt);
                if (index1 != -1)
                {
                    // Get index of auth if present in qop string
                    int index2 = digestResponse.Parameters[Qop].IndexOf(Auth);

                    // 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);
                        if (index2 == -1)
                        {
                            qop = AuthInt;
                        }
                    }
                }
            }

            // Set cnonce
            string cnonce = GetRandomAlphaNumericString();

            // Calculate response
            string a1 = credential.UserName + ":" + realm + ":" + credential.Password;

            if (algorithm.IndexOf("sess") != -1)
            {
                a1 = ComputeHash(a1, algorithm) + ":" + nonce + ":" + cnonce;
            }

            string a2 = httpMethod + ":" + pathAndQuery;

            if (qop == AuthInt)
            {
                a2 = a2 + ":" + ComputeHash(content ?? string.Empty, algorithm);
            }

            string response = ComputeHash(ComputeHash(a1, algorithm) + ":" +
                                          nonce + ":" +
                                          DigestResponse.NonceCount + ":" +
                                          cnonce + ":" +
                                          qop + ":" +
                                          ComputeHash(a2, algorithm), algorithm);

            // Add response
            sb.AppendKeyValue(Response, response);

            // Add algorithm
            sb.AppendKeyValue(Algorithm, algorithm, includeQuotes: false);

            // Add opaque
            if (opaque != null)
            {
                sb.AppendKeyValue(Opaque, opaque);
            }

            // 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));
        }
Esempio n. 10
0
        public static bool IsServerNonceStale(DigestResponse digestResponse)
        {
            string stale = null;

            return(digestResponse.Parameters.TryGetValue(Stale, out stale) && stale == "true");
        }
        public async static Task <bool> TrySetDigestAuthToken(HttpRequestMessage request, ICredentials credentials, DigestResponse digestResponse, string authHeader)
        {
            NetworkCredential credential = credentials.GetCredential(request.RequestUri, Digest);

            if (credential == null)
            {
                return(false);
            }

            string parameter = await GetDigestTokenForCredential(credential, request, digestResponse).ConfigureAwait(false);

            // Any errors in obtaining parameter return false
            if (string.IsNullOrEmpty(parameter))
            {
                return(false);
            }

            if (authHeader == HttpKnownHeaderNames.Authorization)
            {
                request.Headers.Authorization = new AuthenticationHeaderValue(Digest, parameter);
            }
            else if (authHeader == HttpKnownHeaderNames.ProxyAuthorization)
            {
                request.Headers.ProxyAuthorization = new AuthenticationHeaderValue(Digest, parameter);
            }

            return(true);
        }
Esempio n. 12
0
        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));
        }
Esempio n. 13
0
 private static string Response_SpecifiedQoP_Method(DigestResponse p, string HA1, string HA2) => $"{HA1}:{p.nonce}:{p.nc}:{p.cnonce}:{p.qop}:{HA2}".ToMD5Hash();
Esempio n. 14
0
 private static string HA2_AuthInt_Method(DigestResponse p) => $"{p.method}:{p.uri}:{p.entityBody.ToMD5Hash()}".ToMD5Hash();
Esempio n. 15
0
        private static async Task <bool> TrySetDigestAuthToken(HttpRequestMessage request, NetworkCredential credential, DigestResponse digestResponse, bool isProxyAuth)
        {
            string parameter = await GetDigestTokenForCredential(credential, request, digestResponse).ConfigureAwait(false);

            // Any errors in obtaining parameter return false and we don't proceed with auth
            if (string.IsNullOrEmpty(parameter))
            {
                return(false);
            }

            var headerValue = new AuthenticationHeaderValue(DigestScheme, parameter);

            SetRequestAuthenticationHeaderValue(request, headerValue, isProxyAuth);
            return(true);
        }
        public static string GetDigestAuthChallengeResponse(NetworkCredential credential, string httpMethod, string pathAndQuery, string content, string challengeData)
        {
            var digestResponse = new DigestResponse(challengeData);

            return(GetDigestTokenForCredential(credential, httpMethod, pathAndQuery, content, digestResponse));
        }
Esempio n. 17
0
        public static string GetExpectedResponse(DigestResponse parameters, string password)
        {
            SetDelegates(parameters);

            return(ResponseMethod(parameters, HA1Method(parameters, password), HA2Method(parameters)));
        }
Esempio n. 18
0
 public static bool ResponseIsCorrect(DigestResponse parameters, string password)
 {
     return(string.Equals(GetExpectedResponse(parameters, password), parameters.response));
 }
Esempio n. 19
0
        private static async ValueTask <HttpResponseMessage> SendWithAuthAsync(HttpRequestMessage request, Uri authUri, bool async, ICredentials credentials, bool preAuthenticate, bool isProxyAuth, bool doRequestAuth, HttpConnectionPool pool, CancellationToken cancellationToken)
        {
            // If preauth is enabled and this isn't proxy auth, try to get a basic credential from the
            // preauth credentials cache, and if successful, set an auth header for it onto the request.
            // Currently we only support preauth for Basic.
            bool performedBasicPreauth = false;

            if (preAuthenticate)
            {
                Debug.Assert(pool.PreAuthCredentials != null);
                NetworkCredential?credential;
                lock (pool.PreAuthCredentials)
                {
                    // Just look for basic credentials.  If in the future we support preauth
                    // for other schemes, this will need to search in order of precedence.
                    Debug.Assert(pool.PreAuthCredentials.GetCredential(authUri, NegotiateScheme) == null);
                    Debug.Assert(pool.PreAuthCredentials.GetCredential(authUri, NtlmScheme) == null);
                    Debug.Assert(pool.PreAuthCredentials.GetCredential(authUri, DigestScheme) == null);
                    credential = pool.PreAuthCredentials.GetCredential(authUri, BasicScheme);
                }

                if (credential != null)
                {
                    SetBasicAuthToken(request, credential, isProxyAuth);
                    performedBasicPreauth = true;
                }
            }

            HttpResponseMessage response = await InnerSendAsync(request, async, isProxyAuth, doRequestAuth, pool, cancellationToken).ConfigureAwait(false);

            if (TryGetAuthenticationChallenge(response, isProxyAuth, authUri, credentials, out AuthenticationChallenge challenge))
            {
                switch (challenge.AuthenticationType)
                {
                case AuthenticationType.Digest:
                    var digestResponse = new DigestResponse(challenge.ChallengeData);
                    if (await TrySetDigestAuthToken(request, challenge.Credential, digestResponse, isProxyAuth).ConfigureAwait(false))
                    {
                        response.Dispose();
                        response = await InnerSendAsync(request, async, isProxyAuth, doRequestAuth, pool, cancellationToken).ConfigureAwait(false);

                        // Retry in case of nonce timeout in server.
                        if (TryGetRepeatedChallenge(response, challenge.SchemeName, isProxyAuth, out string?challengeData))
                        {
                            digestResponse = new DigestResponse(challengeData);
                            if (IsServerNonceStale(digestResponse) &&
                                await TrySetDigestAuthToken(request, challenge.Credential, digestResponse, isProxyAuth).ConfigureAwait(false))
                            {
                                response.Dispose();
                                response = await InnerSendAsync(request, async, isProxyAuth, doRequestAuth, pool, cancellationToken).ConfigureAwait(false);
                            }
                        }
                    }
                    break;

                case AuthenticationType.Basic:
                    if (performedBasicPreauth)
                    {
                        if (NetEventSource.IsEnabled)
                        {
                            NetEventSource.AuthenticationError(authUri, $"Pre-authentication with {(isProxyAuth ? "proxy" : "server")} failed.");
                        }
                        break;
                    }

                    response.Dispose();
                    SetBasicAuthToken(request, challenge.Credential, isProxyAuth);
                    response = await InnerSendAsync(request, async, isProxyAuth, doRequestAuth, pool, cancellationToken).ConfigureAwait(false);

                    if (preAuthenticate)
                    {
                        switch (response.StatusCode)
                        {
                        case HttpStatusCode.ProxyAuthenticationRequired:
                        case HttpStatusCode.Unauthorized:
                            if (NetEventSource.IsEnabled)
                            {
                                NetEventSource.AuthenticationError(authUri, $"Pre-authentication with {(isProxyAuth ? "proxy" : "server")} failed.");
                            }
                            break;

                        default:
                            lock (pool.PreAuthCredentials !)
                            {
                                try
                                {
                                    if (NetEventSource.IsEnabled)
                                    {
                                        NetEventSource.Info(pool.PreAuthCredentials, $"Adding Basic credential to cache, uri={authUri}, username={challenge.Credential.UserName}");
                                    }
                                    pool.PreAuthCredentials.Add(authUri, BasicScheme, challenge.Credential);
                                }
                                catch (ArgumentException)
                                {
                                    // The credential already existed.
                                    if (NetEventSource.IsEnabled)
                                    {
                                        NetEventSource.Info(pool.PreAuthCredentials, $"Basic credential present in cache, uri={authUri}, username={challenge.Credential.UserName}");
                                    }
                                }
                            }
                            break;
                        }
                    }
                    break;
                }
            }

            if (NetEventSource.IsEnabled && response.StatusCode == HttpStatusCode.Unauthorized)
            {
                NetEventSource.AuthenticationError(authUri, $"{(isProxyAuth ? "Proxy" : "Server")} authentication failed.");
            }

            return(response);
        }
Esempio n. 20
0
        private static async ValueTask <bool> TrySetDigestAuthToken(HttpRequestMessage request, NetworkCredential credential, DigestResponse digestResponse, bool isProxyAuth)
        {
            string?parameter = await GetDigestTokenForCredential(credential, request, digestResponse).ConfigureAwait(false);

            // Any errors in obtaining parameter return false and we don't proceed with auth
            if (string.IsNullOrEmpty(parameter))
            {
                if (NetEventSource.IsEnabled)
                {
                    NetEventSource.AuthenticationError(request.RequestUri, $"Unable to find 'Digest' authentication token when authenticating with {(isProxyAuth ? "proxy" : "server")}");
                }
                return(false);
            }

            var headerValue = new AuthenticationHeaderValue(DigestScheme, parameter);

            SetRequestAuthenticationHeaderValue(request, headerValue, isProxyAuth);
            return(true);
        }
Esempio n. 21
0
 private static string HA1_MD5_Method(DigestResponse p, string password) => $"{p.username}:{p.realm}:{password}".ToMD5Hash();
Esempio n. 22
0
 private static string HA2_Auth_Method(DigestResponse p) => $"{p.method}:{p.uri}".ToMD5Hash();
Esempio n. 23
0
 private static string HA1_MD5SESS_Method(DigestResponse p, string password) => $"{HA1_MD5_Method(p, password)}:{p.nonce}:{p.cnonce}".ToMD5Hash();
Esempio n. 24
0
        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;

            if (digestResponse.Parameters.TryGetValue(Algorithm, out algorithm))
            {
                if (algorithm != Sha256 && algorithm != Md5 && algorithm != Sha256Sess && algorithm != MD5Sess)
                {
                    return(null);
                }
            }
            else
            {
                algorithm = Md5;
            }

            // Check if nonce is there in challenge
            string nonce;

            if (!digestResponse.Parameters.TryGetValue(Nonce, out nonce))
            {
                return(null);
            }

            string opaque;

            if (!digestResponse.Parameters.TryGetValue(Opaque, out opaque))
            {
                return(null);
            }

            string realm = digestResponse.Parameters.ContainsKey(Realm) ? digestResponse.Parameters[Realm] : string.Empty;

            // 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
            {
                string usernameStar;
                if (HeaderUtilities.IsInputEncoded5987(credential.UserName, out usernameStar))
                {
                    sb.AppendKeyValue(UsernameStar, usernameStar, includeQuotes: false);
                }
                else
                {
                    sb.AppendKeyValue(Username, credential.UserName);
                }
            }

            // Add realm
            if (realm != string.Empty)
            {
                sb.AppendKeyValue(Realm, realm);
            }

            // If nonce is same as previous request, update nonce count.
            if (nonce == digestResponse.Nonce)
            {
                digestResponse.NonceCount++;
            }

            // Add nonce
            sb.AppendKeyValue(Nonce, nonce);
            digestResponse.Nonce = nonce;

            // Add uri
            sb.AppendKeyValue(Uri, request.RequestUri.PathAndQuery);

            // Set qop, default is auth
            string qop = Auth;

            if (digestResponse.Parameters.ContainsKey(Qop))
            {
                // Check if auth-int present in qop string
                int index1 = digestResponse.Parameters[Qop].IndexOf(AuthInt);
                if (index1 != -1)
                {
                    // Get index of auth if present in qop string
                    int index2 = digestResponse.Parameters[Qop].IndexOf(Auth);

                    // 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);
                        if (index2 == -1)
                        {
                            qop = AuthInt;
                        }
                    }
                }
            }

            // Set cnonce
            string cnonce = GetRandomAlphaNumericString();

            // Calculate response
            string a1 = credential.UserName + ":" + realm + ":" + credential.Password;

            if (algorithm == Sha256Sess || algorithm == MD5Sess)
            {
                algorithm = algorithm == Sha256Sess ? Sha256 : Md5;
                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();

                a2 = a2 + ":" + ComputeHash(content, algorithm);
            }

            string response = ComputeHash(ComputeHash(a1, algorithm) + ":" +
                                          nonce + ":" +
                                          digestResponse.NonceCount.ToString("x8") + ":" +
                                          cnonce + ":" +
                                          qop + ":" +
                                          ComputeHash(a2, algorithm), algorithm);

            // Add response
            sb.AppendKeyValue(Response, response);

            // Add algorithm
            sb.AppendKeyValue(Algorithm, algorithm, includeQuotes: false);

            // Add opaque
            sb.AppendKeyValue(Opaque, opaque);

            // Add qop
            sb.AppendKeyValue(Qop, qop, includeQuotes: false);

            // Add nc
            sb.AppendKeyValue(NC, digestResponse.NonceCount.ToString("x8"), includeQuotes: false);

            // Add cnonce
            sb.AppendKeyValue(CNonce, cnonce, includeComma: false);

            return(StringBuilderCache.GetStringAndRelease(sb));
        }