Question:
I am attempting to create a signed S3 URL using Javascript & NodeJS. I have used this
specification.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
var crypto = require('crypto'), date = 1331290899, resource = '/myfile.txt', awskey = "XXXX", awssecret = "XXXX"; var stringToSign ='GET\n\n\n' + date + '\n\n' + resource; var sig = encodeURIComponent(crypto.createHmac('sha1', awssecret).update(stringToSign ).digest('base64')); var url = "https://s3-eu-west-1.amazonaws.com/mybucket" + resource + "?AWSAccessKeyId=" + awskey + "&Expires="+ date + "&Signature="+ sig |
This creates a url similar to this:
1 2 |
https://s3-eu-west-1.amazonaws.com/mybucket/test.txt?AWSAccessKeyId=XXXXXX&Expires=1331290899&Signature=EciGxdQ1uOqgFDCRon4vPqTiCLc%3D |
However, I receive the following error when accessing it:
1 2 3 4 5 |
SignatureDoesNotMatch The request signature we calculated does not match the signature you provided. Check your key and signing method. |
What am I doing wrong when creating the signature?
EDIT – ATTEMPT WITH KNOX
I am now attempting to use Knox to produce a signed URL. I need to add headers with the request to force download. I have edited the following:
Added amazonHeaders: 'response-content-disposition:attachment',
to client.signedUrl- http://jsfiddle.net/BpGNM/1/
Added options.amazonHeaders + '\n' +
to auth.queryStringToSign
– http://jsfiddle.net/6b8Tm/
The message that is now being sent to auth.hmacSha1
to create the the sig is:
1 2 |
'GET\n\n\n1321374212\nresponse-content-disposition:attachment\n/meshmesh-dev/test/Readme.md' |
I have then tried to access my new URL with the response-content-disposition=attachment
added as GET var. However, I am still receiving the same error stated above.
Answer:
I would try using Knox along with Node.Js . Its known to be a great combination and also itself utilizes the Node.JS Crypto library which is kind of what you’re trying to do – saving you time:)
More info here : https://github.com/LearnBoost/knox
Than, you could just do something like:
1 2 3 4 5 6 7 8 9 10 11 |
var knox = require('knox'); var s3Client = knox.createClient({ key: 'XXX', secret: 'XXX', bucket: 'XXX' }); var expires = new Date(); expires.setMinutes(expires.getMinutes() + 30); var url = s3Client.signedUrl(filename, expires); |
Edit:
You could also look into Knox and just check what the signedUrl function does and implement that yourself.Than you could add to the auth.signQuery
call an extra option called amazonHeaders
:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
Client.prototype.signedUrl = function(filename, expiration){ var epoch = Math.floor(expiration.getTime()/1000); var signature = auth.signQuery({ amazonHeaders: 'response-content-disposition:attachment', secret: this.secret, date: epoch, resource: '/' + this.bucket + url.parse(filename).pathname }); return this.url(filename) + '?Expires=' + epoch + '&AWSAccessKeyId=' + this.key + '&Signature=' + encodeURIComponent(signature); }; |
Shai.