1: /*
2: Copyright (c) 2010 <a href="http://www.gutgames.com">James Craig</a>
3:
4: Permission is hereby granted, free of charge, to any person obtaining a copy
5: of this software and associated documentation files (the "Software"), to deal
6: in the Software without restriction, including without limitation the rights
7: to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8: copies of the Software, and to permit persons to whom the Software is
9: furnished to do so, subject to the following conditions:
10:
11: The above copyright notice and this permission notice shall be included in
12: all copies or substantial portions of the Software.
13:
14: THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15: IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16: FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17: AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18: LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19: OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
20: THE SOFTWARE.*/
21:
22: #region Usings
23: using System;
24: using System.Collections.Generic;
25: using System.Security.Cryptography;
26: using System.Text;
27: using Utilities.DataTypes;
28: #endregion
29:
30: namespace Utilities.Web.OAuth
31: { 32: /// <summary>
33: /// OAuth base class
34: /// </summary>
35: public class OAuth
36: { 37: #region Constructor
38:
39: /// <summary>
40: /// Constructor
41: /// </summary>
42: public OAuth()
43: { 44: Parameters = new System.Collections.Generic.List<Pair<string, string>>();
45: AddParameter("oauth_consumer_key", ""); 46: AddParameter("oauth_nonce", ""); 47: AddParameter("oauth_signature_method", ""); 48: AddParameter("oauth_timestamp", ""); 49: AddParameter("oauth_version", "1.0"); 50: RandomGenerator = new Random.Random();
51: }
52:
53: #endregion
54:
55: #region Protected Functions
56:
57: /// <summary>
58: /// Generates a request
59: /// </summary>
60: /// <returns>The string containing the request</returns>
61: protected string GenerateRequest()
62: { 63: string Url = "";
64: string Parameters = "";
65: string Signature = GenerateSignature(out Url, out Parameters);
66: string ReturnUrl = Url.ToString() + "?" + Parameters + "&oauth_signature=" + UrlEncode(Signature);
67: return ReturnUrl;
68: }
69:
70: /// <summary>
71: /// Generates the signature
72: /// </summary>
73: /// <param name="Url">Url</param>
74: /// <param name="Parameters">Parameters</param>
75: /// <returns>The signature</returns>
76: protected string GenerateSignature(out string Url, out string Parameters)
77: { 78: Parameters = "";
79: Url = "";
80:
81: if (this.SignatureType == Signature.HMACSHA1)
82: { 83: string Base = GenerateBase(out Url, out Parameters);
84: HMACSHA1 SHA1 = new HMACSHA1();
85: SHA1.Key = Encoding.ASCII.GetBytes(UrlEncode(ConsumerKeySecret) + "&" + (string.IsNullOrEmpty(TokenSecret) ? "" : UrlEncode(TokenSecret)));
86: return Convert.ToBase64String(SHA1.ComputeHash(System.Text.Encoding.ASCII.GetBytes(Base)));
87: }
88: else if (this.SignatureType == Signature.RSASHA1)
89: { 90: throw new NotImplementedException();
91: }
92: else if (this.SignatureType == Signature.PLAINTEXT)
93: { 94: return UrlEncode(ConsumerKeySecret + "&" + TokenSecret);
95: }
96: return "";
97: }
98:
99: /// <summary>
100: /// Does url encoding using uppercase since that is needed for .Net
101: /// </summary>
102: /// <param name="Input">Input string</param>
103: /// <returns>Url encoded string</returns>
104: protected string UrlEncode(string Input)
105: { 106: StringBuilder Result = new StringBuilder();
107: for (int x = 0; x < Input.Length; ++x)
108: { 109: if (UnreservedChars.IndexOf(Input[x]) != -1)
110: Result.Append(Input[x]);
111: else
112: Result.Append("%").Append(String.Format("{0:X2}", (int)Input[x])); 113: }
114: return Result.ToString();
115: }
116:
117: /// <summary>
118: /// Adds a parameter
119: /// </summary>
120: /// <param name="Key">Key text</param>
121: /// <param name="Value">Value text</param>
122: protected void AddParameter(string Key, string Value)
123: { 124: bool Found = false;
125: foreach (Pair<string, string> Pair in Parameters)
126: { 127: if (Pair.Left == Key)
128: { 129: Pair.Right = Value;
130: Found = true;
131: break;
132: }
133: }
134: if (!Found)
135: { 136: Parameters.Add(new Pair<string, string>(Key, Value));
137: }
138: }
139:
140: #endregion
141:
142: #region Private Functions
143:
144: /// <summary>
145: /// Generates the info used in the signature
146: /// </summary>
147: /// <param name="UrlString">Url string</param>
148: /// <param name="ParameterString">Parameter string</param>
149: /// <returns>The base information for the signature</returns>
150: private string GenerateBase(out string UrlString, out string ParameterString)
151: { 152: StringBuilder UrlBuilder = new StringBuilder();
153: StringBuilder Builder = new StringBuilder();
154: StringBuilder ParameterBuilder = new StringBuilder();
155:
156: string SignatureMethod = "";
157: if (this.SignatureType == Signature.HMACSHA1)
158: SignatureMethod = "HMAC-SHA1";
159: else if (this.SignatureType == Signature.RSASHA1)
160: SignatureMethod = "RSA-SHA1";
161: else if (this.SignatureType == Signature.PLAINTEXT)
162: SignatureMethod = "PLAINTEXT";
163:
164: AddParameter("oauth_consumer_key", this.ConsumerKey); 165: AddParameter("oauth_nonce", RandomGenerator.Next(123400, 9999999).ToString()); 166: AddParameter("oauth_signature_method", SignatureMethod); 167: AddParameter("oauth_timestamp", Convert.ToInt64((DateTime.UtcNow - new DateTime(1970, 1, 1, 0, 0, 0, 0)).TotalSeconds).ToString()); 168: AddParameter("oauth_version", "1.0"); 169:
170: if (!string.IsNullOrEmpty(this.Token))
171: AddParameter("oauth_token", this.Token); 172: if (!string.IsNullOrEmpty(this.TokenSecret))
173: AddParameter("oauth_token_secret", this.TokenSecret); 174:
175: Parameters.Sort(new PairComparer());
176:
177: string Splitter = "";
178: foreach (Pair<string, string> Key in Parameters)
179: { 180: ParameterBuilder.Append(Splitter)
181: .Append(Key.Left)
182: .Append("=") 183: .Append(UrlEncode(Key.Right));
184: Splitter = "&";
185: }
186:
187: UrlBuilder.Append(Url.Scheme).Append("://").Append(Url.Host); 188: if ((Url.Scheme == "http" && Url.Port != 80) || (Url.Scheme == "https" && Url.Port != 443))
189: UrlBuilder.Append(":").Append(Url.Port); 190: UrlBuilder.Append(Url.AbsolutePath);
191:
192: UrlString = UrlBuilder.ToString();
193: ParameterString = ParameterBuilder.ToString();
194:
195: Builder.Append(this.Method.ToString().ToUpper())
196: .Append("&") 197: .Append(UrlEncode(UrlBuilder.ToString()))
198: .Append("&") 199: .Append(UrlEncode(ParameterBuilder.ToString()));
200:
201: return Builder.ToString();
202: }
203:
204: #endregion
205:
206: #region Protected Properties
207:
208: /// <summary>
209: /// Url that is being used
210: /// </summary>
211: protected Uri Url { get; set; } 212:
213: /// <summary>
214: /// Consumer key
215: /// </summary>
216: public virtual string ConsumerKey { get; set; } 217:
218: /// <summary>
219: /// Consumer key secret
220: /// </summary>
221: public virtual string ConsumerKeySecret { get; set; } 222:
223: /// <summary>
224: /// Token
225: /// </summary>
226: public virtual string Token { get; set; } 227:
228: /// <summary>
229: /// Token secret
230: /// </summary>
231: public virtual string TokenSecret { get; set; } 232:
233: /// <summary>
234: /// HTTP Method
235: /// </summary>
236: protected HTTPMethod Method { get; set; } 237:
238: /// <summary>
239: /// The hash type that is suppose to be used
240: /// </summary>
241: protected Signature SignatureType { get; set; } 242:
243: #endregion
244:
245: #region Private Variables
246:
247: private string UnreservedChars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-_.~";
248: private System.Collections.Generic.List<Pair<string, string>> Parameters { get; set; } 249: private Random.Random RandomGenerator { get; set; } 250:
251: #endregion
252:
253: #region Private Classes
254:
255: /// <summary>
256: /// Comparer class for the pair type
257: /// </summary>
258: private class PairComparer : IComparer<Pair<string, string>>
259: { 260: public int Compare(Pair<string, string> x, Pair<string, string> y)
261: { 262: if (x.Left == y.Left)
263: { 264: return string.Compare(x.Right, y.Right);
265: }
266: else
267: { 268: return string.Compare(x.Left, y.Left);
269: }
270: }
271: }
272:
273: #endregion
274: }
275:
276: #region Enums
277:
278: /// <summary>
279: /// HTTP Method
280: /// </summary>
281: public enum HTTPMethod
282: { 283: GET,
284: POST,
285: DELETE,
286: PUT
287: }
288:
289: /// <summary>
290: /// Hash type
291: /// </summary>
292: public enum Signature
293: { 294: PLAINTEXT,
295: RSASHA1,
296: HMACSHA1
297: }
298:
299: #endregion
300: }