1- namespace RestSharp . Authenticators . Digest
1+ using System ;
2+ using System . Diagnostics ;
3+ using System . Globalization ;
4+ using System . Linq ;
5+ using System . Net ;
6+ using System . Security . Cryptography ;
7+ using System . Text ;
8+
9+ namespace RestSharp . Authenticators . Digest
210{
3- using System . Collections . Generic ;
4- using System . Globalization ;
5- using System . Linq ;
6- using System . Net ;
7- using System . Security . Cryptography ;
8- using System . Text ;
9- using System . Text . RegularExpressions ;
10- using System ;
11-
12- using RestSharp ;
13-
1411 /// <summary>
15- /// DigestAuthenticatorManager class.
12+ /// DigestAuthenticatorManager class.
1613 /// </summary>
1714 internal class DigestAuthenticatorManager
1815 {
19- /// <summary>
20- /// Header Realm.
21- /// </summary>
22- private const string DIGEST_REALM = "Digest realm" ;
23-
24- /// <summary>
25- /// Header Realm.
26- /// </summary>
27- private const string REALM = "realm" ;
28-
29- /// <summary>
30- /// Header nonce.
31- /// </summary>
32- private const string NONCE = "nonce" ;
33-
34- /// <summary>
35- /// Header qop.
36- /// </summary>
37- private const string QOP = "qop" ;
38-
39- /// <summary>
40- /// The host.
41- /// </summary>
4216 private readonly Uri _host ;
4317
44- /// <summary>
45- /// The user.
46- /// </summary>
47- private readonly string _username ;
48-
49- /// <summary>
50- /// The password.
51- /// </summary>
5218 private readonly string _password ;
5319
54- /// <summary>
55- /// The timeout.
56- /// </summary>
5720 private readonly int _timeout ;
5821
22+ private readonly string _username ;
23+
5924 /// <summary>
60- /// The Realm that is returned by the first digest request (without the data) .
25+ /// The cnounce that is generated randomly by the application .
6126 /// </summary>
62- private string _realm ;
27+ private string _cnonce ;
6328
6429 /// <summary>
65- /// The nonce that is returned by the first digest request (without the data).
30+ /// The nonce that is returned by the first digest request (without the data).
6631 /// </summary>
6732 private string _nonce ;
6833
6934 /// <summary>
70- /// The qop that is returned by the first digest request (without the data).
35+ /// The qop that is returned by the first digest request (without the data).
7136 /// </summary>
7237 private string _qop ;
7338
7439 /// <summary>
75- /// The cnounce that is generated randomly by the application .
40+ /// The Realm that is returned by the first digest request (without the data) .
7641 /// </summary>
77- private string _cnonce ;
78-
79- /// <summary>
80- /// The nounce count (usually 000001)
81- /// </summary>
82- private const int NONCE_COUNT = 1 ;
42+ private string _realm ;
8343
8444 /// <summary>
85- /// Creates a new instance of <see cref="DigestAuthenticatorManager"/> class.
45+ /// Creates a new instance of <see cref="DigestAuthenticatorManager" /> class.
8646 /// </summary>
8747 /// <param name="host">The host.</param>
8848 /// <param name="username">The username.</param>
@@ -97,58 +57,22 @@ public DigestAuthenticatorManager(Uri host, string username, string password, in
9757 }
9858
9959 /// <summary>
100- /// Generate the MD5 Hash.
101- /// </summary>
102- /// <param name="input">The input.</param>
103- /// <returns>The MD5.</returns>
104- private static string GenerateMD5 ( string input )
105- {
106- var inputBytes = Encoding . ASCII . GetBytes ( input ) ;
107- var hash = MD5 . Create ( ) . ComputeHash ( inputBytes ) ;
108- var stringBuilder = new StringBuilder ( ) ;
109- hash . ToList ( ) . ForEach ( b => stringBuilder . Append ( b . ToString ( "x2" ) ) ) ;
110- return stringBuilder . ToString ( ) ;
111- }
112-
113- /// <summary>
114- /// Gets the digest header.
115- /// </summary>
116- /// <param name="digestUri">The digest uri.</param>
117- /// <param name="method">The method.</param>
118- /// <returns>The digest header.</returns>
119- public string GetDigestHeader ( string digestUri , Method method )
120- {
121- var hash1 = GenerateMD5 ( $ "{ _username } :{ _realm } :{ _password } ") ;
122- var hash2 = GenerateMD5 ( $ "{ method } :{ digestUri } ") ;
123- var digestResponse = GenerateMD5 ( $ "{ hash1 } :{ _nonce } :{ NONCE_COUNT : 00000000} :{ _cnonce } :{ _qop } :{ hash2 } ") ;
124- return $ "Digest username=\" { _username } \" ," +
125- $ "realm=\" { _realm } \" ," +
126- $ "nonce=\" { _nonce } \" ," +
127- $ "uri=\" { digestUri } \" ," +
128- "algorithm=MD5," +
129- $ "response=\" { digestResponse } \" ," +
130- $ "qop={ _qop } ," +
131- $ "nc={ NONCE_COUNT : 00000000} ," +
132- $ "cnonce=\" { _cnonce } \" ";
133- }
134-
135- /// <summary>
136- /// Gets the digest auth header.
60+ /// Gets the digest auth header.
13761 /// </summary>
13862 /// <param name="path">The request path.</param>
13963 /// <param name="method">The request method.</param>
14064 public void GetDigestAuthHeader ( string path , Method method )
14165 {
14266 var uri = new Uri ( _host , path ) ;
143- var request = ( HttpWebRequest ) WebRequest . Create ( uri ) ;
67+ var request = ( HttpWebRequest ) WebRequest . Create ( uri ) ;
14468 request . Method = method . ToString ( ) ;
14569 request . ContentLength = 0 ;
14670 request . Timeout = _timeout ;
14771
14872 try
14973 {
150- var response = ( HttpWebResponse ) request . GetResponse ( ) ;
151- System . Diagnostics . Debug . WriteLine ( response ) ;
74+ var response = ( HttpWebResponse ) request . GetResponse ( ) ;
75+ Debug . WriteLine ( response ) ;
15276 }
15377 catch ( WebException ex )
15478 {
@@ -157,105 +81,58 @@ public void GetDigestAuthHeader(string path, Method method)
15781 }
15882
15983 /// <summary>
160- /// Gets the digest data from exception .
84+ /// Gets the digest header .
16185 /// </summary>
162- /// <param name="ex">The exception.</param>
163- private void GetDigestDataFromException ( WebException ex )
86+ /// <param name="digestUri">The digest uri.</param>
87+ /// <param name="method">The method.</param>
88+ /// <returns>The digest header.</returns>
89+ public string GetDigestHeader ( string digestUri , Method method )
16490 {
165- if ( ex . Response == null || ( ( HttpWebResponse ) ex . Response ) . StatusCode != HttpStatusCode . Unauthorized )
166- {
167- throw ex ;
168- }
169-
170- var wwwAuthenticateHeader = TransformHeaderToDictionary (
171- ex . Response . Headers [ "WWW-Authenticate" ]
172- ) ;
173-
174- _cnonce = new Random ( )
175- . Next ( 123400 , 9999999 )
176- . ToString ( CultureInfo . InvariantCulture ) ;
177-
178- _realm = wwwAuthenticateHeader . GetHeader ( REALM ) ;
179- _nonce = wwwAuthenticateHeader . GetHeader ( NONCE ) ;
180- _qop = wwwAuthenticateHeader . GetHeader ( QOP ) ;
91+ var hash1 = GenerateMD5 ( $ "{ _username } :{ _realm } :{ _password } ") ;
92+ var hash2 = GenerateMD5 ( $ "{ method } :{ digestUri } ") ;
93+ var digestResponse =
94+ GenerateMD5 ( $ "{ hash1 } :{ _nonce } :{ DigestHeader . NONCE_COUNT : 00000000} :{ _cnonce } :{ _qop } :{ hash2 } ") ;
95+ return $ "Digest username=\" { _username } \" ," +
96+ $ "realm=\" { _realm } \" ," +
97+ $ "nonce=\" { _nonce } \" ," +
98+ $ "uri=\" { digestUri } \" ," +
99+ "algorithm=MD5," +
100+ $ "response=\" { digestResponse } \" ," +
101+ $ "qop={ _qop } ," +
102+ $ "nc={ DigestHeader . NONCE_COUNT : 00000000} ," +
103+ $ "cnonce=\" { _cnonce } \" ";
181104 }
182105
183106 /// <summary>
184- /// Transform the header to dictionary .
107+ /// Generate the MD5 Hash .
185108 /// </summary>
186- /// <param name="wwwAuthenticateHeader ">The header </param>
187- /// <returns>A instance of <see cref="IDictionary{K,V}"/> .</returns>
188- private static IDictionary < string , string > TransformHeaderToDictionary ( string wwwAuthenticateHeader )
109+ /// <param name="input ">The input. </param>
110+ /// <returns>The MD5 .</returns>
111+ private static string GenerateMD5 ( string input )
189112 {
190- var regex = new Regex ( "realm=\" (?<realm>.*?)\" |qop=\" (?<qop>.*?)\" |nonce=\" (?<nonce>.*?)\" |stale=\" (?<stale>.*?)\" |opaque=\" (?<opaque>.*?)\" |domain=\" (?<domain>.*?)\" " , RegexOptions . IgnoreCase | RegexOptions . Compiled ) ;
191- var matches = regex . Matches ( wwwAuthenticateHeader ) ;
192-
193- var dict = new Dictionary < string , string > ( ) ;
194-
195- // There should be 6 matches
196- foreach ( Match m in matches )
197- {
198- if ( ! m . Success )
199- {
200- continue ;
201- }
202-
203- if ( m . Groups [ QOP ] . Success )
204- {
205- dict . Add ( QOP , m . Groups [ QOP ] . Value ) ;
206- }
207- if ( m . Groups [ REALM ] . Success )
208- {
209- dict . Add ( REALM , m . Groups [ REALM ] . Value ) ;
210- }
211- if ( m . Groups [ NONCE ] . Success )
212- {
213- dict . Add ( NONCE , m . Groups [ NONCE ] . Value ) ;
214- }
215- //if (m.Groups[STALE].Success)
216- //{
217- // dict.Add(STALE, m.Groups[STALE].Value);
218- //}
219- //if (m.Groups[OPAQUE].Success)
220- //{
221- // dict.Add(OPAQUE, m.Groups[OPAQUE].Value);
222- //}
223- //if (m.Groups[DOMAIN].Success)
224- //{
225- // dict.Add(DOMAIN, m.Groups[DOMAIN].Value);
226- //}
227- }
228-
229- return dict ;
113+ var inputBytes = Encoding . ASCII . GetBytes ( input ) ;
114+ var hash = MD5 . Create ( ) . ComputeHash ( inputBytes ) ;
115+ var stringBuilder = new StringBuilder ( ) ;
116+ hash . ToList ( ) . ForEach ( b => stringBuilder . Append ( b . ToString ( "x2" ) ) ) ;
117+ return stringBuilder . ToString ( ) ;
230118 }
231- }
232119
233- /// <summary>
234- /// Dictionary extension.
235- /// </summary>
236- internal static class DictionaryHeaderExtension
237- {
238- internal static string GetHeader ( this IDictionary < string , string > header , string key )
120+ private void GetDigestDataFromException ( WebException ex )
239121 {
240- if ( header . TryGetValue ( key , out var value ) )
122+ if ( ex . Response == null || ( ( HttpWebResponse ) ex . Response ) . StatusCode != HttpStatusCode . Unauthorized )
241123 {
242- return value ;
124+ throw ex ;
243125 }
244126
245- throw new ApplicationException ( $ "Header not found: { key } ") ;
246- }
127+ var digestHeader = new DigestHeader ( ex . Response . Headers [ "WWW-Authenticate" ] ) ;
247128
248- internal static string GetFirstHeader ( this IDictionary < string , string > header , params string [ ] keys )
249- {
250- foreach ( var key in keys )
251- {
252- if ( header . TryGetValue ( key , out var value ) )
253- {
254- return value ;
255- }
256- }
129+ _cnonce = new Random ( )
130+ . Next ( 123400 , 9999999 )
131+ . ToString ( CultureInfo . InvariantCulture ) ;
257132
258- throw new ApplicationException ( $ "No Headers found with following keys: { string . Join ( "," , keys ) } ") ;
133+ _realm = digestHeader . Realm ;
134+ _nonce = digestHeader . Nonce ;
135+ _qop = digestHeader . Qop ;
259136 }
260137 }
261138}
0 commit comments