123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454 |
- #region License
- /*
- * AuthenticationResponse.cs
- *
- * ParseBasicCredentials is derived from HttpListenerContext.cs (System.Net) of
- * Mono (http://www.mono-project.com).
- *
- * The MIT License
- *
- * Copyright (c) 2005 Novell, Inc. (http://www.novell.com)
- * Copyright (c) 2013-2023 sta.blockhead
- *
- * Permission is hereby granted, free of charge, to any person obtaining a copy
- * of this software and associated documentation files (the "Software"), to deal
- * in the Software without restriction, including without limitation the rights
- * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
- * copies of the Software, and to permit persons to whom the Software is
- * furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in
- * all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
- * THE SOFTWARE.
- */
- #endregion
- using System;
- using System.Collections.Specialized;
- using System.Security.Cryptography;
- using System.Security.Principal;
- using System.Text;
- namespace WebSocketSharp.Net
- {
- internal class AuthenticationResponse
- {
- #region Private Fields
- private uint _nonceCount;
- private NameValueCollection _parameters;
- private AuthenticationSchemes _scheme;
- #endregion
- #region Private Constructors
- private AuthenticationResponse (
- AuthenticationSchemes scheme, NameValueCollection parameters
- )
- {
- _scheme = scheme;
- _parameters = parameters;
- }
- #endregion
- #region Internal Constructors
- internal AuthenticationResponse (NetworkCredential credentials)
- : this (
- AuthenticationSchemes.Basic,
- new NameValueCollection (),
- credentials,
- 0
- )
- {
- }
- internal AuthenticationResponse (
- AuthenticationChallenge challenge,
- NetworkCredential credentials,
- uint nonceCount
- )
- : this (challenge.Scheme, challenge.Parameters, credentials, nonceCount)
- {
- }
- internal AuthenticationResponse (
- AuthenticationSchemes scheme,
- NameValueCollection parameters,
- NetworkCredential credentials,
- uint nonceCount
- )
- : this (scheme, parameters)
- {
- _parameters["username"] = credentials.Username;
- _parameters["password"] = credentials.Password;
- _parameters["uri"] = credentials.Domain;
- _nonceCount = nonceCount;
- if (scheme == AuthenticationSchemes.Digest)
- initAsDigest ();
- }
- #endregion
- #region Internal Properties
- internal uint NonceCount {
- get {
- return _nonceCount < UInt32.MaxValue
- ? _nonceCount
- : 0;
- }
- }
- internal NameValueCollection Parameters {
- get {
- return _parameters;
- }
- }
- #endregion
- #region Public Properties
- public string Algorithm {
- get {
- return _parameters["algorithm"];
- }
- }
- public string Cnonce {
- get {
- return _parameters["cnonce"];
- }
- }
- public string Nc {
- get {
- return _parameters["nc"];
- }
- }
- public string Nonce {
- get {
- return _parameters["nonce"];
- }
- }
- public string Opaque {
- get {
- return _parameters["opaque"];
- }
- }
- public string Password {
- get {
- return _parameters["password"];
- }
- }
- public string Qop {
- get {
- return _parameters["qop"];
- }
- }
- public string Realm {
- get {
- return _parameters["realm"];
- }
- }
- public string Response {
- get {
- return _parameters["response"];
- }
- }
- public AuthenticationSchemes Scheme {
- get {
- return _scheme;
- }
- }
- public string Uri {
- get {
- return _parameters["uri"];
- }
- }
- public string UserName {
- get {
- return _parameters["username"];
- }
- }
- #endregion
- #region Private Methods
- private static string createA1 (
- string username, string password, string realm
- )
- {
- return String.Format ("{0}:{1}:{2}", username, realm, password);
- }
- private static string createA1 (
- string username,
- string password,
- string realm,
- string nonce,
- string cnonce
- )
- {
- var a1 = createA1 (username, password, realm);
- return String.Format ("{0}:{1}:{2}", hash (a1), nonce, cnonce);
- }
- private static string createA2 (string method, string uri)
- {
- return String.Format ("{0}:{1}", method, uri);
- }
- private static string createA2 (string method, string uri, string entity)
- {
- return String.Format ("{0}:{1}:{2}", method, uri, hash (entity));
- }
- private static string hash (string value)
- {
- var md5 = MD5.Create ();
- var bytes = Encoding.UTF8.GetBytes (value);
- var res = md5.ComputeHash (bytes);
- var buff = new StringBuilder (64);
- foreach (var b in res)
- buff.Append (b.ToString ("x2"));
- return buff.ToString ();
- }
- private void initAsDigest ()
- {
- var qops = _parameters["qop"];
- if (qops != null) {
- var auth = qops.Split (',').Contains (
- qop => qop.Trim ().ToLower () == "auth"
- );
- if (auth) {
- _parameters["qop"] = "auth";
- _parameters["cnonce"] = AuthenticationChallenge.CreateNonceValue ();
- _parameters["nc"] = String.Format ("{0:x8}", ++_nonceCount);
- }
- else {
- _parameters["qop"] = null;
- }
- }
- _parameters["method"] = "GET";
- _parameters["response"] = CreateRequestDigest (_parameters);
- }
- #endregion
- #region Internal Methods
- internal static string CreateRequestDigest (NameValueCollection parameters)
- {
- var user = parameters["username"];
- var pass = parameters["password"];
- var realm = parameters["realm"];
- var nonce = parameters["nonce"];
- var uri = parameters["uri"];
- var algo = parameters["algorithm"];
- var qop = parameters["qop"];
- var cnonce = parameters["cnonce"];
- var nc = parameters["nc"];
- var method = parameters["method"];
- var a1 = algo != null && algo.ToLower () == "md5-sess"
- ? createA1 (user, pass, realm, nonce, cnonce)
- : createA1 (user, pass, realm);
- var a2 = qop != null && qop.ToLower () == "auth-int"
- ? createA2 (method, uri, parameters["entity"])
- : createA2 (method, uri);
- var secret = hash (a1);
- var data = qop != null
- ? String.Format (
- "{0}:{1}:{2}:{3}:{4}", nonce, nc, cnonce, qop, hash (a2)
- )
- : String.Format ("{0}:{1}", nonce, hash (a2));
- var keyed = String.Format ("{0}:{1}", secret, data);
- return hash (keyed);
- }
- internal static AuthenticationResponse Parse (string value)
- {
- try {
- var cred = value.Split (new[] { ' ' }, 2);
- if (cred.Length != 2)
- return null;
- var schm = cred[0].ToLower ();
- if (schm == "basic") {
- var parameters = ParseBasicCredentials (cred[1]);
- return new AuthenticationResponse (
- AuthenticationSchemes.Basic, parameters
- );
- }
- if (schm == "digest") {
- var parameters = AuthenticationChallenge.ParseParameters (cred[1]);
- return new AuthenticationResponse (
- AuthenticationSchemes.Digest, parameters
- );
- }
- return null;
- }
- catch {
- return null;
- }
- }
- internal static NameValueCollection ParseBasicCredentials (string value)
- {
- var ret = new NameValueCollection ();
- // Decode the basic-credentials (a Base64 encoded string).
- var bytes = Convert.FromBase64String (value);
- var userPass = Encoding.Default.GetString (bytes);
- // The format is [<domain>\]<username>:<password>.
- var i = userPass.IndexOf (':');
- var user = userPass.Substring (0, i);
- var pass = i < userPass.Length - 1
- ? userPass.Substring (i + 1)
- : String.Empty;
- // Check if <domain> exists.
- i = user.IndexOf ('\\');
- if (i > -1)
- user = user.Substring (i + 1);
- ret["username"] = user;
- ret["password"] = pass;
- return ret;
- }
- internal string ToBasicString ()
- {
- var user = _parameters["username"];
- var pass = _parameters["password"];
- var userPass = String.Format ("{0}:{1}", user, pass);
- var bytes = Encoding.UTF8.GetBytes (userPass);
- var cred = Convert.ToBase64String (bytes);
- return "Basic " + cred;
- }
- internal string ToDigestString ()
- {
- var buff = new StringBuilder (256);
- var user = _parameters["username"];
- var realm = _parameters["realm"];
- var nonce = _parameters["nonce"];
- var uri = _parameters["uri"];
- var res = _parameters["response"];
- buff.AppendFormat (
- "Digest username=\"{0}\", realm=\"{1}\", nonce=\"{2}\", uri=\"{3}\", response=\"{4}\"",
- user,
- realm,
- nonce,
- uri,
- res
- );
- var opaque = _parameters["opaque"];
- if (opaque != null)
- buff.AppendFormat (", opaque=\"{0}\"", opaque);
- var algo = _parameters["algorithm"];
- if (algo != null)
- buff.AppendFormat (", algorithm={0}", algo);
- var qop = _parameters["qop"];
- if (qop != null) {
- var cnonce = _parameters["cnonce"];
- var nc = _parameters["nc"];
- buff.AppendFormat (
- ", qop={0}, cnonce=\"{1}\", nc={2}", qop, cnonce, nc
- );
- }
- return buff.ToString ();
- }
- #endregion
- #region Public Methods
- public IIdentity ToIdentity ()
- {
- if (_scheme == AuthenticationSchemes.Basic) {
- var user = _parameters["username"];
- var pass = _parameters["password"];
- return new HttpBasicIdentity (user, pass);
- }
- if (_scheme == AuthenticationSchemes.Digest)
- return new HttpDigestIdentity (_parameters);
- return null;
- }
- public override string ToString ()
- {
- if (_scheme == AuthenticationSchemes.Basic)
- return ToBasicString ();
- if (_scheme == AuthenticationSchemes.Digest)
- return ToDigestString ();
- return String.Empty;
- }
- #endregion
- }
- }
|