Skip to content

Instantly share code, notes, and snippets.

@adv0r
Created July 27, 2017 17:02
Show Gist options
  • Star 6 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save adv0r/1dfaf7999d7aac95d473e65b675496b0 to your computer and use it in GitHub Desktop.
Save adv0r/1dfaf7999d7aac95d473e65b675496b0 to your computer and use it in GitHub Desktop.
AWS s3 : Generate a pre-signed URL - step-by-step. {Node.js}
//A quick way to generate valid URL to GET files hosted on private AWS S3 bucket
//I didn't want to use an SKD so I followed the documentation to make my own and learn
//The code is dirty, but since I couln't find anything online,
//I figured this could be handy to someone
//Requirements : Node and Crypto-js (install with npm install crypto-js)
//Remote settings-----------------------------------------------------------
var regionName = "eu-central-1"; //Replace with your correct AWS region
var bucketName = "your-bucket-name "; //replace with your bucket name
var testFilePath = "path/to/file.ext"; //replace with the file path you want to GET
//AWS Credentials
var accessKey = "AKEXAMPLEKEY"; //Replace with your Access Key
var secretKey = "dasodpsoakdsakdasuidhsaiudusa"; //Replace with your Secret Key
//Settings -----------------------------------------------------------------
var sep = "%2F"; // Used to encode the '/'
var serviceName = "s3";
var expiration = "86400" //The generate url will be valid for 24 hours from the current date;
var baseUrl = "https://"+bucketName+".s3.amazonaws.com/";
//Use crypto-js library to perform SHA256
var crypto = require("crypto-js");
prepareRequest(crypto); //Execute this on startup
//Prepare request
function prepareRequest(){
var resourceUrl = baseUrl+testFilePath;
console.log("Step 1 - Obtain the base URL : "+resourceUrl);
var longDate = getCurrentDate(); //yyyyMMddTHHmmssZ
var shortDate = longDate.substring(0,8); // yyyyMMdd
console.log("Step 2 - Obtain the current date in both long ["+longDate+"] and shorter ["+shortDate+"] format");
var queryParmeters = getQueryParameters(longDate,shortDate);
console.log("Step 3 - : Obtain the query parameters : "+queryParmeters);
var canonicalRequest = getCanonicalRequest(queryParmeters);
console.log("Step 4 - Obtain the CanonicalRequest : "+canonicalRequest);
var messaggeToSign = getStringToSign(crypto,canonicalRequest,longDate,shortDate);
console.log("Step 5 - Obtain the StringToSign : "+messaggeToSign);
var signignKey = getSignatureKey(crypto,secretKey,shortDate,regionName,serviceName);
console.log("Step 6 - Obtain the signing key : "+signignKey);
var signature = getSignature(crypto,messaggeToSign,signignKey);
console.log("Step 7 - Obtain the signature to append to query parameters : "+signature);
var authorizedUrl = getAuthorizedUrl(resourceUrl,queryParmeters,signature);
console.log("Step 8 - Obtain the valid URL to get the file : "+authorizedUrl);
}
//_______________________________________Functions
function getCurrentDate(){ //
return new Date().toISOString(). //'2012-11-04T14:51:06.157Z'
replace(/-/g, ''). // replace dashes with empty char '20121104T14:51:06.157Z'
replace(/:/g, ''). // replace column with empty char '20121104T145106.157Z'
replace(/\..+/, '') // delete the dot and everything after '20121104T145106'
+'Z' ; //re-add Z for UTC '20121104T145106Z'
}
function getQueryParameters(longDate,shortDate){
var queryParmeters = "X-Amz-Algorithm=AWS4-HMAC-SHA256"+
"&X-Amz-Credential="+accessKey+sep+shortDate+sep+regionName+sep+serviceName+sep+"aws4_request"+
"&X-Amz-Date="+longDate+
"&X-Amz-Expires="+expiration+
"&X-Amz-SignedHeaders=host";
return queryParmeters;
}
function getCanonicalRequest(queryParmeters){
var canonicalRequest = "GET" + "\n" +
"/"+testFilePath + "\n" +
AWSUriEncode(queryParmeters) + "\n" +
"host:"+bucketName+".s3.amazonaws.com" + "\n\n" +
"host"+ "\n" +
"UNSIGNED-PAYLOAD";
return canonicalRequest;
}
function getStringToSign(Crypto,canonicalRequest,longDate,shortDate){
var stringToSign = "AWS4-HMAC-SHA256\n" +
longDate + "\n" +
shortDate + "/"+ regionName + "/" +serviceName +"/aws4_request\n" +
Crypto.SHA256(canonicalRequest); //Compute HASH of CanonicalRequest
return stringToSign;
}
function getSignature(Crypto,message,key){
var kSignature = Crypto.HmacSHA256(message, key);
return kSignature.toString();
}
function getSignatureKey(Crypto, secret, date, regionName, serviceName) {
var message = date;
var key = "AWS4" + secret;
var kDate = Crypto.HmacSHA256(message, key);
//console.log("First salt : Message = "+message+ " , key = "+key + " , result = " + kDate);
message = regionName;
key = kDate.toString();
var kRegion = Crypto.HmacSHA256(message, key);
//console.log("Second salt : Message = "+message+ " , key = "+key + " , result = " + kRegion);
message = serviceName;
key = kRegion;
var kService = Crypto.HmacSHA256(message, key);
//console.log("Third salt : Message = "+message+ " , key = "+key + " , result = " + kService);
message = "aws4_request";
key = kService;
var kSigning = Crypto.HmacSHA256(message, key);
//console.log("Fourth salt : Message = "+message+ " , key = "+key + " , result = " + kSigning);
return kSigning;
}
function getAuthorizedUrl(path,queryParmeters,signature){
var authorizedUrl = path+"?"+
queryParmeters+
"&X-Amz-Signature="+signature;
return authorizedUrl;
}
//TODO improve this function and make it complaint with amazon AWS docs
function AWSUriEncode(inputString){
var outputString = inputString.replace(/\//g,sep);
return outputString;
}
@programatt
Copy link

programatt commented Jan 1, 2019

Here's AWS API v4 documentation being referred to in the snippets in case anyone wants it. https://docs.aws.amazon.com/AmazonS3/latest/API/sig-v4-authenticating-requests.html

@yaskshelat
Copy link

Hi what is testfilePath variable here? I am not clear to this, is this "credential" file found under :C/users/username/.aws folder?
are you creating any lambda functions here? or APIgateway ?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment