public override string ToString() { StringBuilder buffer = new StringBuilder(); Pair(buffer, "username", Username, true); Pair(buffer, "realm", Realm, true); Pair(buffer, "nonce", Nonce, true); Pair(buffer, "cnonce", Cnonce, true); string nc = NonceCount.ToString("x8", CultureInfo.InvariantCulture); Pair(buffer, "nc", nc, false); Pair(buffer, "qop", Qop, false); Pair(buffer, "digest-uri", DigestUri, true); Pair(buffer, "response", Response, true); string maxBuffer = MaxBuffer.ToString(CultureInfo.InvariantCulture); Pair(buffer, "maxbuf", maxBuffer, false); Pair(buffer, "charset", Charset, false); Pair(buffer, "cipher", Cipher, false); Pair(buffer, "authzid", Authzid, true); Pair(buffer, "auth-param", AuthParam, true); return(buffer.ToString().TrimEnd(',')); }
/// <summary> /// Generates a string that can be set to an Authorization header. /// </summary> public string GenerateResponseHeader(HTTPRequest request, Credentials credentials) { try { switch (Type) { case AuthenticationTypes.Basic: return(string.Concat("Basic ", Convert.ToBase64String(Encoding.UTF8.GetBytes(string.Format("{0}:{1}", credentials.UserName, credentials.Password))))); case AuthenticationTypes.Digest: { NonceCount++; string HA1 = string.Empty; // The cnonce-value is an opaque quoted string value provided by the client and used by both client and server to avoid chosen plaintext attacks, to provide mutual // authentication, and to provide some message integrity protection. string cnonce = new System.Random(request.GetHashCode()).Next(int.MinValue, int.MaxValue).ToString("X8"); string ncvalue = NonceCount.ToString("X8"); switch (Algorithm.TrimAndLower()) { case "md5": HA1 = string.Format("{0}:{1}:{2}", credentials.UserName, Realm, credentials.Password).CalculateMD5Hash(); break; case "md5-sess": if (string.IsNullOrEmpty(this.HA1Sess)) { this.HA1Sess = string.Format("{0}:{1}:{2}:{3}:{4}", credentials.UserName, Realm, credentials.Password, Nonce, ncvalue).CalculateMD5Hash(); } HA1 = this.HA1Sess; break; default: //throw new NotSupportedException("Not supported hash algorithm found in Web Authentication: " + Algorithm); return(string.Empty); } // A string of 32 hex digits, which proves that the user knows a password. Set according to the qop value. string response = string.Empty; // The server sent QoP-value can be a list of supported methodes(if sent at all - in this case it's null). // The rfc is not specify that this is a space or comma separeted list. So it can be "auth, auth-int" or "auth auth-int". // We will first check the longer value("auth-int") then the short one ("auth"). If one matches we will reset the qop to the exact value. string qop = this.QualityOfProtections != null?this.QualityOfProtections.TrimAndLower() : null; if (qop == null) { string HA2 = string.Concat(request.MethodType.ToString().ToUpper(), ":", request.CurrentUri.GetRequestPathAndQueryURL()).CalculateMD5Hash(); response = string.Format("{0}:{1}:{2}", HA1, Nonce, HA2).CalculateMD5Hash(); } else if (qop.Contains("auth-int")) { qop = "auth-int"; byte[] entityBody = request.GetEntityBody(); if (entityBody == null) { entityBody = string.Empty.GetASCIIBytes(); } string HA2 = string.Format("{0}:{1}:{2}", request.MethodType.ToString().ToUpper(), request.CurrentUri.GetRequestPathAndQueryURL(), entityBody.CalculateMD5Hash()).CalculateMD5Hash(); response = string.Format("{0}:{1}:{2}:{3}:{4}:{5}", HA1, Nonce, ncvalue, cnonce, qop, HA2).CalculateMD5Hash(); } else if (qop.Contains("auth")) { qop = "auth"; string HA2 = string.Concat(request.MethodType.ToString().ToUpper(), ":", request.CurrentUri.GetRequestPathAndQueryURL()).CalculateMD5Hash(); response = string.Format("{0}:{1}:{2}:{3}:{4}:{5}", HA1, Nonce, ncvalue, cnonce, qop, HA2).CalculateMD5Hash(); } else //throw new NotSupportedException("Unrecognized Quality of Protection value found: " + this.QualityOfProtections); { return(string.Empty); } string result = string.Format("Digest username=\"{0}\", realm=\"{1}\", nonce=\"{2}\", uri=\"{3}\", cnonce=\"{4}\", response=\"{5}\"", credentials.UserName, Realm, Nonce, request.Uri.GetRequestPathAndQueryURL(), cnonce, response); if (qop != null) { result += String.Concat(", qop=\"", qop, "\", nc=", ncvalue); } if (!string.IsNullOrEmpty(Opaque)) { result = String.Concat(result, ", opaque=\"", Opaque, "\""); } return(result); } // end of case "digest": default: break; } } catch { } return(string.Empty); }
/// <summary> /// Calculates response value. /// </summary> /// <param name="userName">User name.</param> /// <param name="password">User password.</param> /// <returns>Returns calculated rsponse value.</returns> public string CalculateResponse(string userName, string password) { /* * MD5 * A1 = username-value ":" realm-value ":" passwd * * MD5-sess * A1 = md5(username-value ":" realm-value ":" passwd) ":" nonce-value ":" cnonce-value * * qop not peresent or auth * A2 = Method ":" digest-uri-value * * qop auth-int * A2 = Method ":" digest-uri-value ":" md5h(entity-body) * * qop present * response = md5h(md5h(A1) ":" nonce-value ":" nc-value ":" cnonce-value ":" qop-value ":" md5h(A2)) * * qop not present * response = md5h(md5h(A1) ":" nonce-value ":" md5h(A2)) * */ string a1 = ""; string a2 = ""; // Create A1 if (Algorithm == "" || Algorithm.ToLower() == "md5") { a1 = userName + ":" + Realm + ":" + password; } else if (Algorithm.ToLower() == "md5-sess") { a1 = Core.ComputeMd5(userName + ":" + Realm + ":" + password, false) + ":" + Nonce + ":" + CNonce; } else { throw new ArgumentException("Invalid Algorithm value '" + Algorithm + "' !"); } // Create A2 if (Qop == "" || Qop.ToLower() == "auth") { a2 = m_Method.ToUpper() + ":" + Uri; } else { throw new ArgumentException("Invalid qop value '" + Qop + "' !"); } // Calculate response value. // qop present if (!string.IsNullOrEmpty(Qop)) { return (Core.ComputeMd5( Core.ComputeMd5(a1, true) + ":" + Nonce + ":" + NonceCount.ToString("x8") + ":" + CNonce + ":" + Qop + ":" + Core.ComputeMd5(a2, true), true)); } // qop not present else { return (Core.ComputeMd5( Core.ComputeMd5(a1, true) + ":" + Nonce + ":" + Core.ComputeMd5(a2, true), true)); } }
/// <summary> /// Calculates 'rspauth' value. /// </summary> /// <param name="userName">User name.</param> /// <param name="password">Password.</param> /// <returns>Returns 'rspauth' value.</returns> public string CalculateRspAuth(string userName, string password) { /* RFC 2617 3.2.3. * The optional response digest in the "response-auth" directive * supports mutual authentication -- the server proves that it knows the * user's secret, and with qop=auth-int also provides limited integrity * protection of the response. The "response-digest" value is calculated * as for the "request-digest" in the Authorization header, except that * if "qop=auth" or is not specified in the Authorization header for the * request, A2 is * * A2 = ":" digest-uri-value * * and if "qop=auth-int", then A2 is * * A2 = ":" digest-uri-value ":" H(entity-body) * * where "digest-uri-value" is the value of the "uri" directive on the * Authorization header in the request. The "cnonce-value" and "nc- * value" MUST be the ones for the client request to which this message * is the response. The "response-auth", "cnonce", and "nonce-count" * directives MUST BE present if "qop=auth" or "qop=auth-int" is * specified. */ string a1 = ""; string a2 = ""; // Create A1 if (Algorithm == "" || Algorithm.ToLower() == "md5") { a1 = userName + ":" + Realm + ":" + password; } else if (Algorithm.ToLower() == "md5-sess") { a1 = Core.ComputeMd5(userName + ":" + Realm + ":" + password, false) + ":" + Nonce + ":" + CNonce; } else { throw new ArgumentException("Invalid Algorithm value '" + Algorithm + "' !"); } // Create A2 if (Qop == "" || Qop.ToLower() == "auth") { a2 = ":" + Uri; } else { throw new ArgumentException("Invalid qop value '" + Qop + "' !"); } // Calculate response value. // qop present if (!string.IsNullOrEmpty(Qop)) { return (Core.ComputeMd5( Core.ComputeMd5(a1, true) + ":" + Nonce + ":" + NonceCount.ToString("x8") + ":" + CNonce + ":" + Qop + ":" + Core.ComputeMd5(a2, true), true)); } // qop not present else { return (Core.ComputeMd5( Core.ComputeMd5(a1, true) + ":" + Nonce + ":" + Core.ComputeMd5(a2, true), true)); } }
public string NonceCountString() => NonceCount.ToString("X").PadLeft(8, '0');
public string GenerateResponseHeader(HTTPRequest request, Credentials credentials) { try { switch (Type) { case AuthenticationTypes.Basic: return("Basic " + Convert.ToBase64String(Encoding.UTF8.GetBytes($"{credentials.UserName}:{credentials.Password}"))); case AuthenticationTypes.Digest: { NonceCount++; string empty = string.Empty; string text = new Random(request.GetHashCode()).Next(-2147483648, 2147483647).ToString("X8"); string text2 = NonceCount.ToString("X8"); switch (Algorithm.TrimAndLower()) { case "md5": empty = $"{credentials.UserName}:{Realm}:{credentials.Password}".CalculateMD5Hash(); break; case "md5-sess": if (string.IsNullOrEmpty(HA1Sess)) { HA1Sess = $"{credentials.UserName}:{Realm}:{credentials.Password}:{Nonce}:{text2}".CalculateMD5Hash(); } empty = HA1Sess; break; default: return(string.Empty); } string empty2 = string.Empty; string text3 = (QualityOfProtections == null) ? null : QualityOfProtections.TrimAndLower(); if (text3 == null) { string arg = (request.MethodType.ToString().ToUpper() + ":" + request.CurrentUri.PathAndQuery).CalculateMD5Hash(); empty2 = $"{empty}:{Nonce}:{arg}".CalculateMD5Hash(); } else if (text3.Contains("auth-int")) { text3 = "auth-int"; byte[] array = request.GetEntityBody(); if (array == null) { array = string.Empty.GetASCIIBytes(); } string text4 = $"{request.MethodType.ToString().ToUpper()}:{request.CurrentUri.PathAndQuery}:{array.CalculateMD5Hash()}".CalculateMD5Hash(); empty2 = $"{empty}:{Nonce}:{text2}:{text}:{text3}:{text4}".CalculateMD5Hash(); } else { if (!text3.Contains("auth")) { return(string.Empty); } text3 = "auth"; string text5 = (request.MethodType.ToString().ToUpper() + ":" + request.CurrentUri.PathAndQuery).CalculateMD5Hash(); empty2 = $"{empty}:{Nonce}:{text2}:{text}:{text3}:{text5}".CalculateMD5Hash(); } string text6 = $"Digest username=\"{credentials.UserName}\", realm=\"{Realm}\", nonce=\"{Nonce}\", uri=\"{request.Uri.PathAndQuery}\", cnonce=\"{text}\", response=\"{empty2}\""; if (text3 != null) { text6 += ", qop=\"" + text3 + "\", nc=" + text2; } if (!string.IsNullOrEmpty(Opaque)) { text6 = text6 + ", opaque=\"" + Opaque + "\""; } return(text6); } } } catch { } return(string.Empty); }