I'm having trouble getting a call to the Twitter API working.
I'm having to do this as the package i was using has got an issue and the fix isn't compatible with my project.
I want to retrieve a list of 'recent' tweets but I keep getting a 401. I have a feeling it is because of the way I'm creating my signature but I'm not certain.
My request URL is:
https://api.twitter.com/1.1/search/tweets.json?result_type=recent&count=1&q=#hashtag1 OR #hashtag2 OR #hashtag3
I have the following auth info:
Dictionary<string, object> _parameters = new Dictionary<string, object>();
_parameters.Add("oauth_version", "1.0");
_parameters.Add("oauth_nonce", "[VALUE]");
_parameters.Add("oauth_timestamp", [TIMESTAMP]);
_parameters.Add("oauth_signature_method", "HMAC-SHA1");
_parameters.Add("oauth_consumer_key", "[VALUE]");
_parameters.Add("oauth_consumer_secret", "[VALUE]");
_parameters.Add("oauth_token", "[VALUE]");
_parameters.Add("oauth_token_secret", "[VALUE]");
My 'create signature' method is as follows. It uses the full URL with search parameters to construct it:
private static string GenerateSignature(Dictionary<string,object> parameters, Uri requestUri, string consumerSecret, string accessTokenSecret)
{
IEnumerable<KeyValuePair<string, object>> nonSecretParameters;
nonSecretParameters = (from p in parameters
where (!SecretParameters.Contains(p.Key))
select p);
Uri urlForSigning = requestUri;
string signatureBaseString = string.Format(
CultureInfo.InvariantCulture,
"{0}&{1}&{2}",
"GET",
UrlEncode(NormalizeUrl(urlForSigning)),
UrlEncode(nonSecretParameters));
string key = string.Format(
CultureInfo.InvariantCulture,
"{0}&{1}",
UrlEncode(consumerSecret),
UrlEncode(accessTokenSecret));
HMACSHA1 hmacsha1 = new HMACSHA1(Encoding.UTF8.GetBytes(key));
byte[] signatureBytes = hmacsha1.ComputeHash(Encoding.UTF8.GetBytes(signatureBaseString));
return Convert.ToBase64String(signatureBytes);
}
private static string UrlEncode(string value)
{
if (string.IsNullOrEmpty(value))
{
return string.Empty;
}
value = Uri.EscapeDataString(value);
value = Regex.Replace(value, "(%[0-9a-f][0-9a-f])", c => c.Value.ToUpper());
value = value
.Replace("(", "%28")
.Replace(")", "%29")
.Replace("$", "%24")
.Replace("!", "%21")
.Replace("*", "%2A")
.Replace("'", "%27");
value = value.Replace("%7E", "~");
return value;
}
private static string NormalizeUrl(Uri url)
{
string normalizedUrl = string.Format(CultureInfo.InvariantCulture, "{0}://{1}", url.Scheme, url.Host);
if (!((url.Scheme == "http" && url.Port == 80) || (url.Scheme == "https" && url.Port == 443)))
{
normalizedUrl += ":" + url.Port;
}
normalizedUrl += url.PathAndQuery + url.Fragment.Replace("%20", " ");
return normalizedUrl;
}
Once I have the signature, I generate my Authorisation header which is something like this:
OAuth oauth_consumer_key="[VALUE]",oauth_nonce="[VALUE]",oauth_signature_method="HMAC-SHA1",oauth_timestamp="1565261231",oauth_token="[VALUE]",oauth_version="1.0",oauth_signature="[VALUE]"
Then I append the relevant auth params to the URL and make the request to the Twitter API. The final URL is something like this:
https://api.twitter.com/1.1/search/tweets.json?result_type=recent&count=1&q=%23hashtag1 OR %23hashtag2 OR %23hashtag3
Related
I have written the following method to fetch all orders from Amazon. I'm using the RestSharp library to communicate with the API. Everytime I execute the request I get the following error:
<?xml version="1.0"?>
<ErrorResponse xmlns="https://mws.amazonservices.com/Orders/2013-09-01">
<Error>
<Type>Sender</Type>
<Code>SignatureDoesNotMatch</Code>
<Message>The request signature we calculated does not match the signature you provided. Check your AWS Secret Access Key and signing method. Consult the service documentation for details.</Message>
</Error>
<RequestID>9f03f5b0-e4e4-4766-a554-00ff970b6b8c</RequestID>
</ErrorResponse>
That's very strange, because on the Amazon Scratchpad (https://mws.amazonservices.de/scratchpad/index.html) it is working and I also get the same signature (when I use the timestamp calculated on the Scratchpad) in my c# app.
C# Class
public async void FetchOrders()
{
RestClient client = new RestClient("https://mws.amazonservices.de");
client.DefaultParameters.Clear();
client.ClearHandlers();
Dictionary<string, string> parameters = new Dictionary<string, string>();
parameters.Add("AWSAccessKeyId", "xxxxxxxxxx");
parameters.Add("Action", "ListOrders");
parameters.Add("CreatedAfter", "2018-01-01T11:34:00Z");
parameters.Add("MarketplaceId.Id.1", "A1PA6795UKMFR9");
parameters.Add("SellerId", "xxxxxxxxx");
parameters.Add("SignatureVersion", "2");
parameters.Add("Timestamp", DateTime.UtcNow.ToString("s") + "Z");
parameters.Add("Version", "2013-09-01");
RestRequest request = new RestRequest("Orders/2013-09-01/", Method.POST);
string signature = AmzLibrary.SignParameters(parameters, "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx");
request.AddParameter("Signature", signature);
foreach (KeyValuePair<string, string> keyValuePair in parameters)
{
request.AddParameter(keyValuePair.Key, keyValuePair.Value);
}
IRestResponse result = await client.ExecuteTaskAsync(request);
}
public static class AmzLibrary
{
public static string GetParametersAsString(IDictionary<String, String> parameters)
{
StringBuilder data = new StringBuilder();
foreach (String key in (IEnumerable<String>)parameters.Keys)
{
String value = parameters[key];
if (value != null)
{
data.Append(key);
data.Append('=');
data.Append(UrlEncode(value, false));
data.Append('&');
}
}
String result = data.ToString();
return result.Remove(result.Length - 1);
}
public static String SignParameters(IDictionary<String, String> parameters, String key)
{
String signatureVersion = parameters["SignatureVersion"];
KeyedHashAlgorithm algorithm = new HMACSHA1();
String stringToSign = null;
if ("2".Equals(signatureVersion))
{
String signatureMethod = "HmacSHA256";
algorithm = KeyedHashAlgorithm.Create(signatureMethod.ToUpper());
parameters.Add("SignatureMethod", signatureMethod);
stringToSign = CalculateStringToSignV2(parameters);
}
else
{
throw new Exception("Invalid Signature Version specified");
}
return Sign(stringToSign, key, algorithm);
}
private static String CalculateStringToSignV2(IDictionary<String, String> parameters)
{
StringBuilder data = new StringBuilder();
IDictionary<String, String> sorted =
new SortedDictionary<String, String>(parameters, StringComparer.Ordinal);
data.Append("POST");
data.Append("\n");
Uri endpoint = new Uri("https://mws.amazonservices.de/Orders/2013-09-01");
data.Append(endpoint.Host);
if (endpoint.Port != 443 && endpoint.Port != 80)
{
data.Append(":")
.Append(endpoint.Port);
}
data.Append("\n");
String uri = endpoint.AbsolutePath;
if (uri == null || uri.Length == 0)
{
uri = "/";
}
data.Append(UrlEncode(uri, true));
data.Append("\n");
foreach (KeyValuePair<String, String> pair in sorted)
{
if (pair.Value != null)
{
data.Append(UrlEncode(pair.Key, false));
data.Append("=");
data.Append(UrlEncode(pair.Value, false));
data.Append("&");
}
}
String result = data.ToString();
return result.Remove(result.Length - 1);
}
private static String UrlEncode(String data, bool path)
{
StringBuilder encoded = new StringBuilder();
String unreservedChars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-_.~" + (path ? "/" : "");
foreach (char symbol in System.Text.Encoding.UTF8.GetBytes(data))
{
if (unreservedChars.IndexOf(symbol) != -1)
{
encoded.Append(symbol);
}
else
{
encoded.Append("%" + String.Format("{0:X2}", (int)symbol));
}
}
return encoded.ToString();
}
private static String Sign(String data, String key, KeyedHashAlgorithm algorithm)
{
Encoding encoding = new UTF8Encoding();
algorithm.Key = encoding.GetBytes(key);
return Convert.ToBase64String(algorithm.ComputeHash(
encoding.GetBytes(data.ToCharArray())));
}
}
The problem was the "/" at the end of the Request. Remove it and everything works.
wrong
RestRequest request = new RestRequest("Orders/2013-09-01/", Method.POST);
right
RestRequest request = new RestRequest("Orders/2013-09-01", Method.POST);
hello I am trying to pass a variable number of parameters inside a HttpWebRequest url. My approach so far is this-
public static async Task<ObservableCollection<Anime>> RequestsAnime(string accessToken, int page = 0, params string[] optionals) // All Anime
{
JsonWebClient client = new JsonWebClient();
string baseUri = "https://anilist.co/api/browse/anime?access_token=" + accessToken;
string completeUri = "";
string parameters = "";
if (optionals != null)
{
parameters = CollectionUtils.ToStringFromArray(optionals);
completeUri = baseUri + parameters;
}
if (page != 0)
{
completeUri = baseUri + "&page=" + page;
}
HttpWebRequest reqForToken = HttpWebRequest.CreateHttp(completeUri);
reqForToken.Method = "GET";
var respToken = await client.DoRequestJsonAsync<ObservableCollection<Anime>>(reqForToken);
return respToken;
}
ToStringFromArray function
public static string ToStringFromArray(string[] arrayString)
{
string result = string.Join("", arrayString);
return result;
}
RequestsAnime Calling
string[] ds = new string[] { "&status", "=active", "&season","=fall" };
var nm = await Requests.CreateMultipleTasksAsync(ds);
CreateMultipleTaskAsync calling
public static async Task<ObservableCollection<Anime>> CreateMultipleTasksAsync(int page=0 ,params string[] optionals)
{
Task<Auth> download1 = RequestsAuth();
Auth length1 = await download1;
Task<ObservableCollection<Anime>> download2 = RequestsAnime(download1.Result.Access_token,page, optionals);
var animeList = await download2;
return animeList;
}
I know its a clumsy way. I need to know a better solution. My objectives are-
Pass variable amount of parameters inside the URL
Append the parameters with "&" and "="
I would suggest you put all your query string parameters in a Dictionary:
var parameters = new Dictionary<string, string>
{
{ "status", "active" },
{ "season", "fall" }
};
You can then call the following method generate a valid query string (notice the calls to Uri.EscapeDataString:
string CreateQueryString(Dictionary<string, string> parameters)
{
return String.Join("&", parameters
.Select(param => String.Format("{0}={1}",
Uri.EscapeDataString(param.Key),
Uri.EscapeDataString(param.Value)))
.ToArray());
}
Just append the queryString to you baseUri:
completeUri = baseUri + "&" + CreateQueryString(parameters);
I'am new to Instagram API, I have a client that needs to display the latest media on his website, but am doing the test under my personal Instagram account.
Under Security Tab both [Disable implicit OAuth] & [Enforce signed requests] are checked.
The Endpoint am trying to call is: [users/{user-id}/media/recent]
Signature Generation Method:
string GenerateSignature(string endpoint,
Dictionary<string, string> parameters, string secret) {
StringBuilder message = new StringBuilder(endpoint);
foreach (var param in parameters.OrderBy(p => p.Key))
{
message.AppendFormat("|{0}={1}", param.Key, param.Value);
}
return HMACSHA256_Hash(secret, message.ToString());
}
string HMACSHA256_Hash(string secret, string message)
{
// Create a HMAC-SHA256 digest of the message using the secret key
HMACSHA256 hmac = new HMACSHA256(Encoding.UTF8.GetBytes(secret));
byte[] digest = hmac.ComputeHash(Encoding.UTF8.GetBytes(message.ToString()));
// Return the digest as a hexstring to be used as a signature for the request
return ByteArrayToString(digest);
}
string ByteArrayToString(byte[] array)
{
// Convert the bytes in the array to a lower-case hexstring
return array.Aggregate(new StringBuilder(), (sb, b) => sb.Append(b.ToString("x2"))).ToString();
}
Get Recent Media:
protected void GetUserDetails()
{
string instagram_Username = "";
string instagram_UserID = "";
string instagram_AccessToken = "";
string instagram_ClientID = "";
string instagram_ClientSecret = "";
int count = 1;
string url = String.Format(#"https://api.instagram.com/v1/users/{0}/media/recent?client_id={1}&count={2}", instagram_UserID, instagram_ClientID, count);
string endPoint = String.Format("/users/{0}/media/recent", instagram_UserID);
Dictionary<string, string> endPointParameters = new Dictionary<string, string>();
endPointParameters.Add("client_id", instagram_ClientID);
endPointParameters.Add("count", count.ToString());
string signature = GenerateSignature(endPoint, endPointParameters, instagram_AccessToken);
url = url + "&sig=" + signature;
}
URL being called: https://api.instagram.com/v1/users/{user-id}/media/recent?client_id={cliend_id}&count=1&sig=7c024d67c1000106686c412778dc3534614d43822f9e13554418c3d3d5386872
Response: {"code": 403, "error_type": "OAuthForbiddenException", "error_message": "Invalid signed-request: Signature does not match"}
IN ADDITION; can I please know if access token will expire Or I can use same one for all my calls, noting that the call will be done each time the home page of the website is rendered :-)
I appreciate the help ! many thanks
I've missed that the secret should be the client Secret.
string signature = GenerateSignature(endPoint, endPointParameters, instagram_ClientSecret);
I am having an issue with OAuth and Facebook. I am using MVC4 standard OAuth login. I am not having the issue locally but on the server this is proving to be a problem.
If I paste the following URL into the browser it works OK:
http://localhost:46260/Account/ExternalLoginCallback?ReturnUrl=%2FDashboard&__provider__=FacebookPro&__sid__=1234somesid456 // this is autogenerated
When I change the URL for the app in facebook to the current domain and paste this url in, I get re-directed to the Unsuccessful login page:
http://freersvp.mytakeawaysite.com:80/Account/ExternalLoginCallback?ReturnUrl=%2FDashboard&__provider__=Facebook+Pro&__sid__=1234someid456 // note this is autogenerated
N.B The above two url's are the redirect uri
The below URL is what is requested and is causing the exception:
URL
https://graph.facebook.com/oauth/access_token?client_id=52*********37&redirect_uri=http%3a%2f%2ffreersvp.mytakeawaysite.com%3a80%2fAccount%2fExternalLoginCallback%3fReturnUrl%3d%252FDashboard%26__provider__%3dFacebook%2bPro%26__sid__%3d3c92eb7e84304afc931ef0ea7b62f56a&client_secret=2123***********4256&code=AQAQIJsj-ondldllVYKdpxJaZouqrlg9sjTcfUxyWhAw8MXbD2DvsOSujg2m7E3s3cvNusCI0ZZoJAuGgu_FLkPyjYMQAkTWDVyHTcAoJD-tezyXgn0vhoFzX3FmuRBHYpyJEM-dk0KgF5ugsTHo9yGjBjrcfMDUGu9IxkKQ36k3gMrwocM1_l5t342Q2kIOHdt8pPcyrs--NzgNyZv48vSq7jkZwuQ95xRjUHG5J-ptcgq0l2BlqjzHDDuvIFH23lpMWHzzqdejdj5ejukz7t_Fnhx-mrpVdcRYhP3JeZ2UOTjAyKQmUB3rInooECcjq4c
Exception
{
"error": {
"message": "Error validating verification code. Please make sure your redirect_uri is identical to the one you used in the OAuth dialog request",
"type": "OAuthException",
"code": 100
}
}
The string token does come back with null in the GetUserData function in the below code:
I am using the FacebookScopedClient:
public class FacebookScopedClient : IAuthenticationClient
{
private string appId;
private string appSecret;
private string scope;
private const string baseUrl = "https://www.facebook.com/dialog/oauth?client_id=";
public const string graphApiToken = "https://graph.facebook.com/oauth/access_token?";
public const string graphApiMe = "https://graph.facebook.com/me?";
private static string GetHTML(string URL)
{
string connectionString = URL;
try
{
System.Net.HttpWebRequest myRequest = (HttpWebRequest)WebRequest.Create(connectionString);
myRequest.Credentials = CredentialCache.DefaultCredentials;
//// Get the response
WebResponse webResponse = myRequest.GetResponse();
Stream respStream = webResponse.GetResponseStream();
////
StreamReader ioStream = new StreamReader(respStream);
string pageContent = ioStream.ReadToEnd();
//// Close streams
ioStream.Close();
respStream.Close();
return pageContent;
}
catch(Exception ex)
{
}
return null;
}
private IDictionary<string, string> GetUserData(string accessCode, string redirectURI)
{
SessionControl ctl = new SessionControl();
ctl.SaveParam("redirecturi", redirectURI, -3);
ctl.Dispose();
string token = GetHTML(graphApiToken + "client_id=" + appId + "&redirect_uri=" + HttpUtility.UrlEncode(redirectURI) + "&client_secret=" + appSecret + "&code=" + accessCode);
if(token == null || token == "")
{
return null;
}
string access_token = token.Substring(token.IndexOf("access_token="), token.IndexOf("&"));
string data = GetHTML(graphApiMe + "fields=id,name,email,username,gender,link&" + access_token);
try
{
}
catch { }
// this dictionary must contains
Dictionary<string, string> userData = JsonConvert.DeserializeObject<Dictionary<string, string>>(data);
userData.Add("accesstoken", access_token);
try
{
userData.Add("id", userData["id"]);
}
catch { }
return userData;
}
public FacebookScopedClient(string appId, string appSecret, string scope)
{
this.appId = appId;
this.appSecret = appSecret;
this.scope = scope;
}
public string ProviderName
{
get { return "FacebookPro"; }
}
public void RequestAuthentication(System.Web.HttpContextBase context, Uri returnUrl)
{
string url = baseUrl + appId + "&redirect_uri=" + HttpUtility.UrlEncode(returnUrl.ToString()) + "&scope=" + scope;
context.Response.Redirect(url);
}
public AuthenticationResult VerifyAuthentication(System.Web.HttpContextBase context)
{
string code = context.Request.QueryString["code"];
string rawUrl = context.Request.Url.OriginalString;
//From this we need to remove code portion
rawUrl = Regex.Replace(rawUrl, "&code=[^&]*", "");
IDictionary<string, string> userData = GetUserData(code, rawUrl);
if(userData == null)
return new AuthenticationResult(false, ProviderName, null, null, null);
string id = userData["id"];
string username = userData["email"];
if(username == null || username == "")
{
username = userData["username"];
}
//userData.Remove("id");
userData.Remove("username");
AuthenticationResult result = new AuthenticationResult(true, ProviderName, id, username, userData);
return result;
}
}
after running your posted url that's causing the error through a url decoder the issue lies in for some reason your url encoding the entire query string and not just the url.
you will notice in that url a bunch of %26 items those are url encoded & and that's what is throwing your error. the Facebook parser is seeing %26 instead of & and treating it as one single parameter.
the & separates url query string parameters when sending to a page. Without the full code I can't tell you where to look but some where in your code your completely encoding the entire query string and need to find that piece of code and only encode the embedded urls.
ok after reading over things maybe try this theory.
I think your code is receiving this stuff from Facebook, url encoded, and then your system is re-encoding it. try taking anything received and first url decode it, manipulate it and then re-encode things as needed.
hope this helps
Try it with sandbox mode off within facebook app.
Noticing your URL's query string, I found an answer from Stackoverflow. Please see if it solves your issue:
https://stackoverflow.com/a/16699058/2005136
Steve S posted as a response:
"In our case, we were doing something unusual (so this might not be relevant to your case). Our redirect_uri was a URL with another URL embedded as an encoded path element. The URL-within-a-URL, doubly-encoded when passed to FB, had started causing problems with the Facebook API servers.
We resolved this by changing the encoding of the nested URL to a long hex number rather than % encoding, so all Facebook servers see is a simple redirect_uri containing some hex within the path, unaffected by normal URL encoding/decoding."
This is what I have so far, but it isn't working as I don't understand how DotNetOpenAuth is supposed to work. I only need it to sign the outcome with my key, but I am not having luck. Everything seems to point towards me needing to get the client to authorize my access, but I just need to get it signed as I don't need the user for this request.
Refer to http://developer.netflix.com/docs/read/Security , the section labeled "Netflix API Requests"
public class class1
{
private void Main()
{
string consumerKey = "<MyAPIKey>";
string consumerSecret = "<MyAPISharedSecret>";
var tokenManager = new InMemoryTokenManager(consumerKey, consumerSecret);
MessageReceivingEndpoint oauthEndpoint =
new MessageReceivingEndpoint(new Uri("http://api-public.netflix.com/catalog/titles/index"),
HttpDeliveryMethods.PostRequest);
WebConsumer consumer = new WebConsumer(
new ServiceProviderDescription
{
RequestTokenEndpoint = oauthEndpoint,
UserAuthorizationEndpoint = oauthEndpoint,
AccessTokenEndpoint = oauthEndpoint,
TamperProtectionElements =
new ITamperProtectionChannelBindingElement[] { new HmacSha1SigningBindingElement()},
},
tokenManager);
var result = consumer.Channel.Request(new AccessProtectedResourceRequest());
}
internal class InMemoryTokenManager : IConsumerTokenManager
{
private Dictionary<string, string> tokensAndSecrets = new Dictionary<string, string>();
public InMemoryTokenManager(string consumerKey, string consumerSecret)
{
if (string.IsNullOrEmpty(consumerKey))
{
throw new ArgumentNullException("consumerKey");
}
this.ConsumerKey = consumerKey;
this.ConsumerSecret = consumerSecret;
}
public string ConsumerKey { get; private set; }
public string ConsumerSecret { get; private set; }
public string GetTokenSecret(string token)
{
return this.tokensAndSecrets[token];
}
public void StoreNewRequestToken(UnauthorizedTokenRequest request, ITokenSecretContainingMessage response)
{
this.tokensAndSecrets[response.Token] = response.TokenSecret;
}
public void ExpireRequestTokenAndStoreNewAccessToken(string consumerKey, string requestToken, string accessToken,
string accessTokenSecret)
{
this.tokensAndSecrets.Remove(requestToken);
this.tokensAndSecrets[accessToken] = accessTokenSecret;
}
public TokenType GetTokenType(string token)
{
throw new NotImplementedException();
}
}
}
Your actual question should be something like 'Is it possible to use DotNetOpenAuth to sign requests with or without access token?", to answer that question I should say I don't know and even I can't find it out by reading DotNetOpenAuth codebase.
There is no single page of documentation available for DotNetOpenAuth and the codebase is so huge that you can't read it and understand what is supported by it or not.
I guess making non-authenticated request is not an issue as it is simply a query string parameter added to your request.
But to make signed requests you need to follow a simple process:
Collecting request parameters
Calculating signature
Making request(signed/protected)
Collecting request parameters
These are basically two categories of parameters, oauth specific parameters and Netflix API specific parameters.
Among the OAuth specific parameters is nonce, this is the code in which you can use to generate a nonce value:
public static string GenerateNonce()
{
byte[] bytes = new byte[32];
var first = Guid.NewGuid().ToByteArray();
var second = Guid.NewGuid().ToByteArray();
for (var i = 0; i < 16; i++)
bytes[i] = first[i];
for (var i = 16; i < 32; i++)
bytes[i] = second[i - 16];
var result = Convert.ToBase64String(bytes, Base64FormattingOptions.None);
result = new string(result.ToCharArray().Where(char.IsLetter).ToArray());
return result;
}
And another OAuth specific parameter is timestamp, this is the code in which you can use to calculate timestamp:
DateTime.UtcNow.Subtract(new DateTime(1970, 1, 1)).TotalSeconds
Other oauth specific parameters are easy to provision and no need to write a specific code for them.
API specific parameters are any value you add to query string or to the authorization headers(except the oauth_signature itself) or to the body request(if request content type is application/x-www-form-urlencoded).
Calculating signature
To make either a signed request or a protected signature you need to calculate a signature, which the process is almost the same, except the way that you create signing key:
Calculate signature base string
Calculate signing key
Creating the signature by signing the signature base string using signing key
To calculate signature base string you need to first concatenate all parameters into a string and the percent encode the whole string. This is the code which helps you doing percent encoding:
public static string Encode(string source)
{
Func<char, string> encodeCharacter = c => {
if ((c >= '0' && c <= '9') || (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || (c == '.' || c == '-' || c == '_' || c == '~'))
return new string(c, 1);
return EncodeCharacter(c);
};
return string.Concat(source.ToCharArray().Select(encodeCharacter));
}
Also you need to sort parameters in alphabetical order and be concatenated using '&'. Here is the code which you may have to write to do this:
public static string CalculateParameterString(KeyValuePair<string, string>[] parameters)
{
var q = from entry in parameters
let encodedkey = PercentEncode.Encode(entry.Key)
let encodedValue = PercentEncode.Encode(entry.Value)
let encodedEntry = encodedkey + "=" + encodedValue
orderby encodedEntry
select encodedEntry;
var result = string.Join("&", q.ToArray());
return result;
}
Lets call the above string 'parameters string'. Then to calculate signature base string all you need is to concatenate http verb of your request, your request's url and parameters string together using '&'. Also you need to percent encode them first. Here is the code which does that:
public static string CalcualteSignatureBaseString(string httpMethod, string baseUri, string parametersString)
{
return httpMethod.ToUpper() + "&" + PercentEncode.Encode(baseUri) + "&" + PercentEncode.Encode(parametersString);
}
Once you have created signature base string then you the next step is to calculate signing key.
If you just need to make a signed request, then you create signing key based on your consumer key(shared secret) only. This the signing key to be used to make a signed request.
During authorization process, if you just made a request token request and recieved a temporary oauth token, then your singing key is based on your consumer key and that oauth token. This is the signing key used to make request to get the access token.
If a user authorized your application and you have the relevant access token, then your signing key would be your consumer key and access token. This is the signing key to make a protected request.
This is the code that will generate the signing key:
public static string GetSigningKey(string ConsumerSecret, string OAuthTokenSecret = null)
{
return ConsumerSecret + "&" + (OAuthTokenSecret != null ? OAuthTokenSecret : "");
}
In your case, to make a signed request, you just need pass null value for OAuthTokenSecret parameter.
Ok, now you have a signature base string, all you need to do now is to sign using HMAC-SHA1 algorithm:
public static string Sign(string signatureBaseString, string signingKey)
{
var keyBytes = System.Text.Encoding.ASCII.GetBytes(signingKey);
using (var myhmacsha1 = new System.Security.Cryptography.HMACSHA1(keyBytes)) {
byte[] byteArray = System.Text.Encoding.ASCII.GetBytes(signatureBaseString);
var stream = new MemoryStream(byteArray);
var signedValue = myhmacsha1.ComputeHash(stream);
var result = Convert.ToBase64String(signedValue, Base64FormattingOptions.None);
return result;
}
}
To sum up this is the whole process for calculating signature:
public virtual string GetSignature(string consumerSecret, string tokenSecret, string uri, string method, params ParameterSet[] parameters)
{
var allParameters = parameters.SelectMany(p => p.ToList()).ToArray();
var parametersString = CalculateSignatureBaseString(allParameters);
var signatureBaseString = OAuth1aUtil.CalcualteSignatureBaseString(method, uri, parametersString);
var sigingKey = GetSigningKey(consumerSecret, tokenSecret);
var signature = Sign(signatureBaseString, sigingKey);
return signature;
}
Making request
Now you just need to make a valid http request and add the oauth parameters to the request as the 'Authorization' header.