Question

I using the jquery-file-upload plugin to directly upload a file to S3. I have written the code to generate the proper policy document as well as calculated the signature to the best of my knowlegde. The following is the data being posted to the S3

-----------------------------233832764916806 Content-Disposition: form-data; name="key" 50121d1ccb3f3f04400203ab/5365aa6fe104842054008a71.log 
-----------------------------233832764916806 Content-Disposition: form-data; name="acl" private 
-----------------------------233832764916806 Content-Disposition: form-data; name="Policy" eyAiZXhwaXJhdGlvbiIgOiAiMjAxNC0wNS0wNFQwMzo0ODoxNS4wMDBaIiwgImNvbmRpdGlvbnMiIDogW1siZXEiLCAiJGFjbCIsICJwcml2YXRlIl0sIFsiZXEiLCAiJGJ1Y2tldCIsICJmaXJtemVuLWRvY3VtZW50LXVwbG9hZHMiXSwgWyJzdGFydHMtd2l0aCIsICIka2V5IiwgIjUwMTIxZDFjY2IzZjNmMDQ0MDAyMDNhYi8iXSwgWyJjb250ZW50LWxlbmd0aC1yYW5nZSIsIDAsIE51bWJlckxvbmWFtei1kYXRlIiwgIjIwMTcoNTI0Mjg4MCldLCBbImVxIiwgIiR4LQwNTA0VDAwMDAwMFoiXSwgWyJlcSIsICIkeC1hbXotYWxnb3JpdGhtIiwgIkFXUzQtSE1BQy1TSEEyNTYiXSwgWyJlcSIsICIkeC1hbXotY3JlZGVudGlhbCIsICJBS0lBSUczSFZEUzZCRllaNE1NUS8yMDE0MDUwNC9hcC1zb3V0aGVhc3QtMS9zMy9hd3M0X3JlcXVlc3QiXV0gfQ== 
-----------------------------233832764916806 Content-Disposition: form-data; name="X-Amz-Algorithm" AWS4-HMAC-SHA256 
-----------------------------233832764916806 Content-Disposition: form-data; name="X-Amz-Credential" AKIHVDSAIG36BFYZ4MMQ/20140504/ap-southeast-1/s3/aws4_request 
-----------------------------233832764916806 Content-Disposition: form-data; name="X-Amz-Date" 20140504T000000Z 
-----------------------------233832764916806 Content-Disposition: form-data; name="X-Amz-Signature" 6d675b1a24ccd5e4299faaac4218fe27949fb3ac38bd7ccfb7b213195a014682 
-----------------------------233832764916806 Content-Disposition: form-data; name="file"; filename="test.log" Content-Type: application/octet-stream

The error response received is

<error>
    <code>SignatureDoesNotMatch</code>
    <message>The request signature we calculated does not match the signature you provided. Check your key and signing method.</message>
    <stringtosignbytes>hex string</stringtosignbytes>
    <requestid>C2CAD41FF4687E39</requestid>
    <hostid>52UqNeMK28UEydmQx/I/jy/3fsMdKo0UtRAbZnXkfB2aEk35A2bjkciJvNzRktdt</hostid>
    <signatureprovided>6d675b1a24ccd5e4299faaac4218fe27949fb3ac38bd7ccfb7b213195a014682</signatureprovided>
    <stringtosign>eyAiZXhwaXJhdGlvbiIgOiAiMjAxNC0wNS0wNFQwMzo0ODoxNS4wMDBaIiwgImNvbmRpdGlvbnMiIDogW1siZXEiLCAiJGFjbCIsICJwcml2YXRlIl0sIFsiZXEiLCAiJGJ1Y2tldCIsICJmaXJtemVuLWRvY3VtZW50LXVwbG9hZHMiXSwgWyJzdGFydHMtd2l0aCIsICIka2V5IiwgIjUwMTIxZDFjY2IzZjNmMDQ0MDAyMDNhYi8iXSwgWyJjb250ZW50LWxlbmd0aC1yYW5nZSIsIDAsIE51bWJlckxvbmWFtei1kYXRlIiwgIjIwMTcoNTI0Mjg4MCldLCBbImVxIiwgIiR4LQwNTA0VDAwMDAwMFoiXSwgWyJlcSIsICIkeC1hbXotYWxnb3JpdGhtIiwgIkFXUzQtSE1BQy1TSEEyNTYiXSwgWyJlcSIsICIkeC1hbXotY3JlZGVudGlhbCIsICJBS0lBSUczSFZEUzZCRllaNE1NUS8yMDE0MDUwNC9hcC1zb3V0aGVhc3QtMS9zMy9hd3M0X3JlcXVlc3QiXV0gfQ==</stringtosign>
    <awsaccesskeyid>AKIHVDSAIG36BFYZ4MMQ</awsaccesskeyid>
