AWS Signature Version 4 S3 Upload using Node.js - node.js

I've been following the AWS example on how to generate a V4 HMAC signature. I've done this successfully in Java but I'm trying to get it to work in Node/JavaScript. When I use my code I generate all the correct intermediary keys in their 1st example below but on the next example when given the test StringToSign the same code that generated the correct intermediary keys fails to generate the supposed correct signature.
Correct Intermediary Keys:
secretkey = 'wJalrXUtnFEMI/K7MDENG+bPxRfiCYEXAMPLEKEY'
dateStamp = '20120215'
regionName = 'us-east-1'
serviceName = 'iam'
kSecret = '41575334774a616c725855746e46454d492f4b374d44454e472b62507852666943594558414d504c454b4559'
kDate = '969fbb94feb542b71ede6f87fe4d5fa29c789342b0f407474670f0c2489e0a0d'
kRegion = '69daa0209cd9c5ff5c8ced464a696fd4252e981430b10e3d3fd8e2f197d7a70c'
kService = 'f72cfd46f26bc4643f06a11eabb6c0ba18780c19a8da0c31ace671265e3c87fa'
kSigning = 'f4780e2d9f65fa895f9c67b32ce1baf0b0d8a43505a000a1a9e090d414db404d'
http://docs.aws.amazon.com/general/latest/gr/signature-v4-examples.html.
Fails With the Following Input
secretkey = 'wJalrXUtnFEMI/K7MDENG+bPxRfiCYEXAMPLEKEY'
dateStamp = '20151229'
regionName = 'us-east-1'
serviceName = 's3'
StringToSign
eyAiZXhwaXJhdGlvbiI6ICIyMDE1LTEyLTMwVDEyOjAwOjAwLjAwMFoiLA0KICAiY29uZGl0aW9ucyI6IFsNCiAgICB7ImJ1Y2tldCI6ICJzaWd2NGV4YW1wbGVidWNrZXQifSwNCiAgICBbInN0YXJ0cy13aXRoIiwgIiRrZXkiLCAidXNlci91c2VyMS8iXSwNCiAgICB7ImFjbCI6ICJwdWJsaWMtcmVhZCJ9LA0KICAgIHsic3VjY2Vzc19hY3Rpb25fcmVkaXJlY3QiOiAiaHR0cDovL3NpZ3Y0ZXhhbXBsZWJ1Y2tldC5zMy5hbWF6b25hd3MuY29tL3N1Y2Nlc3NmdWxfdXBsb2FkLmh0bWwifSwNCiAgICBbInN0YXJ0cy13aXRoIiwgIiRDb250ZW50LVR5cGUiLCAiaW1hZ2UvIl0sDQogICAgeyJ4LWFtei1tZXRhLXV1aWQiOiAiMTQzNjUxMjM2NTEyNzQifSwNCiAgICB7IngtYW16LXNlcnZlci1zaWRlLWVuY3J5cHRpb24iOiAiQUVTMjU2In0sDQogICAgWyJzdGFydHMtd2l0aCIsICIkeC1hbXotbWV0YS10YWciLCAiIl0sDQoNCiAgICB7IngtYW16LWNyZWRlbnRpYWwiOiAiQUtJQUlPU0ZPRE5ON0VYQU1QTEUvMjAxNTEyMjkvdXMtZWFzdC0xL3MzL2F3czRfcmVxdWVzdCJ9LA0KICAgIHsieC1hbXotYWxnb3JpdGhtIjogIkFXUzQtSE1BQy1TSEEyNTYifSwNCiAgICB7IngtYW16LWRhdGUiOiAiMjAxNTEyMjlUMDAwMDAwWiIgfQ0KICBdDQp9
Correct Signature: 46503978d3596de22955b4b18d6dfb1d54e8c5958727d5bdcd02cc1119c60fc9
My Signature: e7318f0bfd7d86fb9ba81c314f62192ee2baf7273792ef01ffafeb430fc2fb68
http://docs.aws.amazon.com/AmazonS3/latest/API/sigv4-post-example.html
My Code
var crypto = require("crypto-js")
module.exports.getSignatureKey = function(key, dateStamp, regionName, serviceName) {
var kSecret = "AWS4" + key
var kDate = crypto.HmacSHA256(dateStamp, kSecret)
var kRegion = crypto.HmacSHA256(regionName, kDate)
var kService = crypto.HmacSHA256(serviceName, kRegion)
var kSigning = crypto.HmacSHA256("aws4_request", kService)
return kSigning;
}
module.exports.sign = function(signatureKey,stringToSign) {
var unencodedSignature = crypto.HmacSHA256(stringToSign,signatureKey)
return unencodedSignature
}
module.exports.getSignature = function(stringToSign,secretKey,dateStamp,regionName, serviceName) {
var signingKey = this.getSignatureKey(secretKey,dateStamp,regionName,serviceName)
return this.sign(signingKey,stringToSign)
}

