Creating a S3 Signed URL Using Javascript

I am trying to create a signed S3 URL using Javascript and NodeJS. I 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, when accessing it, the following error appears:

 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 a signature?

EDIT - AN ATTEMPT WITH KNOX

Now I'm trying to use Knox to create a signed URL. I need to add headers asking me to force download. I edited the following:

Added amazonHeaders: 'response-content-disposition:attachment', in client.signedUrl- http://jsfiddle.net/BpGNM/1/

Added options.amazonHeaders + '\n' + to auth.queryStringToSign - http://jsfiddle.net/6b8Tm/

The message that is now sent to auth.hmacSha1 to create the whitefish:

 'GET\n\n\n1321374212\nresponse-content-disposition:attachment\n/meshmesh-dev/test/Readme.md' 

Then I tried to access the new URL using response-content-disposition=attachment , added as GET var. However, I still get the same error that was mentioned above.

+6
source share
3 answers

I would try using Knox along with Node.Js. It is known that this is a great combination, and also uses the Node.JS Crypto library itself, which, like what you are trying to do, is saving your time :)

More info here: https://github.com/LearnBoost/knox

What could you 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 can also look in Knox and just check what the signedUrl function does and implement it yourself. If you can add an additional amazonHeaders option to the 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); }; 

Shay.

+8
source

perhaps too many line feed lines?

 var stringToSign ='GET\n\n\n' + date + '\n\n' + resource; 

If any help here is a garbage PHP implementation that definitely works:

 class myS3Helper{ public function getSignedImageLink($timeout = 1800) { $now = new Zend_Date(); //Gives us a time object that is set to NOW $now->setTimezone('UTC'); //Set to UTC a-la AWS requirements $now->addSecond($timeout); $expirationTime = $now->getTimestamp(); //returns unix timestamp representation of the time. $signature = urlencode( base64_encode( hash_hmac( 'sha1', $this->_generateStringToSign($expirationTime), $my_aws_secretkey, true ) ) ); //FIXME make this less ugly when I know it works $url = 'https://'; $url .= Zend_Service_Amazon_S3::S3_ENDPOINT; //eg s3.amazonaws.com $url .= $this->_getImagePath(); //eg /mybucket/myFirstCar.jpg $url .='?AWSAccessKeyId=' . $my_aws_key; $url .='&Signature=' . $signature; //signature as returned by below function $url .='&Expires=' . $expirationTime; return $url; } protected function _generateStringToSign($expires) { $string = "GET\n"; //Methods $string .= "\n"; $string .= "\n"; $string .= "$expires\n"; //Expires $string .= $this->_getImagePath(); return $string; } 

}

EDIT--

Look at this node.js s3 download code (it’s not mine, but found that it lies on my mac server, so if someone can connect it with someone, let me know and I will do the props). Hope this can help (third time lucky)

https://gist.github.com/1370593

+3
source

My implementation using AWS-SDK and Rx.

 import AWS from "aws-sdk" import Rx from 'rx' /* * Credentials could be loaded from env variables * http://docs.aws.amazon.com/sdk-for-javascript/v2/developer-guide/loading-node-credentials-environment.html * */ const s3 = new AWS.S3({apiVersion: '2006-03-01'}); export function getS3SignedImage(objectKey) { return Rx.Observable.create(function (observer) { s3.getSignedUrl('getObject',{ Bucket: process.env.AWS_BUCKET, Key: objectKey }, (err, data) => { if (err) { return observer.onError(err); } observer.onNext(data); observer.onCompleted(); }); }); } 
0
source

Source: https://habr.com/ru/post/901497/


All Articles