diff --git a/src/RestSharp/Authenticators/DigestAuthenticator.cs b/src/RestSharp/Authenticators/DigestAuthenticator.cs
new file mode 100644
index 000000000..aa42af8d1
--- /dev/null
+++ b/src/RestSharp/Authenticators/DigestAuthenticator.cs
@@ -0,0 +1,191 @@
+// Copyright (c) .NET Foundation and Contributors
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+using RestSharp.Authenticators;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Net;
+using System.Security.Cryptography;
+using System.Text;
+using System.Text.RegularExpressions;
+using System.Threading.Tasks;
+
+namespace RestSharp.Authenticators;
+
+///
+/// Allows "digest access authentication" for HTTP requests.
+///
+///
+/// Encoding can be specified depending on what your server expect (see https://stackoverflow.com/a/7243567).
+/// UTF-8 is used by default but some servers might expect ISO-8859-1 encoding.
+///
+[PublicAPI]
+public class DigestAuthenticator : IAuthenticator
+{
+ private string _username;
+ private string _password;
+ private string _realm;
+ private string _nonce;
+ private string _qop;
+ private string _opaque;
+ public DigestAuthenticator(string username, string password)
+ {
+ _username = username;
+ _password = password;
+ }
+ public ValueTask Authenticate(IRestClient client, RestRequest request)
+ {
+ if (string.IsNullOrEmpty(_realm) || string.IsNullOrEmpty(_nonce) || string.IsNullOrEmpty(_qop))
+ {
+ try
+ {
+ FetchAuthenticationInfo(client, request);
+ }
+ catch (Exception ex)
+ {
+ Console.WriteLine("Error: {0}", ex.Message);
+ if (client.Options.ThrowOnAnyError)
+ throw ex;
+ return new ValueTask(Task.FromResult(new HeaderParameter(KnownHeaders.Authorization, "")));
+ }
+ }
+ string authorizationHeader = GenerateDigestAuthorization(request.Method.ToString(), client.Options.BaseUrl.ToString(), _username, _password, _realm, _nonce, _opaque);
+ _realm = "";
+ _nonce = "";
+ _qop = "";
+ return new ValueTask(Task.FromResult(request.AddOrUpdateParameter(new HeaderParameter(KnownHeaders.Authorization, authorizationHeader))));
+ }
+ private void FetchAuthenticationInfo(IRestClient client, RestRequest request)
+ {
+ RestClient headClient = new RestClient(new RestClientOptions(client.Options.BaseUrl.ToString())
+ {
+ MaxTimeout = Convert.ToInt32(TimeSpan.FromSeconds(10).TotalMilliseconds)
+ });
+ RestRequest headRequest = new RestRequest(client.BuildUri(request).PathAndQuery, request.Method);
+ headRequest.Timeout = Convert.ToInt32(TimeSpan.FromSeconds(10).TotalMilliseconds);
+ RestResponse headResponse = headClient.Execute(headRequest);
+ if (headResponse.StatusCode == HttpStatusCode.Unauthorized)
+ {
+ var wwwAuthenticateHeader = headResponse.Headers.FirstOrDefault(h => h.Name == "WWW-Authenticate")?.Value.ToString();
+ if (!string.IsNullOrEmpty(wwwAuthenticateHeader))
+ ParseAuthenticationInfo(wwwAuthenticateHeader);
+ }
+ else
+ {
+ if (headResponse.StatusCode == HttpStatusCode.RequestTimeout)
+ throw new Exception("The request timed out.");
+ else
+ throw new Exception("The server did not respond with a valid WWW-Authenticate in the header.");
+ }
+ }
+ private void ParseAuthenticationInfo(string wwwAuthenticateHeader)
+ {
+ // Parse the WWW-Authenticate header and extract the necessary information (realm, nonce, qop, etc.)
+ // You'll need to implement the parsing logic according to the structure of the header.
+ // This will depend on the server's implementation of Digest authentication.
+ // Example parsing code for demonstration purposes:
+ // The parsing logic here assumes a specific format for the header. You should adjust this to match your server's format.
+ var valores = ExtrairValoresPropriedades(wwwAuthenticateHeader);
+ valores.TryGetValue("realm", out _realm);
+ valores.TryGetValue("nonce", out _nonce);
+ valores.TryGetValue("qop", out _qop);
+ valores.TryGetValue("opaque", out _opaque);
+ }
+ public static Dictionary ExtrairValoresPropriedades(string texto)
+ {
+ // Define o padrão da regex para encontrar os valores entre as aspas
+ string padraoRegex = @"(\w+)=""([^""]+)""";
+ var regex = new Regex(padraoRegex);
+ // Cria um dicionário para armazenar os pares chave-valor
+ var valores = new Dictionary();
+ // Encontra todas as correspondências na string
+ var correspondencias = regex.Matches(texto);
+ // Itera sobre as correspondências e adiciona ao dicionário
+ foreach (Match correspondencia in correspondencias)
+ {
+ if (correspondencia.Success)
+ {
+ string chave = correspondencia.Groups[1].Value;
+ string valor = correspondencia.Groups[2].Value;
+ valores[chave] = valor;
+ }
+ }
+ return valores;
+ }
+ private string GetValueFromHeader(string header, string key)
+ {
+ string searchKey = key + "=";
+ int startIndex = header.IndexOf(searchKey, StringComparison.OrdinalIgnoreCase);
+ if (startIndex == -1)
+ return null;
+ startIndex += searchKey.Length;
+ int endIndex = header.IndexOf('"', startIndex);
+ if (endIndex == -1)
+ return null;
+ return header.Substring(startIndex, endIndex - startIndex);
+ }
+ private string GenerateDigestAuthorization(
+ string method,
+ string uri,
+ string username,
+ string password,
+ string realm,
+ string nonce,
+ string opaque,
+ string qop = "auth",
+ string algorithm = "MD5")
+ {
+ string cnonce = Guid.NewGuid().ToString("N").Substring(0, 8).ToLower();
+ string A1 = $"{username}:{realm}:{password}";
+ string H_A1 = CalculateMD5Hash(A1);
+ string A2 = $"{method.ToUpper()}:{uri}";
+ string H_A2 = CalculateMD5Hash(A2);
+ string request_digest = gerarRequestDigest(H_A1, nonce, "00000001", cnonce, qop, H_A2);
+ string authorizationHeader = $"Digest username=\"{username}\", " +
+ $"realm=\"{realm}\", " +
+ $"nonce=\"{nonce}\", " +
+ $"uri=\"{uri}\", " +
+ $"algorithm=\"{algorithm}\", " +
+ $"qop={qop}, " +
+ $"nc=00000001, " +
+ $"cnonce=\"{cnonce}\", " +
+ $"response=\"{request_digest}\", " +
+ $"opaque=\"{opaque}\"";
+ return authorizationHeader;
+ }
+ private string CalculateMD5Hash(string input)
+ {
+ using (MD5 md5 = MD5.Create())
+ {
+ byte[] inputBytes = Encoding.ASCII.GetBytes(input);
+ byte[] hashBytes = md5.ComputeHash(inputBytes);
+ StringBuilder sb = new StringBuilder();
+ for (int i = 0; i < hashBytes.Length; i++)
+ {
+ sb.Append(hashBytes[i].ToString("x2"));
+ }
+ return sb.ToString();
+ }
+ }
+ private string gerarRequestDigest(string H_A1, string nonce, string nc, string cnonce, string qop, string H_A2)
+ {
+ string combined = "";
+ if (qop == "auth")
+ combined = $"{H_A1}:{nonce}:{nc}:{cnonce}:{qop}:{H_A2}";
+ else
+ combined = $"{H_A1}:{nonce}:{H_A2}";
+ return CalculateMD5Hash(combined);
+ }
+}