The AWS example in the second link has the wrong signature. Using my solution I am able to successfully upload to s3.
Something else to consider is that the crypto-js node library outputs the signatures already in hex. Theres no need to do a manual conversion yourself as you would if you we in Java using the example code they provide.

Related

Converting .NodeJS Authentication Token script to Apps Script

I am a newer Apps Scripts user who has been stuck on this problem for some time. I am trying to connect my company's reporting feature to a Google Sheet so I can create reporting dashboards for our users.
I have a .jsNode code snippet which I am able to successfully run outside of the Apps Scripts editor, but I am having issues translating it to Apps Scripts.
My goal is to be able to generate an Authentication Token which will be used in a header within a POST request. I will then use this to get a specific URL that I can pull data from (it will be a .csv file. I already feel I can accomplish this using another script)
Below is the .NodeJS code snippet:
const crypto = require('crypto');
module.exports.init = function () {
let prefix = 'abc-admin-v1'
let businessId = 'XXXX'
let publicAppKeyId = 'XXXX'
let secretKey = 'XXXX'
let unixTimestamp = Math.floor(Date.now() / 1000)
let payload = `${prefix}${businessId}${unixTimestamp}`
let rawKey = Buffer.from(secretKey, 'base64')
let signature = crypto.createHmac('sha256', rawKey).update(payload, 'utf8').digest('base64')
let token = `${signature}${payload}`
let httpBasicPayload = `${publicAppKeyId}:${token}`
let httpBasicCredentials = Buffer.from(httpBasicPayload, 'utf8').toString('base64')
console.log(httpBasicCredentials);
};
Below will be what I have in Apps Scripts:
function apiInfo() {
var secret = "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX";
var apiKey = "XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX";
var locationID = "XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX";
var prefix = "abc-admin-v1";
var timeStamp = Math.round((new Date()).getTime() / 1000);
var timeStampReal = JSON.stringify(timeStamp);
var tokenPayload = prefix + businessID + timeStampReal;
var enCodeTokenPayload = Utilities.base64Encode(tokenPayload);
var deCodeTokenPayload = Utilities.base64Decode(enCodeTokenPayload);
var tokenPayloadReal = JSON.stringify(tokenPayload);
var rawKey = Utilities.base64Decode(secret);
var rawMac = Utilities.computeHmacSha256Signature(tokenPayloadReal, rawKey);
var signature = Utilities.base64Encode(rawMac);
var token = signature + tokenPayload;
var httpBasicPayload = apiKey + ":" + token;
var httpBasicCredentials = Utilities.base64Encode(httpBasicPayload);
When testing using the current GAS code, it produces an invalid Auth Token.
When I run the Node.js version outside of GAS, it produces a valid Auth Token. I am looking to find out what is currently wrong with my code that is producing an invalid auth token. I would like to keep the project within GAS if possible.
I believe your goal is as follows.
You want to convert your showing Node.js script to Google Apps Script. Namely, you want to retrieve the same values of httpBasicCredentials between your Node.js script and Google Apps Script.
const crypto = require('crypto');
module.exports.init = function () {
let prefix = 'abc-admin-v1'
let businessId = 'XXXX'
let publicAppKeyId = 'XXXX'
let secretKey = 'XXXX'
let unixTimestamp = Math.floor(Date.now() / 1000)
let payload = `${prefix}${businessId}${unixTimestamp}`
let rawKey = Buffer.from(secretKey, 'base64')
let signature = crypto.createHmac('sha256', rawKey).update(payload, 'utf8').digest('base64')
let token = `${signature}${payload}`
let httpBasicPayload = `${publicAppKeyId}:${token}`
let httpBasicCredentials = Buffer.from(httpBasicPayload, 'utf8').toString('base64')
console.log(httpBasicCredentials);
};
When I saw your showing Google Apps Script, unfortunately, there are several undeclared variable and unused variables. By this, in this answer, I would like to propose to directly converting your showing Node.js to Google Apps Script.
Sample script:
function myFunction() {
let prefix = 'abc-admin-v1'
let businessId = 'XXXX'
let publicAppKeyId = 'XXXX'
let secretKey = 'XXXX'
let unixTimestamp = Math.floor(Date.now() / 1000);
let payload = `${prefix}${businessId}${unixTimestamp}`;
let payloadBytes = Utilities.newBlob(payload).getBytes();
let rawKey = Utilities.base64Decode(secretKey);
let signature = Utilities.base64Encode(Utilities.computeHmacSha256Signature(payloadBytes, rawKey));
let token = `${signature}${payload}`;
let httpBasicPayload = `${publicAppKeyId}:${token}`;
let httpBasicCredentials = Utilities.base64Encode(httpBasicPayload);
console.log(httpBasicCredentials);
}
Testing:
In the above scripts, when the value of unixTimestamp is 1661403828, both your Node.js and my sample Google Apps Script are the same values as follows.
WFhYWDpGYXBwRk9kaFoxdDVQNGVkV2JJbFIvR1RiMkEyZ0hwYThld05BYjVDVkxnPWFiYy1hZG1pbi12MVhYWFgxNjYxNDAzODI4
Note:
From your question, in this answer, your Node.js is converted to Google Apps Script. Please be careful about this.
References:
base64Encode(data)
computeHmacSha256Signature(value, key)

How do I form an SAS token for Microsoft Azure API Management's REST API in Node.js?

I am using Microsoft Azure API Management service and want to use the REST API service. In creating my SAS token, which is needed otherwise the API call doesn't authorize, I'm having difficulty forming a proper token. Microsoft's webpage about this SAS token for API Management only shows an example in C#. I want to know how to form an SAS token in Node.js, which is not shown. Below is my code that was working last week, but is not now for some unknown reason. The error I get is: 401 Authorization error, token invalid
If someone can help me formulate this token, I would appreciate it.
This is Microsoft's webpage regarding this authentication token: https://learn.microsoft.com/en-us/rest/api/apimanagement/apimanagementrest/azure-api-management-rest-api-authentication
Here's my code:
const crypto = require('crypto');
const util = require('util');
const sign = () => {
const id = ${process.env.id}
const key = `${process.env.SASKey}`;
const date = new Date();
const newDate = new Date(date.setTime(date.getTime() + 8 * 86400000));
const expiry = `${newDate.getFullYear()}${
newDate.getMonth() < 10
? '' + newDate.getMonth() + 1
: newDate.getMonth() + 1
}${newDate.getDate()}${newDate.getHours()}${
newDate.getMinutes() < 10
? '0' + newDate.getMinutes()
: newDate.getMinutes()
}`;
const dataToSignString = '%s\n%s';
const dataToSign = util.format(dataToSignString, ${id}, expiry);
const hash = crypto
.createHmac('sha512', key)
.update(dataToSign)
.digest('base64');
const encodedToken = `SharedAccessSignature ${id}&${expiry}&${hash}`;
console.log(encodedToken);
return encodedToken;
};
Try the code:
protected getAPIManagementSAS(){
let utf8 = require("utf8")
let crypto= require("crypto")
let identifier = process.env.API_IDENTIFIER;
let key = process.env.API_KEY;
var now = new Date;
var utcDate = new Date(now.getUTCFullYear(),now.getUTCMonth(), now.getUTCDate() , now.getUTCHours(), now.getUTCMinutes(), now.getUTCSeconds(), now.getUTCMilliseconds());
let expiry = addMinutes(utcDate,1,"yyyy-MM-ddThh:mm:ss") + '.0000000Z'
var dataToSign = identifier + "\n" + expiry;
var signatureUTF8 = utf8.encode(key);
var signature = crypto.createHmac('sha512', signatureUTF8).update(dataToSign).digest('base64');
var encodedToken = `SharedAccessSignature uid=${identifier}&ex=${expiry}&sn=${signature}`;
return encodedToken
}
For more information, see here.
After a million tries, it seems like the only format acceptable right now is:
SharedAccessSignature uid=${identifier}&ex=${expiry}&sn=${signature}
If you are using the other format that has the "integration" parameter, that's a hit or a miss, mostly miss though. Set the uid as "integration" if that's your identifier and follow the above format as it works.

ShareSasBuilder generating an invalid signature

I'm trying to generate a SAS signed URL to download a file from an Azure file storage (using this as an example):
using Azure.Storage;
using Azure.Storage.Files.Shares;
using Azure.Storage.Files.Shares.Models;
using Azure.Storage.Sas;
(...)
public Uri getFileUri(string fileName)
{
string AccountName = WebConfigurationManager.AppSettings["AzureStorageDepotAccountName"];
string AccountKey = WebConfigurationManager.AppSettings["AzureStorageDepotAccountKey"];
sharedKeyCredential = new StorageSharedKeyCredential(AccountName, AccountKey);
shareClient = new ShareClient(new Uri("https://sanitizedShare.file.core.windows.net/"), sharedKeyCredential);
ShareDirectoryClient directory = shareClient.GetDirectoryClient("sanitizedDir");
ShareFileClient file = directory.GetFileClient(fileName);
var shareSasBuilder = new ShareSasBuilder
{
ShareName = "sanitizedShare",
FilePath = file.Uri.LocalPath,
Protocol = SasProtocol.None,
StartsOn = DateTime.UtcNow.AddHours(-1),
ExpiresOn = DateTime.UtcNow.AddHours(+2),
IPRange = new SasIPRange(IPAddress.None, IPAddress.None)
};
shareSasBuilder.SetPermissions(ShareFileSasPermissions.Read);
return new Uri(file.Uri + "?" + shareSasBuilder.ToSasQueryParameters(sharedKeyCredential).ToString());
}
It returns a correct looking URL (https://sanitizedShare.file.core.windows.net/sanitizedDir/sanitizedFile?sv=2019-07-07&st=2020-05-27T19:36:55Z&se=2020-05-27T22:36:55Z&sr=f&sp=r&sig=l3bLiYlA9Y+Se1jC1g/F5A0T4yOT0nUJHUxyLhNksw8=) but when I try it I get this error:
<Error>
<Code>AuthenticationFailed</Code>
<Message>
Server failed to authenticate the request. Make sure the value of Authorization header is formed correctly including the signature. RequestId:8c400781-e01a-0040-4266-347d43000000 Time:2020-05-27T20:36:56.2303652Z
</Message>
<AuthenticationErrorDetail>Signature fields not well formed.</AuthenticationErrorDetail>
</Error>
At first I thought that I had the wrong credentials, but I'm using the same credentials elsewhere in my code and it can access the share. Do you know what the problem could be?
Make the following changes to your code:
1.add the file share name at the end of the url when create the ShareClient(Note:for the url, I see you're using fileshareName.file.core.windows.net, it should be your_storage_account_name.file.core.windows.net), like below:
var shareClient = new ShareClient(new Uri("https://your_storage_account_name.file.core.windows.net/the_share_name"), sharedKeyCredential);
2.in the code block of new ShareSasBuilder{}, remove FilePath = file.Uri.LocalPath,
Then I tested the code(with the latest version of Azure.Storage.Files.Shares 12.2.1), it generates a valid and working url with sastoken. My code as below:
string storageAccount= "yy1";
string password = "xxxx";
var sharedKeyCredential = new StorageSharedKeyCredential(storageAccount, password);
//the file share name is aaa
var shareClient = new ShareClient(new Uri("https://yy1.file.core.windows.net/aaa"), sharedKeyCredential);
ShareDirectoryClient directory = shareClient.GetDirectoryClient("a11");
ShareFileClient file = directory.GetFileClient("1.txt");
var shareSasBuilder = new ShareSasBuilder
{
ShareName = "aaa",
//FilePath = file.Uri.LocalPath,
Protocol = SasProtocol.None,
StartsOn = DateTime.UtcNow.AddHours(-1),
ExpiresOn = DateTime.UtcNow.AddHours(+2),
IPRange = new SasIPRange(IPAddress.None, IPAddress.None)
};
shareSasBuilder.SetPermissions(ShareFileSasPermissions.Read);
var url = new Uri(file.Uri + "?" + shareSasBuilder.ToSasQueryParameters(sharedKeyCredential).ToString());

How to use 'script' in nodejs

I have this kind of api example and I want to use this in nodejs.
/*
https://code.google.com/archive/p/crypto-js/
https://storage.googleapis.com/google-code-archive-downloads/v2/code.google.com/crypto-js/CryptoJS%20v3.1.2.zip
*/
<script type="text/javascript" src="./CryptoJS/rollups/hmac-sha256.js"></script>
<script type="text/javascript" src="./CryptoJS/components/enc-base64.js"></script>
function makeSignature() {
var space = " "; // one space
var newLine = "\n"; // new line
var method = "GET"; // method
var url = "/photos/puppy.jpg?query1=&query2"; // url (include query string)
var timestamp = "{timestamp}"; // current timestamp (epoch)
var accessKey = "{accessKey}"; // access key id (from portal or Sub Account)
var secretKey = "{secretKey}"; // secret key (from portal or Sub Account)
var hmac = CryptoJS.algo.HMAC.create(CryptoJS.algo.SHA256, secretKey);
hmac.update(method);
hmac.update(space);
hmac.update(url);
hmac.update(newLine);
hmac.update(timestamp);
hmac.update(newLine);
hmac.update(accessKey);
var hash = hmac.finalize();
return hash.toString(CryptoJS.enc.Base64);
}
But the problem is when I use this in Nodejs, I don't know how to require those CryptoJS.
For example, I downloaded CryptoJS file by google. and it is reading by require.
Even though it is read, I don't know which should I read correctly.
Could you help how to solve this problem?
const CryptoJS = require('./CryptoJS v3.1.2/components/enc-base64');
In NodeJS (latest version), you don't even need to download an external library or install from NPM.
Nodejs has crypto built-in library.
const crypto = require('crypto');
var space = " ";
var newLine = "\n";
var method = "GET";
var url = "/photos/puppy.jpg?query1=&query2";
var timestamp = "{timestamp}";
var accessKey = "{accessKey}";
var secretKey = "{secretKey}";
const hash = crypto.createHmac('sha256', secretKey)
.update(method)
.update(space)
.update(url)
.update(newLine)
.update(timestamp)
.update(newLine)
.update(accessKey)
.digest('hex');
console.log(hash);
First of all I dont know why you download file from google? There is very useful npm library. find it here and use it. https://www.npmjs.com/package/crypto-js

Example AWS4 S3 POST signature calculation fails

var crypto = require('crypto')
var secret = 'wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY'
var date = '20151229'
var region = 'us-east-1'
var service = 's3'
var terminator = 'aws4_request'
var policyEncoded = 'eyAiZXhwaXJhdGlvbiI6ICIyMDE1LTEyLTMwVDEyOjAwOjAwLjAwMFoiLA0KICAiY29uZGl0aW9ucyI6IFsNCiAgICB7ImJ1Y2tldCI6ICJzaWd2NGV4YW1wbGVidWNrZXQifSwNCiAgICBbInN0YXJ0cy13aXRoIiwgIiRrZXkiLCAidXNlci91c2VyMS8iXSwNCiAgICB7ImFjbCI6ICJwdWJsaWMtcmVhZCJ9LA0KICAgIHsic3VjY2Vzc19hY3Rpb25fcmVkaXJlY3QiOiAiaHR0cDovL3NpZ3Y0ZXhhbXBsZWJ1Y2tldC5zMy5hbWF6b25hd3MuY29tL3N1Y2Nlc3NmdWxfdXBsb2FkLmh0bWwifSwNCiAgICBbInN0YXJ0cy13aXRoIiwgIiRDb250ZW50LVR5cGUiLCAiaW1hZ2UvIl0sDQogICAgeyJ4LWFtei1tZXRhLXV1aWQiOiAiMTQzNjUxMjM2NTEyNzQifSwNCiAgICB7IngtYW16LXNlcnZlci1zaWRlLWVuY3J5cHRpb24iOiAiQUVTMjU2In0sDQogICAgWyJzdGFydHMtd2l0aCIsICIkeC1hbXotbWV0YS10YWciLCAiIl0sDQoNCiAgICB7IngtYW16LWNyZWRlbnRpYWwiOiAiQUtJQUlPU0ZPRE5ON0VYQU1QTEUvMjAxNTEyMjkvdXMtZWFzdC0xL3MzL2F3czRfcmVxdWVzdCJ9LA0KICAgIHsieC1hbXotYWxnb3JpdGhtIjogIkFXUzQtSE1BQy1TSEEyNTYifSwNCiAgICB7IngtYW16LWRhdGUiOiAiMjAxNTEyMjlUMDAwMDAwWiIgfQ0KICBdDQp9'
console.log('Calculated answer:', crypto
.createHmac('sha256', 'AWS4' + secret)
.update(date)
.update(region)
.update(service)
.update(terminator)
.update(policyEncoded)
.digest('hex'))
console.log('Correct answer :', '8afdbf4008c03f22c2cd3cdb72e4afbb1f6a588f3255ac628749a66d7f09699e')
I wrote the code to calculate the signature for AWS4 S3 upload from the browser.
The example StringToSign comes from here:
Signature v4 calculation example for POST
The expected output is also present on the same page.
The algorithm to sign is presented here:
Calculating a signature v4 for POST
But the result I am getting does not match the correct answer. Please help to locate the error.
I've googled and searched dozens of answers here on SO already, spend 10+ hours.
Short version of exebook answer:
var hmac = require('crypto').createHmac
var secret = 'wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY'
var req = {
date: '20151229',
region: 'us-east-1',
service: 's3',
terminator: 'aws4_request',
policyEncoded: 'eyAiZXhwaXJhdGlvbiI6ICIyMDE1LTEyLTMwVDEyOjAwOjAwLjAwMFoiLA0KICAiY29uZGl0aW9ucyI6IFsNCiAgICB7ImJ1Y2tldCI6ICJzaWd2NGV4YW1wbGVidWNrZXQifSwNCiAgICBbInN0YXJ0cy13aXRoIiwgIiRrZXkiLCAidXNlci91c2VyMS8iXSwNCiAgICB7ImFjbCI6ICJwdWJsaWMtcmVhZCJ9LA0KICAgIHsic3VjY2Vzc19hY3Rpb25fcmVkaXJlY3QiOiAiaHR0cDovL3NpZ3Y0ZXhhbXBsZWJ1Y2tldC5zMy5hbWF6b25hd3MuY29tL3N1Y2Nlc3NmdWxfdXBsb2FkLmh0bWwifSwNCiAgICBbInN0YXJ0cy13aXRoIiwgIiRDb250ZW50LVR5cGUiLCAiaW1hZ2UvIl0sDQogICAgeyJ4LWFtei1tZXRhLXV1aWQiOiAiMTQzNjUxMjM2NTEyNzQifSwNCiAgICB7IngtYW16LXNlcnZlci1zaWRlLWVuY3J5cHRpb24iOiAiQUVTMjU2In0sDQogICAgWyJzdGFydHMtd2l0aCIsICIkeC1hbXotbWV0YS10YWciLCAiIl0sDQoNCiAgICB7IngtYW16LWNyZWRlbnRpYWwiOiAiQUtJQUlPU0ZPRE5ON0VYQU1QTEUvMjAxNTEyMjkvdXMtZWFzdC0xL3MzL2F3czRfcmVxdWVzdCJ9LA0KICAgIHsieC1hbXotYWxnb3JpdGhtIjogIkFXUzQtSE1BQy1TSEEyNTYifSwNCiAgICB7IngtYW16LWRhdGUiOiAiMjAxNTEyMjlUMDAwMDAwWiIgfQ0KICBdDQp9'
}
var signature = Object.keys(req).
reduce((h, k) => hmac('sha256', h).update(req[k]).digest(), 'AWS4' + secret).
toString('hex')
console.log(signature)
//8afdbf4008c03f22c2cd3cdb72e4afbb1f6a588f3255ac628749a66d7f09699e
It does not work because .update() merely writes new data into a hash stream. AWS requires you to hash the result of previous steps with a new key like this:
var x
x = crypto.createHmac('sha256', 'AWS4' + secret).update(date).digest()
x = crypto.createHmac('sha256', x).update(region).digest()
x = crypto.createHmac('sha256', x).update(service).digest()
x = crypto.createHmac('sha256', x).update(terminator).digest()
x = crypto.createHmac('sha256', x).update(policyEncoded).digest()
console.log('Calculated answer:', x.toString('hex'))
console.log('Correct answer :', '8afdbf4008c03f22c2cd3cdb72e4afbb1f6a588f3255ac628749a66d7f09699e')
Output:
Calculated answer: 8afdbf4008c03f22c2cd3cdb72e4afbb1f6a588f3255ac628749a66d7f09699e
Correct answer : 8afdbf4008c03f22c2cd3cdb72e4afbb1f6a588f3255ac628749a66d7f09699e

Resources