t56k
t56k

Reputation: 6981

S3: custom headers return a 403

I've got an app that uploads documents to an S3 bucket. No problems there, but when I specify some custom headers in the request like so:

req.setHeader('x-amz-meta-purpose', 'purpose');

And write matching CORS rules for the bucket:

<CORSRule>
   <AllowedOrigin>http://www.example.com</AllowedOrigin>
   <AllowedMethod>PUT</AllowedMethod>
   <AllowedMethod>POST</AllowedMethod>
   <AllowedMethod>DELETE</AllowedMethod>
   <AllowedHeader>*</AllowedHeader>
   <MaxAgeSeconds>3000</MaxAgeSec>
   <ExposeHeader>x-amz-meta-purpose</ExposeHeader>
</CORSRule>

I get a 403 response. What am I forgetting? My code works fine without the custom header added.

Update

I get this response:

<Error><Code>SignatureDoesNotMatch</Code><Message>The request signature we calculated does not match the signature you provided. Check your key and signing method.</Message>

How do I change my signature to include the custom header? It's currently made like this:

String stringToSign = 'PUT\n\n' + contentType + '\n' + now + '\n' + '/' + bucketname + '/' + lead.Id + '/' + fileName;
String encodedStringToSign = EncodingUtil.urlEncode(stringToSign, 'UTF-8');
Blob mac = Crypto.generateMac('HMACSHA1', Blob.valueOf(stringToSign), Blob.valueOf(secret));
String signed = EncodingUtil.base64Encode(mac);

Any ideas or documentation on how to add headers to that?

Upvotes: 0

Views: 406

Answers (1)

Michael - sqlbot
Michael - sqlbot

Reputation: 179074

You are using Signature V2, so you need to include the "Canonicalized Amz Headers"...

String stringToSign = 'PUT\n\n' + 
                      contentType + '\n' + 
                      now + '\n' + 
>>>>>> here >>>>>>    CanonicalizedAmzHeaders +
                      '/' + bucketname + '/' + lead.Id + '/' + fileName;

Note there is no additional '\n' needed after this new value, but it the CanonicalizedAmzHeaders string, itself, ends with a '\n'.

Canonicalized Amz headers is built by iterating through each header beginning with x-amz-, with the header names converted to lowercase and then sorted lexically, building a string that looks like this (pseudocode):

lowercase(header1) + ':' + trim(value1) + '\n' +
lowercase(header2) + ':' + trim(value2) + '\n' +
...

So, in your request, it would the last three lines would look like this:

[date]\n
x-amz-meta-purpose:purpose\n
/bucketname/key

http://docs.aws.amazon.com/AmazonS3/latest/dev/RESTAuthentication.html#ConstructingTheCanonicalizedResourceElement

Upvotes: 1

Related Questions