Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Creating a signed S3 URL with Javascript

I am attempting to create a signed S3 URL using Javascript & NodeJS. I have used this specification.

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:

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:

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:

'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.

like image 919
Kit Avatar asked Nov 15 '11 10:11

Kit


1 Answers

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:

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:

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.

like image 127
Shai Mishali Avatar answered Sep 30 '22 19:09

Shai Mishali