AuthenticationChallenge.cs 6.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276
  1. #region License
  2. /*
  3. * AuthenticationChallenge.cs
  4. *
  5. * The MIT License
  6. *
  7. * Copyright (c) 2013-2023 sta.blockhead
  8. *
  9. * Permission is hereby granted, free of charge, to any person obtaining a copy
  10. * of this software and associated documentation files (the "Software"), to deal
  11. * in the Software without restriction, including without limitation the rights
  12. * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  13. * copies of the Software, and to permit persons to whom the Software is
  14. * furnished to do so, subject to the following conditions:
  15. *
  16. * The above copyright notice and this permission notice shall be included in
  17. * all copies or substantial portions of the Software.
  18. *
  19. * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  20. * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  21. * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  22. * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  23. * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  24. * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
  25. * THE SOFTWARE.
  26. */
  27. #endregion
  28. using System;
  29. using System.Collections.Specialized;
  30. using System.Text;
  31. namespace WebSocketSharp.Net
  32. {
  33. internal class AuthenticationChallenge
  34. {
  35. #region Private Fields
  36. private NameValueCollection _parameters;
  37. private AuthenticationSchemes _scheme;
  38. #endregion
  39. #region Private Constructors
  40. private AuthenticationChallenge (
  41. AuthenticationSchemes scheme, NameValueCollection parameters
  42. )
  43. {
  44. _scheme = scheme;
  45. _parameters = parameters;
  46. }
  47. #endregion
  48. #region Internal Constructors
  49. internal AuthenticationChallenge (
  50. AuthenticationSchemes scheme, string realm
  51. )
  52. : this (scheme, new NameValueCollection ())
  53. {
  54. _parameters["realm"] = realm;
  55. if (scheme == AuthenticationSchemes.Digest) {
  56. _parameters["nonce"] = CreateNonceValue ();
  57. _parameters["algorithm"] = "MD5";
  58. _parameters["qop"] = "auth";
  59. }
  60. }
  61. #endregion
  62. #region Internal Properties
  63. internal NameValueCollection Parameters {
  64. get {
  65. return _parameters;
  66. }
  67. }
  68. #endregion
  69. #region Public Properties
  70. public string Algorithm {
  71. get {
  72. return _parameters["algorithm"];
  73. }
  74. }
  75. public string Domain {
  76. get {
  77. return _parameters["domain"];
  78. }
  79. }
  80. public string Nonce {
  81. get {
  82. return _parameters["nonce"];
  83. }
  84. }
  85. public string Opaque {
  86. get {
  87. return _parameters["opaque"];
  88. }
  89. }
  90. public string Qop {
  91. get {
  92. return _parameters["qop"];
  93. }
  94. }
  95. public string Realm {
  96. get {
  97. return _parameters["realm"];
  98. }
  99. }
  100. public AuthenticationSchemes Scheme {
  101. get {
  102. return _scheme;
  103. }
  104. }
  105. public string Stale {
  106. get {
  107. return _parameters["stale"];
  108. }
  109. }
  110. #endregion
  111. #region Internal Methods
  112. internal static AuthenticationChallenge CreateBasicChallenge (string realm)
  113. {
  114. return new AuthenticationChallenge (AuthenticationSchemes.Basic, realm);
  115. }
  116. internal static AuthenticationChallenge CreateDigestChallenge (string realm)
  117. {
  118. return new AuthenticationChallenge (AuthenticationSchemes.Digest, realm);
  119. }
  120. internal static string CreateNonceValue ()
  121. {
  122. var rand = new Random ();
  123. var bytes = new byte[16];
  124. rand.NextBytes (bytes);
  125. var buff = new StringBuilder (32);
  126. foreach (var b in bytes)
  127. buff.Append (b.ToString ("x2"));
  128. return buff.ToString ();
  129. }
  130. internal static AuthenticationChallenge Parse (string value)
  131. {
  132. var chal = value.Split (new[] { ' ' }, 2);
  133. if (chal.Length != 2)
  134. return null;
  135. var schm = chal[0].ToLower ();
  136. if (schm == "basic") {
  137. var parameters = ParseParameters (chal[1]);
  138. return new AuthenticationChallenge (
  139. AuthenticationSchemes.Basic, parameters
  140. );
  141. }
  142. if (schm == "digest") {
  143. var parameters = ParseParameters (chal[1]);
  144. return new AuthenticationChallenge (
  145. AuthenticationSchemes.Digest, parameters
  146. );
  147. }
  148. return null;
  149. }
  150. internal static NameValueCollection ParseParameters (string value)
  151. {
  152. var ret = new NameValueCollection ();
  153. foreach (var param in value.SplitHeaderValue (',')) {
  154. var i = param.IndexOf ('=');
  155. var name = i > 0 ? param.Substring (0, i).Trim () : null;
  156. var val = i < 0
  157. ? param.Trim ().Trim ('"')
  158. : i < param.Length - 1
  159. ? param.Substring (i + 1).Trim ().Trim ('"')
  160. : String.Empty;
  161. ret.Add (name, val);
  162. }
  163. return ret;
  164. }
  165. internal string ToBasicString ()
  166. {
  167. return String.Format ("Basic realm=\"{0}\"", _parameters["realm"]);
  168. }
  169. internal string ToDigestString ()
  170. {
  171. var buff = new StringBuilder (128);
  172. var domain = _parameters["domain"];
  173. var realm = _parameters["realm"];
  174. var nonce = _parameters["nonce"];
  175. if (domain != null) {
  176. buff.AppendFormat (
  177. "Digest realm=\"{0}\", domain=\"{1}\", nonce=\"{2}\"",
  178. realm,
  179. domain,
  180. nonce
  181. );
  182. }
  183. else {
  184. buff.AppendFormat ("Digest realm=\"{0}\", nonce=\"{1}\"", realm, nonce);
  185. }
  186. var opaque = _parameters["opaque"];
  187. if (opaque != null)
  188. buff.AppendFormat (", opaque=\"{0}\"", opaque);
  189. var stale = _parameters["stale"];
  190. if (stale != null)
  191. buff.AppendFormat (", stale={0}", stale);
  192. var algo = _parameters["algorithm"];
  193. if (algo != null)
  194. buff.AppendFormat (", algorithm={0}", algo);
  195. var qop = _parameters["qop"];
  196. if (qop != null)
  197. buff.AppendFormat (", qop=\"{0}\"", qop);
  198. return buff.ToString ();
  199. }
  200. #endregion
  201. #region Public Methods
  202. public override string ToString ()
  203. {
  204. if (_scheme == AuthenticationSchemes.Basic)
  205. return ToBasicString ();
  206. if (_scheme == AuthenticationSchemes.Digest)
  207. return ToDigestString ();
  208. return String.Empty;
  209. }
  210. #endregion
  211. }
  212. }