· 6 years ago · Mar 13, 2019, 05:52 PM
1using System;
2using System.Collections;
3using System.Data;
4using OutSystems.HubEdition.RuntimePlatform;
5using System.Text;
6using System.Collections.Generic;
7using System.Net;
8using System.Security.Cryptography;
9using System.Web;
10using OutSystems.RuntimePublic.REST;
11
12namespace OutSystems.NssTwitterSignature
13{
14
15 public class CssTwitterSignature : IssTwitterSignature
16 {
17
18 /// <summary>
19 ///
20 /// </summary>
21 /// <param name="ssConsumerKey"></param>
22 /// <param name="ssConsumerSecret"></param>
23 /// <param name="ssOAuthToken"></param>
24 /// <param name="ssOAuthTokenSecret"></param>
25 public void MssSignUploadRequest(string ssConsumerKey, string ssConsumerSecret, string ssOAuthToken, string ssOAuthTokenSecret) {
26 // TODO: Write implementation for action
27 // This code is based on
28 // http://garyshortblog.wordpress.com/2011/02/11/a-twitter-oauth-example-in-c/
29
30 RestRequest context = RestRequest.GetCurrent();
31
32 // Get the current http request, we will be adding the authorization header to this object
33 HttpWebRequest request = context.GetHttpWebRequest();
34
35 // Get the request body, that is also used to compute the signature
36 //string requestBody = context.GetRequestBodyAsText();
37
38 string oauth_signature_method = "HMAC-SHA1";
39
40
41 string oauth_nonce = Convert.ToBase64String(new ASCIIEncoding().GetBytes(DateTime.UtcNow.Ticks.ToString()))
42 .Replace("=", "").Replace("+", "");
43
44 TimeSpan ts = DateTime.UtcNow - new DateTime(1970, 1, 1, 0, 0, 0, 0);
45
46 string oauth_timestamp = Convert.ToInt64(ts.TotalSeconds).ToString();
47
48 string oauth_version = "1.0";
49
50 //When building the signature string the params
51 //must be in alphabetical order.
52
53 SortedDictionary<string, string> sd = new SortedDictionary<string, string>();
54
55 // add all parameters specified in the body
56 /*
57 var parsedQueryString = HttpUtility.ParseQueryString(requestBody);
58 foreach (var name in parsedQueryString.AllKeys)
59 {
60 sd.Add(name, PercentEncode(parsedQueryString[name]));
61 }
62
63 // add all parameters specified in the querystring
64 parsedQueryString = HttpUtility.ParseQueryString(request.RequestUri.Query);
65 foreach (var name in parsedQueryString.AllKeys)
66 {
67 sd.Add(name, PercentEncode(parsedQueryString[name]));
68 }*/
69
70 string auth_callback = null;
71
72 sd.Add("oauth_version", oauth_version);
73 if (request.Headers["oauth_callback"] != null)
74 {
75 auth_callback = request.Headers["oauth_callback"];
76 sd.Add("oauth_callback", PercentEncode(auth_callback));
77 request.Headers.Remove("oauth_callback");
78 }
79 sd.Add("oauth_consumer_key", ssConsumerKey);
80 sd.Add("oauth_nonce", oauth_nonce);
81 sd.Add("oauth_signature_method", oauth_signature_method);
82 sd.Add("oauth_timestamp", oauth_timestamp);
83 if (!sd.ContainsKey("oauth_token"))
84 {
85 sd.Add("oauth_token", ssOAuthToken);
86 }
87
88
89 /*
90 * StringBuilder baseString = new StringBuilder(request.Method + "&" + PercentEncode(NormalizeUrl(request.RequestUri)) + "&");
91 foreach (var keyValuePair in sd)
92 {
93 // these & need to be UrlEncoded, but the previous ones do not!
94 baseString.Append(PercentEncode(string.Format("{0}={1}&", keyValuePair.Key, keyValuePair.Value)));
95 }
96 string signatureBaseString = baseString.ToString().Substring(0, baseString.Length - 3);
97 */
98 // Build the signing key
99
100 // Build the signing key
101 string signingKey =
102 PercentEncode(ssConsumerSecret) + "&" +
103 PercentEncode(ssOAuthTokenSecret);
104
105 string signatureString = ComputeSignature(signingKey, "");
106
107
108
109
110 /**************************************/
111 // Build the authorization header
112 string authorizationHeaderParams = "OAuth ";
113 authorizationHeaderParams += "oauth_nonce=" + "\"" + PercentEncode(oauth_nonce) + "\", ";
114
115 if (auth_callback != null)
116 {
117 authorizationHeaderParams += "oauth_callback=" + "\"" + PercentEncode(auth_callback) + "\", ";
118 }
119
120 if (sd.ContainsKey("oauth_verifier"))
121 {
122 authorizationHeaderParams += "oauth_verifier=" + "\"" + PercentEncode(sd["oauth_verifier"]) + "\", ";
123 }
124
125 authorizationHeaderParams += "oauth_signature_method=" + "\"" + PercentEncode(oauth_signature_method) + "\", ";
126
127 authorizationHeaderParams += "oauth_timestamp=" + "\"" + PercentEncode(oauth_timestamp) + "\", ";
128
129 authorizationHeaderParams += "oauth_consumer_key=" + "\"" + PercentEncode(ssConsumerKey) + "\", ";
130
131 authorizationHeaderParams += "oauth_token=" + "\"" + PercentEncode(ssOAuthToken) + "\", ";
132
133 authorizationHeaderParams += "oauth_signature=" + "\"" + PercentEncode(signatureString) + "\", ";
134
135 authorizationHeaderParams += "oauth_version=" + "\"" + PercentEncode(oauth_version) + "\"";
136
137 request.Headers.Remove("Authorization");
138 request.Headers.Add("Authorization", authorizationHeaderParams);
139 } // MssSignUploadRequest
140
141
142 /// <summary>
143 ///
144 /// </summary>
145 /// <param name="ssUri"></param>
146 /// <param name="ssEscapedUri"></param>
147 public void MssPercentEncode(string ssUri, out string ssEscapedUri) {
148 ssEscapedUri = PercentEncode(ssUri);
149 } // MssPercentEncode
150
151 /// <summary>
152 ///
153 /// </summary>
154 /// <param name="ssText"></param>
155 /// <param name="ssBase64Text"></param>
156 public void MssBase64(string ssText, out string ssBase64Text)
157 {
158 var plainTextBytes = System.Text.Encoding.UTF8.GetBytes(ssText);
159 ssBase64Text = System.Convert.ToBase64String(plainTextBytes);
160 } // MssBase64
161
162 /// <summary>
163 /// Method to be used inside a REST OnBeforeRequestAdvanced callback to sign the request according with the Twitter API specification.
164 /// </summary>
165 /// <param name="ssConsumerKey"></param>
166 /// <param name="ssConsumerSecret"></param>
167 /// <param name="ssOAuthToken"></param>
168 /// <param name="ssOAuthTokenSecret"></param>
169 public void MssSignRequest(string ssConsumerKey, string ssConsumerSecret, string ssOAuthToken, string ssOAuthTokenSecret)
170 {
171 // This code is based on
172 // http://garyshortblog.wordpress.com/2011/02/11/a-twitter-oauth-example-in-c/
173
174 RestRequest context = RestRequest.GetCurrent();
175
176 // Get the current http request, we will be adding the authorization header to this object
177 HttpWebRequest request = context.GetHttpWebRequest();
178
179 // Get the request body, that is also used to compute the signature
180 string requestBody = context.GetRequestBodyAsText();
181
182 string oauth_signature_method = "HMAC-SHA1";
183
184
185 string oauth_nonce = Convert.ToBase64String(new ASCIIEncoding().GetBytes(DateTime.UtcNow.Ticks.ToString()))
186 .Replace("=", "").Replace("+", "");
187
188 TimeSpan ts = DateTime.UtcNow - new DateTime(1970, 1, 1, 0, 0, 0, 0);
189
190 string oauth_timestamp = Convert.ToInt64(ts.TotalSeconds).ToString();
191
192 string oauth_version = "1.0";
193
194 //When building the signature string the params
195 //must be in alphabetical order.
196
197 SortedDictionary<string, string> sd = new SortedDictionary<string, string>();
198
199 // add all parameters specified in the body
200 var parsedQueryString = HttpUtility.ParseQueryString(requestBody);
201 foreach (var name in parsedQueryString.AllKeys)
202 {
203 sd.Add(name, PercentEncode(parsedQueryString[name]));
204 }
205
206 // add all parameters specified in the querystring
207 parsedQueryString = HttpUtility.ParseQueryString(request.RequestUri.Query);
208 foreach (var name in parsedQueryString.AllKeys)
209 {
210 sd.Add(name, PercentEncode(parsedQueryString[name]));
211 }
212
213 string auth_callback = null;
214
215 sd.Add("oauth_version", oauth_version);
216 if (request.Headers["oauth_callback"] != null)
217 {
218 auth_callback = request.Headers["oauth_callback"];
219 sd.Add("oauth_callback", PercentEncode(auth_callback));
220 request.Headers.Remove("oauth_callback");
221 }
222 sd.Add("oauth_consumer_key", ssConsumerKey);
223 sd.Add("oauth_nonce", oauth_nonce);
224 sd.Add("oauth_signature_method", oauth_signature_method);
225 sd.Add("oauth_timestamp", oauth_timestamp);
226 if (!sd.ContainsKey("oauth_token"))
227 {
228 sd.Add("oauth_token", ssOAuthToken);
229 }
230
231 StringBuilder baseString = new StringBuilder(request.Method + "&" + PercentEncode(NormalizeUrl(request.RequestUri)) + "&");
232 foreach (var keyValuePair in sd)
233 {
234 // these & need to be UrlEncoded, but the previous ones do not!
235 baseString.Append(PercentEncode(string.Format("{0}={1}&", keyValuePair.Key, keyValuePair.Value)));
236 }
237 string signatureBaseString = baseString.ToString().Substring(0, baseString.Length - 3);
238
239 // Build the signing key
240 string signingKey =
241 PercentEncode(ssConsumerSecret) + "&" +
242 PercentEncode(ssOAuthTokenSecret);
243
244 string signatureString = ComputeSignature(signingKey, signatureBaseString);
245
246 // Build the authorization header
247 string authorizationHeaderParams = "OAuth ";
248 authorizationHeaderParams += "oauth_nonce=" + "\"" + PercentEncode(oauth_nonce) + "\", ";
249
250 if (auth_callback != null)
251 {
252 authorizationHeaderParams += "oauth_callback=" + "\"" + PercentEncode(auth_callback) + "\", ";
253 }
254
255 if (sd.ContainsKey("oauth_verifier"))
256 {
257 authorizationHeaderParams += "oauth_verifier=" + "\"" + PercentEncode(sd["oauth_verifier"]) + "\", ";
258 }
259
260 authorizationHeaderParams += "oauth_signature_method=" + "\"" + PercentEncode(oauth_signature_method) + "\", ";
261
262 authorizationHeaderParams += "oauth_timestamp=" + "\"" + PercentEncode(oauth_timestamp) + "\", ";
263
264 authorizationHeaderParams += "oauth_consumer_key=" + "\"" + PercentEncode(ssConsumerKey) + "\", ";
265
266 authorizationHeaderParams += "oauth_token=" + "\"" + PercentEncode(ssOAuthToken) + "\", ";
267
268 authorizationHeaderParams += "oauth_signature=" + "\"" + PercentEncode(signatureString) + "\", ";
269
270 authorizationHeaderParams += "oauth_version=" + "\"" + PercentEncode(oauth_version) + "\"";
271
272 request.Headers.Remove("Authorization");
273 request.Headers.Add("Authorization", authorizationHeaderParams);
274
275 } // MssSignRequest
276
277
278 private static string ComputeSignature(string signingKey, string text)
279 {
280 // Sign the request
281 HMACSHA1 hasher = new HMACSHA1(new ASCIIEncoding().GetBytes(signingKey));
282 return Convert.ToBase64String(hasher.ComputeHash(new ASCIIEncoding().GetBytes(text)));
283 }
284
285 private static string NormalizeUrl(Uri url)
286 {
287 string normalizedUrl = string.Format(System.Globalization.CultureInfo.InvariantCulture, "{0}://{1}", url.Scheme, url.Host);
288 if (!((url.Scheme == "http" && url.Port == 80) || (url.Scheme == "https" && url.Port == 443)))
289 {
290 normalizedUrl += ":" + url.Port;
291 }
292
293 normalizedUrl += url.AbsolutePath;
294 return normalizedUrl;
295 }
296
297 /// <summary>
298 /// The set of characters that are unreserved in RFC 2396 but are NOT unreserved in RFC 3986.
299 /// </summary>
300 private static readonly string[] UriRfc3986CharsToEscape = new[] { "!", "*", "'", "(", ")" };
301
302 /// <summary>
303 /// Escapes a string according to the URI data string rules given in RFC 3986.
304 /// </summary>
305 /// <param name="value">The value to escape.</param>
306 /// <returns>The escaped value.</returns>
307 /// <remarks>
308 /// The <see cref="PercentEncode"/> method is <i>supposed</i> to take on
309 /// RFC 3986 behavior if certain elements are present in a .config file. Even if this
310 /// actually worked (which in my experiments it <i>doesn't</i>), we can't rely on every
311 /// host actually having this configuration element present.
312 /// </remarks>
313 private static string PercentEncode(string value)
314 {
315 // Start with RFC 2396 escaping by calling the .NET method to do the work.
316 // This MAY sometimes exhibit RFC 3986 behavior (according to the documentation).
317 // If it does, the escaping we do that follows it will be a no-op since the
318 // characters we search for to replace can't possibly exist in the string.
319 StringBuilder escaped = new StringBuilder();
320 int limit = 2000;
321 int loops = value.Length / limit;
322
323 for (int i = 0; i <= loops; i++)
324 {
325 if (i < loops)
326 {
327 escaped.Append(Uri.EscapeDataString(value.Substring(limit * i, limit)));
328 }
329 else
330 {
331 escaped.Append(Uri.EscapeDataString(value.Substring(limit * i)));
332 }
333 }
334
335 // Upgrade the escaping to RFC 3986, if necessary.
336 for (int i = 0; i < UriRfc3986CharsToEscape.Length; i++)
337 {
338 escaped.Replace(UriRfc3986CharsToEscape[i], Uri.HexEscape(UriRfc3986CharsToEscape[i][0]));
339 }
340
341 // Return the fully-RFC3986-escaped string.
342 return escaped.ToString();
343 }
344
345 } // CssTwitterSignature
346
347} // OutSystems.NssTwitterSignature