Mercurial > altnet-hispano
comparison Agendas/trunk/src/Agendas.Twitter/oAuth.cs @ 90:d1688622fa88
Autenticando con twitter (falta emprolijar el código, pero autentica!)
author | Nelo@Kenia.neluz.int |
---|---|
date | Fri, 03 Jun 2011 21:35:59 -0300 |
parents | |
children |
comparison
equal
deleted
inserted
replaced
89:24e9488ac152 | 90:d1688622fa88 |
---|---|
1 using System; | |
2 using System.Security.Cryptography; | |
3 using System.Collections.Generic; | |
4 using System.Text; | |
5 using System.Web; | |
6 | |
7 namespace AltNetHispano.Agendas.Twitter | |
8 { | |
9 public class OAuthBase | |
10 { | |
11 | |
12 /// <summary> | |
13 /// Provides a predefined set of algorithms that are supported officially by the protocol | |
14 /// </summary> | |
15 public enum SignatureTypes | |
16 { | |
17 HMACSHA1, | |
18 PLAINTEXT, | |
19 RSASHA1 | |
20 } | |
21 | |
22 /// <summary> | |
23 /// Provides an internal structure to sort the query parameter | |
24 /// </summary> | |
25 protected class QueryParameter | |
26 { | |
27 private string name = null; | |
28 private string value = null; | |
29 | |
30 public QueryParameter(string name, string value) | |
31 { | |
32 this.name = name; | |
33 this.value = value; | |
34 } | |
35 | |
36 public string Name | |
37 { | |
38 get { return name; } | |
39 } | |
40 | |
41 public string Value | |
42 { | |
43 get { return value; } | |
44 } | |
45 } | |
46 | |
47 /// <summary> | |
48 /// Comparer class used to perform the sorting of the query parameters | |
49 /// </summary> | |
50 protected class QueryParameterComparer : IComparer<QueryParameter> | |
51 { | |
52 | |
53 #region IComparer<QueryParameter> Members | |
54 | |
55 public int Compare(QueryParameter x, QueryParameter y) | |
56 { | |
57 if (x.Name == y.Name) | |
58 { | |
59 return string.Compare(x.Value, y.Value); | |
60 } | |
61 else | |
62 { | |
63 return string.Compare(x.Name, y.Name); | |
64 } | |
65 } | |
66 | |
67 #endregion | |
68 } | |
69 | |
70 protected const string OAuthVersion = "1.0"; | |
71 protected const string OAuthParameterPrefix = "oauth_"; | |
72 | |
73 // | |
74 // List of know and used oauth parameters' names | |
75 // | |
76 protected const string OAuthConsumerKeyKey = "oauth_consumer_key"; | |
77 protected const string OAuthCallbackKey = "oauth_callback"; | |
78 protected const string OAuthVersionKey = "oauth_version"; | |
79 protected const string OAuthSignatureMethodKey = "oauth_signature_method"; | |
80 protected const string OAuthSignatureKey = "oauth_signature"; | |
81 protected const string OAuthTimestampKey = "oauth_timestamp"; | |
82 protected const string OAuthNonceKey = "oauth_nonce"; | |
83 protected const string OAuthTokenKey = "oauth_token"; | |
84 protected const string OAuthTokenSecretKey = "oauth_token_secret"; | |
85 protected const string OAuthVerifierKey = "oauth_verifier"; | |
86 | |
87 protected const string HMACSHA1SignatureType = "HMAC-SHA1"; | |
88 protected const string PlainTextSignatureType = "PLAINTEXT"; | |
89 protected const string RSASHA1SignatureType = "RSA-SHA1"; | |
90 | |
91 protected Random random = new Random(); | |
92 | |
93 protected string unreservedChars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-_.~"; | |
94 | |
95 /// <summary> | |
96 /// Helper function to compute a hash value | |
97 /// </summary> | |
98 /// <param name="hashAlgorithm">The hashing algoirhtm used. If that algorithm needs some initialization, like HMAC and its derivatives, they should be initialized prior to passing it to this function</param> | |
99 /// <param name="data">The data to hash</param> | |
100 /// <returns>a Base64 string of the hash value</returns> | |
101 private string ComputeHash(HashAlgorithm hashAlgorithm, string data) | |
102 { | |
103 if (hashAlgorithm == null) | |
104 { | |
105 throw new ArgumentNullException("hashAlgorithm"); | |
106 } | |
107 | |
108 if (string.IsNullOrEmpty(data)) | |
109 { | |
110 throw new ArgumentNullException("data"); | |
111 } | |
112 | |
113 byte[] dataBuffer = System.Text.Encoding.ASCII.GetBytes(data); | |
114 byte[] hashBytes = hashAlgorithm.ComputeHash(dataBuffer); | |
115 | |
116 return Convert.ToBase64String(hashBytes); | |
117 } | |
118 | |
119 /// <summary> | |
120 /// Internal function to cut out all non oauth query string parameters (all parameters not begining with "oauth_") | |
121 /// </summary> | |
122 /// <param name="parameters">The query string part of the Url</param> | |
123 /// <returns>A list of QueryParameter each containing the parameter name and value</returns> | |
124 private List<QueryParameter> GetQueryParameters(string parameters) | |
125 { | |
126 if (parameters.StartsWith("?")) | |
127 { | |
128 parameters = parameters.Remove(0, 1); | |
129 } | |
130 | |
131 List<QueryParameter> result = new List<QueryParameter>(); | |
132 | |
133 if (!string.IsNullOrEmpty(parameters)) | |
134 { | |
135 string[] p = parameters.Split('&'); | |
136 foreach (string s in p) | |
137 { | |
138 if (!string.IsNullOrEmpty(s) && !s.StartsWith(OAuthParameterPrefix)) | |
139 { | |
140 if (s.IndexOf('=') > -1) | |
141 { | |
142 string[] temp = s.Split('='); | |
143 result.Add(new QueryParameter(temp[0], temp[1])); | |
144 } | |
145 else | |
146 { | |
147 result.Add(new QueryParameter(s, string.Empty)); | |
148 } | |
149 } | |
150 } | |
151 } | |
152 | |
153 return result; | |
154 } | |
155 | |
156 /// <summary> | |
157 /// This is a different Url Encode implementation since the default .NET one outputs the percent encoding in lower case. | |
158 /// While this is not a problem with the percent encoding spec, it is used in upper case throughout OAuth | |
159 /// </summary> | |
160 /// <param name="value">The value to Url encode</param> | |
161 /// <returns>Returns a Url encoded string</returns> | |
162 public string UrlEncode(string value) | |
163 { | |
164 StringBuilder result = new StringBuilder(); | |
165 | |
166 foreach (char symbol in value) | |
167 { | |
168 if (unreservedChars.IndexOf(symbol) != -1) | |
169 { | |
170 result.Append(symbol); | |
171 } | |
172 else | |
173 { | |
174 result.Append('%' + String.Format("{0:X2}", (int)symbol)); | |
175 } | |
176 } | |
177 | |
178 return result.ToString(); | |
179 } | |
180 | |
181 /// <summary> | |
182 /// Normalizes the request parameters according to the spec | |
183 /// </summary> | |
184 /// <param name="parameters">The list of parameters already sorted</param> | |
185 /// <returns>a string representing the normalized parameters</returns> | |
186 protected string NormalizeRequestParameters(IList<QueryParameter> parameters) | |
187 { | |
188 StringBuilder sb = new StringBuilder(); | |
189 QueryParameter p = null; | |
190 for (int i = 0; i < parameters.Count; i++) | |
191 { | |
192 p = parameters[i]; | |
193 sb.AppendFormat("{0}={1}", p.Name, p.Value); | |
194 | |
195 if (i < parameters.Count - 1) | |
196 { | |
197 sb.Append("&"); | |
198 } | |
199 } | |
200 | |
201 return sb.ToString(); | |
202 } | |
203 | |
204 /// <summary> | |
205 /// Generate the signature base that is used to produce the signature | |
206 /// </summary> | |
207 /// <param name="url">The full url that needs to be signed including its non OAuth url parameters</param> | |
208 /// <param name="consumerKey">The consumer key</param> | |
209 /// <param name="token">The token, if available. If not available pass null or an empty string</param> | |
210 /// <param name="tokenSecret">The token secret, if available. If not available pass null or an empty string</param> | |
211 /// <param name="callBackUrl">The callback URL (for OAuth 1.0a).If your client cannot accept callbacks, the value MUST be 'oob' </param> | |
212 /// <param name="oauthVerifier">This value MUST be included when exchanging Request Tokens for Access Tokens. Otherwise pass a null or an empty string</param> | |
213 /// <param name="httpMethod">The http method used. Must be a valid HTTP method verb (POST,GET,PUT, etc)</param> | |
214 /// <param name="signatureType">The signature type. To use the default values use <see cref="OAuthBase.SignatureTypes">OAuthBase.SignatureTypes</see>.</param> | |
215 /// <returns>The signature base</returns> | |
216 public string GenerateSignatureBase(Uri url, string consumerKey, string token, string tokenSecret, string callBackUrl, string oauthVerifier, string httpMethod, string timeStamp, string nonce, string signatureType, out string normalizedUrl, out string normalizedRequestParameters) | |
217 { | |
218 if (token == null) | |
219 { | |
220 token = string.Empty; | |
221 } | |
222 | |
223 if (tokenSecret == null) | |
224 { | |
225 tokenSecret = string.Empty; | |
226 } | |
227 | |
228 if (string.IsNullOrEmpty(consumerKey)) | |
229 { | |
230 throw new ArgumentNullException("consumerKey"); | |
231 } | |
232 | |
233 if (string.IsNullOrEmpty(httpMethod)) | |
234 { | |
235 throw new ArgumentNullException("httpMethod"); | |
236 } | |
237 | |
238 if (string.IsNullOrEmpty(signatureType)) | |
239 { | |
240 throw new ArgumentNullException("signatureType"); | |
241 } | |
242 | |
243 normalizedUrl = null; | |
244 normalizedRequestParameters = null; | |
245 | |
246 List<QueryParameter> parameters = GetQueryParameters(url.Query); | |
247 parameters.Add(new QueryParameter(OAuthVersionKey, OAuthVersion)); | |
248 parameters.Add(new QueryParameter(OAuthNonceKey, nonce)); | |
249 parameters.Add(new QueryParameter(OAuthTimestampKey, timeStamp)); | |
250 parameters.Add(new QueryParameter(OAuthSignatureMethodKey, signatureType)); | |
251 parameters.Add(new QueryParameter(OAuthConsumerKeyKey, consumerKey)); | |
252 | |
253 if (!string.IsNullOrEmpty(callBackUrl)) | |
254 { | |
255 parameters.Add(new QueryParameter(OAuthCallbackKey, UrlEncode(callBackUrl))); | |
256 } | |
257 | |
258 | |
259 if (!string.IsNullOrEmpty(oauthVerifier)) | |
260 { | |
261 parameters.Add(new QueryParameter(OAuthVerifierKey, oauthVerifier)); | |
262 } | |
263 | |
264 if (!string.IsNullOrEmpty(token)) | |
265 { | |
266 parameters.Add(new QueryParameter(OAuthTokenKey, token)); | |
267 } | |
268 | |
269 parameters.Sort(new QueryParameterComparer()); | |
270 | |
271 normalizedUrl = string.Format("{0}://{1}", url.Scheme, url.Host); | |
272 if (!((url.Scheme == "http" && url.Port == 80) || (url.Scheme == "https" && url.Port == 443))) | |
273 { | |
274 normalizedUrl += ":" + url.Port; | |
275 } | |
276 normalizedUrl += url.AbsolutePath; | |
277 normalizedRequestParameters = NormalizeRequestParameters(parameters); | |
278 | |
279 StringBuilder signatureBase = new StringBuilder(); | |
280 signatureBase.AppendFormat("{0}&", httpMethod.ToUpper()); | |
281 signatureBase.AppendFormat("{0}&", UrlEncode(normalizedUrl)); | |
282 signatureBase.AppendFormat("{0}", UrlEncode(normalizedRequestParameters)); | |
283 | |
284 return signatureBase.ToString(); | |
285 } | |
286 | |
287 /// <summary> | |
288 /// Generate the signature value based on the given signature base and hash algorithm | |
289 /// </summary> | |
290 /// <param name="signatureBase">The signature based as produced by the GenerateSignatureBase method or by any other means</param> | |
291 /// <param name="hash">The hash algorithm used to perform the hashing. If the hashing algorithm requires initialization or a key it should be set prior to calling this method</param> | |
292 /// <returns>A base64 string of the hash value</returns> | |
293 public string GenerateSignatureUsingHash(string signatureBase, HashAlgorithm hash) | |
294 { | |
295 return ComputeHash(hash, signatureBase); | |
296 } | |
297 | |
298 /// <summary> | |
299 /// Generates a signature using the HMAC-SHA1 algorithm | |
300 /// </summary> | |
301 /// <param name="url">The full url that needs to be signed including its non OAuth url parameters</param> | |
302 /// <param name="consumerKey">The consumer key</param> | |
303 /// <param name="consumerSecret">The consumer seceret</param> | |
304 /// <param name="token">The token, if available. If not available pass null or an empty string</param> | |
305 /// <param name="tokenSecret">The token secret, if available. If not available pass null or an empty string</param> | |
306 /// <param name="callBackUrl">The callback URL (for OAuth 1.0a).If your client cannot accept callbacks, the value MUST be 'oob' </param> | |
307 /// <param name="oauthVerifier">This value MUST be included when exchanging Request Tokens for Access Tokens. Otherwise pass a null or an empty string</param> | |
308 /// <param name="httpMethod">The http method used. Must be a valid HTTP method verb (POST,GET,PUT, etc)</param> | |
309 /// <returns>A base64 string of the hash value</returns> | |
310 public string GenerateSignature(Uri url, string consumerKey, string consumerSecret, string token, string tokenSecret, string callBackUrl, string oauthVerifier, string httpMethod, string timeStamp, string nonce, out string normalizedUrl, out string normalizedRequestParameters) | |
311 { | |
312 return GenerateSignature(url, consumerKey, consumerSecret, token, tokenSecret, callBackUrl, oauthVerifier, httpMethod, timeStamp, nonce, SignatureTypes.HMACSHA1, out normalizedUrl, out normalizedRequestParameters); | |
313 } | |
314 | |
315 /// <summary> | |
316 /// Generates a signature using the specified signatureType | |
317 /// </summary> | |
318 /// <param name="url">The full url that needs to be signed including its non OAuth url parameters</param> | |
319 /// <param name="consumerKey">The consumer key</param> | |
320 /// <param name="consumerSecret">The consumer seceret</param> | |
321 /// <param name="token">The token, if available. If not available pass null or an empty string</param> | |
322 /// <param name="tokenSecret">The token secret, if available. If not available pass null or an empty string</param> | |
323 /// <param name="callBackUrl">The callback URL (for OAuth 1.0a).If your client cannot accept callbacks, the value MUST be 'oob' </param> | |
324 /// <param name="oauthVerifier">This value MUST be included when exchanging Request Tokens for Access Tokens. Otherwise pass a null or an empty string</param> | |
325 /// <param name="httpMethod">The http method used. Must be a valid HTTP method verb (POST,GET,PUT, etc)</param> | |
326 /// <param name="signatureType">The type of signature to use</param> | |
327 /// <returns>A base64 string of the hash value</returns> | |
328 public string GenerateSignature(Uri url, string consumerKey, string consumerSecret, string token, string tokenSecret, string callBackUrl, string oauthVerifier, string httpMethod, string timeStamp, string nonce, SignatureTypes signatureType, out string normalizedUrl, out string normalizedRequestParameters) | |
329 { | |
330 normalizedUrl = null; | |
331 normalizedRequestParameters = null; | |
332 | |
333 switch (signatureType) | |
334 { | |
335 case SignatureTypes.PLAINTEXT: | |
336 return HttpUtility.UrlEncode(string.Format("{0}&{1}", consumerSecret, tokenSecret)); | |
337 case SignatureTypes.HMACSHA1: | |
338 string signatureBase = GenerateSignatureBase(url, consumerKey, token, tokenSecret, callBackUrl, oauthVerifier, httpMethod, timeStamp, nonce, HMACSHA1SignatureType, out normalizedUrl, out normalizedRequestParameters); | |
339 | |
340 HMACSHA1 hmacsha1 = new HMACSHA1(); | |
341 hmacsha1.Key = Encoding.ASCII.GetBytes(string.Format("{0}&{1}", UrlEncode(consumerSecret), string.IsNullOrEmpty(tokenSecret) ? "" : UrlEncode(tokenSecret))); | |
342 | |
343 return GenerateSignatureUsingHash(signatureBase, hmacsha1); | |
344 case SignatureTypes.RSASHA1: | |
345 throw new NotImplementedException(); | |
346 default: | |
347 throw new ArgumentException("Unknown signature type", "signatureType"); | |
348 } | |
349 } | |
350 | |
351 /// <summary> | |
352 /// Generate the timestamp for the signature | |
353 /// </summary> | |
354 /// <returns></returns> | |
355 public virtual string GenerateTimeStamp() | |
356 { | |
357 // Default implementation of UNIX time of the current UTC time | |
358 TimeSpan ts = DateTime.UtcNow - new DateTime(1970, 1, 1, 0, 0, 0, 0); | |
359 return Convert.ToInt64(ts.TotalSeconds).ToString(); | |
360 } | |
361 | |
362 /// <summary> | |
363 /// Generate a nonce | |
364 /// </summary> | |
365 /// <returns></returns> | |
366 public virtual string GenerateNonce() | |
367 { | |
368 // Just a simple implementation of a random number between 123400 and 9999999 | |
369 return random.Next(123400, 9999999).ToString(); | |
370 } | |
371 | |
372 } | |
373 } |