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: }