</error>

I am using the following C# code to generate the policy and sign it

var extension = Path.GetExtension(fileName);
var fileId = ObjectId.GenerateNewId();
var key = string.Format("{0}/{1}{2}", _firm.Id, fileId, extension);
var keyMatchCondition = string.Format("{0}/", _firm.Id);
var utcNow = DateTime.UtcNow;
var dateString = utcNow.Date.ToString("yyyyMMdd"); // 20140504
var amzCredentialString = string.Format("{0}/{1}/{2}/s3/aws4_request",
                                        <AWSAccessKey>,
                                        dateString,
                                        "ap-southeast-1");
const string awsAlgorithm = "AWS4-HMAC-SHA256";
var conditions = 
    new List<dynamic[]>
    {
        new dynamic[] {"eq", "$acl", "private"},
        new dynamic[] {"eq", "$bucket", _commonConfig.S3Config.DocumentsBucket},
        new dynamic[] {"starts-with", "$key", keyMatchCondition},
        new dynamic[] {"content-length-range", 0, 1 * 1024 * 1024},
        // utcNow.Date.ToAwsIS08601String() == 20140504T000000Z
        new dynamic[] {"eq", "$x-amz-date", utcNow.Date.ToAwsIS08601String()}, 
        new dynamic[] {"eq", "$x-amz-algorithm", awsAlgorithm},
        new dynamic[] {"eq", "$x-amz-credential", amzCredentialString},
    };
var expiration = utcNow.AddHours(1).ToString("yyyy-MM-ddTHH:mm:ss.000Z", CultureInfo.InvariantCulture);
var policyJson = new {expiration = expiration, conditions = conditions}.ToJson();
var policyBase64 = Convert.ToBase64String(Encoding.UTF8.GetBytes(policyJson));

var dateKey = new HMACSHA256(Encoding.UTF8.GetBytes("AWS4" + "<secret-key>"))
    .ComputeHash(Encoding.UTF8.GetBytes(dateString));
var dateRegionKey = new HMACSHA256(dateKey)
    .ComputeHash(Encoding.UTF8.GetBytes("ap-southeast-1"));
var dateRegionServiceKey = new HMACSHA256(dateRegionKey)
    .ComputeHash(Encoding.UTF8.GetBytes("s3"));
var signingKey = new HMACSHA256(dateRegionServiceKey)
    .ComputeHash(Encoding.UTF8.GetBytes("aws4-request"));
var signedPolicyBytes = new HMACSHA256(signingKey)
    .ComputeHash(Encoding.UTF8.GetBytes(policyBase64));
var signature = BitConverter.ToString(signedPolicyBytes).Replace("-", string.Empty).ToLowerInvariant();

I would really appreciate any pointers on what I am doing wrong or how I should be doing it...

Was it helpful?

Solution

The problem was in how the final signingKey was computed.

var signingKey = new HMACSHA256(dateRegionServiceKey)
    .ComputeHash(Encoding.UTF8.GetBytes("aws4-request"));

The data to be hashed is supposed to be aws4_request and NOT aws4-request. At the time of writing, the AWS S3 docs erroneously[1] mention[2] the final data to be aws4-request.

To conclude, the final signing key should be computed as follows,

var signingKey = new HMACSHA256(dateRegionServiceKey)
    .ComputeHash(Encoding.UTF8.GetBytes("aws4_request"));
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top