Ok, I finally found the answer myself. Here are a few things you have to keep in mind when signing requests for oauth.
- signature must be encrypted with HMAC-SHA1 using the basestring(see nr.2) as text to be encrypted and the clientsecret and the token_secret (if you have one) (see nr. 3)
- basestring = [HttpMethod]&[FlickrAPIEndpoint]&[Parameters]
- Key for request oauth_token = [ApiSecret]&
(or key = [ApiSecret]&[Oauth_token_secret] for request access_token)
IMPORTANT: FlickrAPIEndPoint and Parameters must be UrlEncoded (in two parts!)
I have used a separate class for the encoding because the HttpUtility.UrlEncode method uses lowercase encoding while the uppercase encoding should be used.
IMPORTANT: Parameters must be in alphabetical order!
Here's the code for a console application that will create a signed request for a request token and a request token secret.
class Program
{
private static string Secret = "9dcc18a121e9a02e";
private static string ConsumerKey = "3aafc63ec6b05f3f9a9ff3a1c35ce541";
private static string request_token = "";
static void Main(string[] args)
{
string requestString = "http://www.flickr.com/services/oauth/request_token";
//generate a random nonce and a timestamp
Random rand = new Random();
string nonce = rand.Next(999999).ToString();
string timestamp = GetTimestamp();
//create the parameter string in alphabetical order
string parameters = "oauth_callback=" + UrlHelper.Encode("http://www.example.com");
parameters += "&oauth_consumer_key=" + ConsumerKey;
parameters += "&oauth_nonce=" + nonce;
parameters += "&oauth_signature_method=HMAC-SHA1";
parameters += "&oauth_timestamp=" + timestamp;
parameters += "&oauth_version=1.0";
//generate a signature base on the current requeststring and parameters
string signature = generateSignature("GET", requestString, parameters);
//add the parameters and signature to the requeststring
string url = requestString + "?" + parameters + "&oauth_signature=" + signature;
//test the request
WebClient web = new WebClient();
string result = web.DownloadString(url);
Console.WriteLine("Flickr Response: ");
Console.WriteLine(result); //contains the oauth_token and the oauth_token_secret
Console.ReadKey(true);
}
private static string generateSignature(string httpMethod, string ApiEndpoint, string parameters)
{
//url encode the API endpoint and the parameters
//IMPORTANT NOTE:
//encoded text should contain uppercase characters: '=' => %3D !!! (not %3d )
//the HtmlUtility.UrlEncode creates lowercase encoded tags!
//Here I use a urlencode class by Ian Hopkins
string encodedUrl = UrlHelper.Encode(ApiEndpoint);
string encodedParameters = UrlHelper.Encode(parameters);
//generate the basestring
string basestring = httpMethod + "&" + encodedUrl + "&";
parameters = UrlHelper.Encode(parameters);
basestring = basestring + parameters;
//hmac-sha1 encryption:
System.Text.ASCIIEncoding encoding = new System.Text.ASCIIEncoding();
//create key (request_token can be an empty string)
string key = Secret + "&" + request_token;
byte[] keyByte = encoding.GetBytes(key);
//create message to encrypt
byte[] messageBytes = encoding.GetBytes(basestring);
//encrypt message using hmac-sha1 with the provided key
HMACSHA1 hmacsha1 = new HMACSHA1(keyByte);
byte[] hashmessage = hmacsha1.ComputeHash(messageBytes);
//signature is the base64 format for the genarated hmac-sha1 hash
string signature = System.Convert.ToBase64String(hashmessage);
//encode the signature to make it url safe and return the encoded url
return UrlHelper.Encode(signature);
}
//generator of unix epoch time
public static String GetTimestamp()
{
int epoch = (int)(DateTime.UtcNow - new DateTime(1970, 1, 1)).TotalSeconds;
return epoch.ToString();
}
}
UrlHelper class by Ian Hopkins used for the url encoding
/// <summary>
/// URL encoding class. Note: use at your own risk.
/// Written by: Ian Hopkins (http://www.lucidhelix.com)
/// Date: 2008-Dec-23
/// (Ported to C# by t3rse (http://www.t3rse.com))
/// </summary>
public class UrlHelper
{
public static string Encode(string str)
{
var charClass = String.Format("0-9a-zA-Z{0}", Regex.Escape("-_.!~*'()"));
return Regex.Replace(str,
String.Format("[^{0}]", charClass),
new MatchEvaluator(EncodeEvaluator));
}
public static string EncodeEvaluator(Match match)
{
return (match.Value == " ") ? "+" : String.Format("%{0:X2}", Convert.ToInt32(match.Value[0]));
}
public static string DecodeEvaluator(Match match)
{
return Convert.ToChar(int.Parse(match.Value.Substring(1), System.Globalization.NumberStyles.HexNumber)).ToString();
}
public static string Decode(string str)
{
return Regex.Replace(str.Replace('+', ' '), "%[0-9a-zA-Z][0-9a-zA-Z]", new MatchEvaluator(DecodeEvaluator));
}
}
与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…