public class OAuthSignatureGenerator
{
private const string OAuthConsumerKey = "oauth_consumer_key";
private const string OAuthNonce = "oauth_nonce";
private const string OAuthSignatureMethod = "oauth_signature_method";
private const string OAuthTimestamp = "oauth_timestamp";
private const string OAuthToken = "oauth_token";
private const string OAuthVersion = "oauth_version";
public enum SignatureMethod
{
Plaintext,
HmacSha1
}
public static string GenerateSignature(Uri uri, string httpMethod, string consumerKey, string consumerSecret, string nonce, string timestamp, SignatureMethod signatureMethod, string token = null, string tokenSecret = null, string version = null)
{
if (signatureMethod == SignatureMethod.Plaintext) return HttpUtility.UrlEncode(string.Format("{0}&{1}", consumerSecret, tokenSecret));
var parameters = ConvertQueryStringToListOfKvp(uri.Query);
AddParameter(parameters, OAuthConsumerKey, consumerKey);
AddParameter(parameters, OAuthNonce, nonce);
AddParameter(parameters, OAuthSignatureMethod, "HMAC-SHA1");
AddParameter(parameters, OAuthTimestamp, timestamp);
if (!string.IsNullOrEmpty(token)) AddParameter(parameters, OAuthToken, token);
if (!string.IsNullOrWhiteSpace(version)) AddParameter(parameters, OAuthVersion, version);
parameters.Sort((x, y) => x.Key == y.Key ? string.Compare(x.Value, y.Value) : string.Compare(x.Key, y.Key));
var normalizedUrl = string.Format("{0}://{1}{2}{3}", uri.Scheme, uri.Host, (uri.Scheme == "http" && uri.Port == 80) || (uri.Scheme == "https" && uri.Port == 443) ? null : ":" + uri.Port, uri.AbsolutePath);
var normalizedRequestParameters = string.Join(null, parameters.Select(x => "&" + x.Key + "=" + x.Value)).TrimStart('&');
var signatureData = string.Format("{0}&{1}&{2}", httpMethod.ToUpper(), UrlEncode(normalizedUrl), UrlEncode(normalizedRequestParameters));
return ComputeHash(consumerSecret, tokenSecret, signatureData);
}
private static List<KeyValuePair<string, string>> ConvertQueryStringToListOfKvp(string queryString)
{
return Regex.Matches(queryString, @"(\w+)=.*?(?:&|$)").Cast<Match>().Select(x => x.Value.TrimEnd('&').Split('=')).Select(x => new KeyValuePair<string, string>(x[0], x[1])).ToList();
}
private static void AddParameter(ICollection<KeyValuePair<string, string>> parameters, string key, string value)
{
parameters.Add(new KeyValuePair<string, string>(key, value));
}
private static string ComputeHash(string consumerSecret, string tokenSecret, string signatureData)
{
var hash = new HMACSHA1 { Key = Encoding.ASCII.GetBytes(string.Format("{0}&{1}", UrlEncode(consumerSecret), string.IsNullOrEmpty(tokenSecret) ? string.Empty : UrlEncode(tokenSecret))) }.ComputeHash(Encoding.ASCII.GetBytes(signatureData));
return Convert.ToBase64String(hash);
}
private static string UrlEncode(string value)
{
if (string.IsNullOrWhiteSpace(value)) return string.Empty;
const string unreservedChars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-_.~";
return value.Select(x => x.ToString()).Aggregate((x, y) => x + (unreservedChars.Contains(y.ToString()) ? y : HttpUtility.UrlEncode(y).ToUpper()));
}
}