websocket-sharp/websocket-sharp/AuthenticationResponse.cs

329 lines
7.5 KiB
C#

#region License
/*
* AuthenticationResponse.cs
*
* The MIT License
*
* Copyright (c) 2013 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.Security.Cryptography;
using System.Text;
namespace WebSocketSharp {
internal class AuthenticationResponse {
#region Private Fields
private string _algorithm;
private string _cnonce;
private string _method;
private string _nc;
private string _nonce;
private string _opaque;
private string _password;
private string _qop;
private string _realm;
private string _response;
private string _scheme;
private string _uri;
private string _userName;
#endregion
#region Private Constructors
private AuthenticationResponse()
{
}
#endregion
#region Public Constructors
public AuthenticationResponse(WsCredential credential)
{
_userName = credential.UserName;
_password = credential.Password;
_scheme = "Basic";
}
public AuthenticationResponse(WsCredential credential, AuthenticationChallenge challenge)
{
_userName = credential.UserName;
_password = credential.Password;
_scheme = challenge.Scheme;
_realm = challenge.Realm;
if (_scheme == "Digest")
initForDigest(credential, challenge);
}
#endregion
#region Public Properties
public string Algorithm {
get {
return _algorithm ?? String.Empty;
}
private set {
_algorithm = value;
}
}
public string Cnonce {
get {
return _cnonce ?? String.Empty;
}
private set {
_cnonce = value;
}
}
public string Nc {
get {
return _nc ?? String.Empty;
}
private set {
_nc = value;
}
}
public string Nonce {
get {
return _nonce ?? String.Empty;
}
private set {
_nonce = value;
}
}
public string Opaque {
get {
return _opaque ?? String.Empty;
}
private set {
_opaque = value;
}
}
public string Qop {
get {
return _qop ?? String.Empty;
}
private set {
_qop = value;
}
}
public string Realm {
get {
return _realm ?? String.Empty;
}
private set {
_realm = value;
}
}
public string Response {
get {
return _response ?? String.Empty;
}
private set {
_response = value;
}
}
public string Scheme {
get {
return _scheme ?? String.Empty;
}
private set {
_scheme = value;
}
}
public string Uri {
get {
return _uri ?? String.Empty;
}
private set {
_uri = value;
}
}
public string UserName {
get {
return _userName ?? String.Empty;
}
private set {
_userName = value;
}
}
#endregion
#region Private Methods
private string a1()
{
var result = String.Format("{0}:{1}:{2}", _userName, _realm, _password);
return _algorithm != null && _algorithm.ToLower() == "md5-sess"
? String.Format("{0}:{1}:{2}", hash(result), _nonce, _cnonce)
: result;
}
private string a2()
{
return String.Format("{0}:{1}", _method, _uri);
}
private static string createNonceValue()
{
var src = new byte[16];
var rand = new Random();
rand.NextBytes(src);
var nonce = new StringBuilder(32);
foreach (var b in src)
nonce.Append(b.ToString("x2"));
return nonce.ToString();
}
private string createRequestDigest()
{
if (Qop == "auth")
{
var data = String.Format("{0}:{1}:{2}:{3}:{4}",
_nonce, _nc, _cnonce, _qop, hash(a2()));
return kd(hash(a1()), data);
}
return kd(hash(a1()), String.Format("{0}:{1}", _nonce, hash(a2())));
}
private static string hash(string value)
{
var md5 = MD5.Create();
var src = Encoding.UTF8.GetBytes(value);
var hashed = md5.ComputeHash(src);
var result = new StringBuilder(64);
foreach (var b in hashed)
result.Append(b.ToString("x2"));
return result.ToString();
}
private void initForDigest(WsCredential credential, AuthenticationChallenge challenge)
{
_nonce = challenge.Nonce;
_method = "GET";
_uri = credential.Domain;
_algorithm = challenge.Algorithm;
_opaque = challenge.Opaque;
foreach (var qop in challenge.Qop.Split(','))
{
if (qop.Trim().ToLower() == "auth")
{
_qop = "auth";
_nc = "00000001";
break;
}
}
_cnonce = createNonceValue();
_response = createRequestDigest();
}
private static string kd(string secret, string data)
{
var concatenated = String.Format("{0}:{1}", secret, data);
return hash(concatenated);
}
private string toBasicCredentials()
{
var userPass = String.Format("{0}:{1}", _userName, _password);
var base64UserPass = Convert.ToBase64String(Encoding.UTF8.GetBytes(userPass));
return "Basic " + base64UserPass;
}
private string toDigestCredentials()
{
var digestResponse = new StringBuilder(64);
digestResponse.AppendFormat("username={0}", _userName.Quote());
digestResponse.AppendFormat(", realm={0}", _realm.Quote());
digestResponse.AppendFormat(", nonce={0}", _nonce.Quote());
digestResponse.AppendFormat(", uri={0}", _uri.Quote());
digestResponse.AppendFormat(", response={0}", _response.Quote());
if (!_algorithm.IsNullOrEmpty())
digestResponse.AppendFormat(", algorithm={0}", _algorithm);
if (!_opaque.IsNullOrEmpty())
digestResponse.AppendFormat(", opaque={0}", _opaque.Quote());
if (!_qop.IsNullOrEmpty())
digestResponse.AppendFormat(", qop={0}", _qop);
if (!_nc.IsNullOrEmpty())
digestResponse.AppendFormat(", nc={0}", _nc);
if (!_qop.IsNullOrEmpty())
digestResponse.AppendFormat(", cnonce={0}", _cnonce.Quote());
return "Digest " + digestResponse.ToString();
}
#endregion
#region Public Methods
public static AuthenticationResponse Parse(string response)
{
throw new NotImplementedException();
}
public override string ToString()
{
return _scheme == "Basic"
? toBasicCredentials()
: toDigestCredentials();
}
#endregion
}
}