How do you create the hex encoded signature in crypto in a nodejs environment?
Do you just do this like so?
const secret = 'mysecret';
const date = yyyymmdd;
const dateKey = crypto.createHmac('sha256', 'AWS4' + secret + ',' + date);
const dateRegionKey = crypto('sha256', dateKey + ',' + 'myregion')
const DateRegionServiceKey = crypto('sha256', dateRegionKey + ',' + 'someservice');
const signingKey = crypto('sha256', DateRegionServiceKey + ',' + 'aws4_request');
const signature = crypo('sha256', signingKey + base64Policy);
High level it looks ok. I hope you know that secret is not just a string, it is the secretKey of your IAM User accessKey/secretKey pair.
Any particular reason you want to do it yourself and not use the AWS SDK?
Also look at below for a sample signing implementation (in Java) which works for AWS ElasticSearch. Look at getSignatureKey and calculateSignature methods.
https://github.com/dy10/aws-elasticsearch-query-java/blob/master/src/main/java/dy/aws/es/AWSV4Auth.java
you should use this library https://github.com/mhart/aws4
you need to use result = aws4.sign({}, { cred from AWS })
and it returns result.headers that are the headers you need to send in your request
Related
After seeing through this link, I tried the same in my postman.
var storageAccount = "mystorage";
var accountKey = "<<primaryKey>>";
var date = new Date();
var UTCstring = date.toUTCString();
var data = UTCstring + "\n" + "/mystorage/Health"
var encodedData = unescape(encodeURIComponent(data));
var hash = CryptoJS.HmacSHA256(encodedData, CryptoJS.enc.Base64.parse(accountKey));
var signature = hash.toString(CryptoJS.enc.Base64);
var auth = "SharedKeyLite " + storageAccount + ":" + signature;
postman.setEnvironmentVariable("auth", auth);
postman.setEnvironmentVariable("date", UTCstring);
When I make the request to ATS, to the following url,
I get the auth denied!
Can someone please guide me what's going wrong here?!
I think you need to generate a bearer token and put it to the Authorization of Postman.
If you are using C#, you can use this to get the bearer token:
AzureServiceTokenProvider azureServiceTokenProvider = new AzureServiceTokenProvider();
string accesstoken = azureServiceTokenProvider.GetAccessTokenAsync("https://storage.azure.com/").Result;
string bearertoken = "Bearer " + accesstoken;
Then Copy the bearer token:
After that, it should be ok.
Just realized that the url and the data that we are encoding should exactly match the url we are querying...
After changing
var data = UTCstring + "\n" + "/mystorage/Health"
to
var data = UTCstring + "\n" + "/mystorage/Health(PartitionKey='USA',RowKey='WA')"
things started working.
Update :
It just expects the right table query. The following works fine,
var data = UTCstring + "\n" + "/mystorage/Health()"
with all filter expressions in the url being invoked from postman.
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.
I am using Bold Commerce Webhooks to subscribe to subscription events in my store. They give documentation about how their request signatures are generated in PHP:
$now = time(); // current unix timestamp
$json = json_encode($payload, JSON_FORCE_OBJECT);
$signature = hash_hmac('sha256', $now.'.'.$json, $signingKey);
I'm trying to recreate the hash on my side in node.js. From my research I've figured out the following so far, which I believe is pretty close, but doesn't match yet:
const hash = request.header("X-Bold-Signature")!;
const SECRET = "my-secret-api-key";
const body = request.body;
const time = request.header("timestamp")!;
const mySignature = crypto.createHmac('sha256', SECRET).update(time + '.' + body).digest("hex");
if (mySignature !== request.header("X-Bold-Signature")!) {
//...
}
I've also tried using JSON.stringify(body) which changes the hash but still doesn't match.
It matched with this code.
const hash = crypto
.createHmac('sha256', secretKey)
.update(timestamp + '.' + JSON.stringify(body))
.digest('hex');
Note that unlike shopify, do not use rawbody.
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
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.