I've asked this on the AWS Forums but getting plenty of views but no comments, I wonder if anyone here can shed any light on it?
Hi, I've been trying to write a simple c# console application to call the topsites service for two days now and still get issues with the signature generation.
I've tested using a java sample in the gallery and can successfully query using my accesskeyid and secret. I've then used my C# code to prove I can generate the same signature and my code will do so, however when I then craft a request and issue it against the api every single one returns a 403 status - signaturedoesnotmatch - please can someone help me find out what the issue is? I'm tearing my hair out with this.
C# Code:
using System;
using System.Collections.Generic;
using System.Configuration;
using System.Diagnostics;
using System.Linq;
using System.Net.Http;
using System.Security.Cryptography;
using System.Text;
using System.Threading.Tasks;
using System.Web;
namespace ConsoleApplication1
{
class Program
{
private static string baseUrl = ConfigurationManager.AppSettings["baseUrl"];
private static string accessKeyId = ConfigurationManager.AppSettings["accessKeyId"];
private static string accessKey = ConfigurationManager.AppSettings["accessKey"];
private static string serviceVersion = ConfigurationManager.AppSettings["serviceVersion"];
static void Main(string[] args)
{
HttpClient client = new HttpClient();
string requestParameters = "AWSAccessKeyId=" + accessKeyId + "&Action=TopSites&Count=10&CountryCode=&ResponseGroup=Country&SignatureMethod=HmacSHA256&SignatureVersion=2&Start=1001&Timestamp=" + Amazon.Util.AWSSDKUtils.FormattedCurrentTimestampISO8601;
var signature = generateSignature(requestParameters);
var url = "http://" + baseUrl + "?" + requestParameters + "&Signature=" + signature;
HttpResponseMessage message = client.GetAsync(url).Result;
Console.ReadKey();
}
private static string generateSignature(string queryParameters)
{
string stringToSign = "GET\n" + baseUrl + "\n/\n" + queryParameters;
var bytesToSign = Encoding.UTF8.GetBytes(stringToSign);
var secretKeyBytes = Encoding.UTF8.GetBytes(accessKey);
var hmacSha256 = new HMACSHA256(secretKeyBytes);
var hashBytes = hmacSha256.ComputeHash(bytesToSign);
var signature = System.Net.WebUtility.UrlEncode(Convert.ToBase64String(hmacSha256.Hash));
Trace.Write("String to sign:{0}", signature);
return signature;
}
}
}
Request generated (from Fiddler): GET http://ats.amazonaws.com/?AWSAccessKeyId=REMOVED&Action=TopSites&Count=10&CountryCode=&ResponseGroup=Country&SignatureMethod=HmacSHA256&SignatureVersion=2&Start=1001&Timestamp=2014-11-20T16:57:52.422Z&Signature=vdKOQYRmoJJL3ecY9GAzmGKHAXevoli6rGcEotGFaNY%3D HTTP/1.1 Host: ats.amazonaws.com Connection: Keep-Alive
Response: HTTP/1.1 403 Forbidden Server: Apache-Coyote/1.1 Transfer-Encoding: chunked Date: Thu, 20 Nov 2014 16:57:52 GMT
16d
SignatureDoesNotMatch
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.84291dc8-a35e-7dc3-7cc1-56fe20b5b236
0
Based on Darrel's comment and extensive comparisons between requests from the Java app and my sample app I've been able to correctly query the services using a number of requests including the sample one above. It would appear to have been a problem whereby the request string which is signed had an erroneous space character in front of the hostname, for added resiliency I am using the Amazon AWS SDK for .Net to perform the Url Encoding against their requirements to ensure the encoding is correct.
Here's the working sample code:
Hope this helps someone else too.