Signing AWS API Gateway Request using Node - javascript

I've been searching for ways to restrict access to an API made for using a AWS Lambda function written on javascript.
I've found documentation on how to use AWS Signature S4, but I still do not understand it.
According to creating a signature, after applying the pseudocode I should get the signature to be placed on the header.
I've found the following code that addresses this point:
// Example of signature generator
var crypto = require("crypto-js");
function getSignatureKey(Crypto, key, dateStamp, regionName, serviceName) {
var kDate = Crypto.HmacSHA256(dateStamp, "AWS4" + key);
var kRegion = Crypto.HmacSHA256(regionName, kDate);
var kService = Crypto.HmacSHA256(serviceName, kRegion);
var kSigning = Crypto.HmacSHA256("aws4_request", kService);
return kSigning;
}
console.log(getSignatureKey(crypto,'secretkey','date','us-east-2','iam'));
Here comes my first question, I do not know what should be the output of getSignatureKey()? This is because on the documentation it is a very long string, while the output I got was {words:[x,x,x,x,x,x,x,x],sigBytes: 32},where the x are random numbers.
Moreover, after getting the signature and filling the header for the request with the "authorization" field and others, how do I filter unproper requests? Do I have to create a policy for the AWS API so it only allows signed requests? Here I guess I should follow Signing Requests.
Thanks!

Here is the simple implementation of Signed URL's. aws-cloudfront-sign package offers simpler implementation.
var cfsign = require('aws-cloudfront-sign');
var signingParams = {
keypairId: process.env.PUBLIC_KEY,
privateKeyString: process.env.PRIVATE_KEY,
// Optional - this can be used as an alternative to privateKeyString
privateKeyPath: '/path/to/private/key',
expireTime: 1426625464599
}
// Generating a signed URL
var signedUrl = cfsign.getSignedUrl(
'http://example.cloudfront.net/path/to/s3/object',
signingParams
);
https://aws.amazon.com/blogs/developer/creating-amazon-cloudfront-signed-urls-in-node-js/
Purpose of SignedURL is to serve Private Contents.
More details at,
http://docs.aws.amazon.com/AmazonCloudFront/latest/DeveloperGuide/PrivateContent.html
Hope it helps.

Related

How do I set cryptoJS.sha256 output to binary in Postman pre-request script

I am trying to create an HMAC signature in Postman using a pre-request script. Without going too far into the details of implementation,
I have confirmed that my means for generating the signature is messed up. I can see what the expected result should be with a proof of concept example but I’m missing something somewhere and cannot tell if it is in the conversion. I’ve read around from other questions on SO that binary is the default provided by cryptojs internally and that simply calling for the hash is the equivalent of asking for the digest with conversions completed for you. Here is the code I’m trying to run in postman and the working implementation code as shown in nodeJS.
var CryptoJS = require("crypto-js");
const d = new Date();
const timestamp = d.getTime();
const postData = {};
postData.nonce = 100; //timestamp * 1000; //nanosecond
postman.setEnvironmentVariable('nonce', postData.nonce);
const secret = CryptoJS.enc.Base64.parse(pm.environment.get("apiSecret"));
const path = pm.globals.get("balanceMethod");
const message = CryptoJS.SHA256( encodeURI(postData.nonce + postData)) ; // ...
const hmacDigest = CryptoJS.HmacSHA512(path + message, secret);
postman.setEnvironmentVariable('API-Signature', CryptoJS.enc.Base64.stringify(hmacDigest));
console.log(CryptoJS.enc.Base64.stringify(hmacDigest));
Does this apply to my situation in that I’d need to convert my sha256 message into a bytes array in order to work?
Reference code for building implementation that does work with nodeJS:
const getMessageSignature = (path, request, secret, nonce) => {
const message = qs.stringify(request);
const secret_buffer = new Buffer(secret, 'base64');
const hash = new crypto.createHash('sha256');
const hmac = new crypto.createHmac('sha512', secret_buffer);
const hash_digest = hash.update(nonce + message).digest('binary');
const hmac_digest = hmac.update(path + hash_digest, 'binary').digest('base64');
return hmac_digest;
};
Same reference code for building implementation in python3:
req['nonce'] = 100 #int(1000*time.time())
postdata = urllib.parse.urlencode(req)
# Unicode-objects must be encoded before hashing
encoded = (str(req['nonce']) + postdata).encode()
message = urlpath.encode() + hashlib.sha256(encoded).digest()
signature = hmac.new(base64.b64decode(self.secret),
message, hashlib.sha512)
sigdigest = base64.b64encode(signature.digest())
The only post data I'm sending is the Nonce at this time and I've purposely set it to 100 to be able to replicate the result to fix the generated signature. Seems close but not matching result. The python and nodeJS do match expected results and work properly.
Check out the answer in this thread. It helped me with the my problem and may be what is happening in your case also. All it is necessary is break the input of the HMAC into two parts.

How do I decode a BASE64, PCKS-8 representation of a private key in NetSuite or javascript?

I'm working on a suitescript to integrate NetSuite with the Walmart Marketplace APIs. And, as the another OP here says it right their documentation pretty much says if you don't use Java you're on your own.
I'm looking for a way to do the same either in suitescript or javascript.
Instruction from Walmart's API documentation:
Sign the byte array representation of this data by:
Decoding the Base 64, PKCS-8 representation of your private key. Note that the key is encoded using PKCS-8. Libraries in various languages offer the ability to specify that the key is in this format and not in other conflicting formats such as PKCS-1. Use this byte representation of your key to sign the data using SHA-256 With RSA. Encode the resulting signature using Base 64.
And, a java code from their documentation to do the same:
public static String signData(String stringToBeSigned, String encodedPrivateKey) {
String signatureString = null;
try {
byte[] encodedKeyBytes = Base64.decodeBase64(encodedPrivateKey);
PKCS8EncodedKeySpec privSpec = new PKCS8EncodedKeySpec(encodedKeyBytes);
KeyFactory kf = KeyFactory.getInstance("RSA");
PrivateKey myPrivateKey = kf.generatePrivate(privSpec);
Signature signature = Signature.getInstance("SHA256withRSA");
signature.initSign(myPrivateKey);
byte[] data = stringToBeSigned.getBytes("UTF-8");
signature.update(data);
byte[] signedBytes = signature.sign();
signatureString = Base64.encodeBase64String(signedBytes);
} catch (Exception e) {
e.printStackTrace();
}
return signatureString;
}
For reference, here's the similar thing asked for dot net. Any help would be appreciated.
I tried developing a SAML connector in Javascript once and found several libraries that deal with different key file formats etc. I got fairly far along but the time to run some of the scripts was incredible (imagine trying to login but the process taking two minutes to decide your login was valid)
At that point I switched to an external system and managed the SSO with Netsuite's inbound SSO.
It doesn't look like things have improved that much with NS in the crypto department even with SS 2.0.
I'd tend to package this into two parts. Generate your files in Suitescript and pass them through a java based web service that handles the signing requirements. Minimizes the amount of Java you have to write and keeps your transaction extraction/formatting scripts under easy control.
I found a library (jsrsasign) that will do the Walmart signature from NetSuite server side in under 4 seconds! (Marketplace has gone to OAuth2, but I'm stuck with signing as a Drop Ship Vendor)
/**
*#NApiVersion 2.x
*#NScriptType ScheduledScript
*/
define(['N/log', 'N/https', '../lib/jsrsasign-master/jsrsasign-all-min'],
function(log, https) {
function execute(context) {
var pkcs8Der = {Your Walmart Private Key};
var pkcs8Pem = [
'-----BEGIN PRIVATE KEY-----',
pkcs8Der.match(/.{0,64}/g).join('\n'),
'-----END PRIVATE KEY-----'
].join('\n');
var tStamp = Date.now()
var stringToSign = [
tStamp,
{Your Walmart Comsumer Id},
{Request URL},
{Request Method (All Caps)}
].join('\n') + '\n';
var sig = new KJUR.crypto.Signature({"alg": "SHA256withRSA"});
sig.init(pkcs8Pem);
var sigVal = hextob64(sig.signString(stringToSign));
log.audit({title: 'Signature', details: sigVal});
log.audit({title: 'Timestamp', details: tStamp});
}
return {
execute: execute,
};
}
);
I had to add the following code to the jsrsasign-all-min.js library file for the Scheduled Script to load the module:
var navigator = {}, window = undefined;

Gigya Comment Notification - Generate signature in node

I'm using Gigya Comment Notification service in my node app and trying generate a valid signature. I have followed this documentation, but my code generate wrong hash.
This is my code:
var crypto = require('crypto');
var params = [the notification object from the request];
var eventData = JSON.stringify(params.eventData);
var text = params.event + '_'
+ eventData + '_'
+ params.nonce + '_'
+ params.timestamp;
var secret = new Buffer('Qmxxxxxxxxxxxxx...xxxxxxw=', 'base64');
var hash = crypto.createHmac('sha1', secret).update(text).digest('base64');
if (hash !== params.signature) {
console.log('Not ok')
} else{
console.log('Ok')
}
I think the signature base (text variable) construction may not valid.
This is what my text variable contains (with fake datas):
newComment_[{"categoryID":"category","streamID":"stream","commentID":"123","comment":{"ID":"123","etc":"foobar","timestamp":1447078842653,"threadTimestamp":1447078842653,"status":"published"}}]_aaaaaaaa-bbbb-cccc-dddd-ffffffffffff_1447078842
How can I generate the right signature?
Your signature base construction looks correct, although a nonce doesn't generally have any dashes in it.
The most common reason for a generating an incorrect signature is using the incorrect Secret Key.
Your partner's Secret Key is provided in BASE64 encoding, at the bottom of the Sites Table in the Dashboard section in Gigya website (please make sure that you are logged in to Gigya's website and that you have completed the Gigya's Site Setup process). Please make sure that you are using the partner's Secret Key and not your user's Secret Key.

RSA Verification Javascript

I am trying to verify an RSA signature in Javascript but I can't seem to get it to work. I think I have to do something with my key and signatures but I am very confused.
Here is a link to this library
var publicKey = "MIIBIjBNBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAoPNkJzHqbY/6mAjJwb4zUbOiOjvmg3b8fvydYdGXdv04r6vzgn/FD5NPJM7bojAxi6sZ8vV+fYVIQey6HnrLSsdU/QXhT3p22a+kB4ym8SbKsOy2fWqL950nZCPYW/DC9txHy+ceFuKMAarFWAMJRe+MaVIbDIAAi8tMNjZ204GkmqveyAeA6JppzthAuiX69H8Zb3Hbs49CHNwLnSpKz5HBTfcgWqHkar2HlEFccvWC++Kq47MIkEcKScS/oneDb/TiL5ClOas1gMxfwiVtkFI6zNxxJOJDSTlY66oHCVCfTruk2pQbtOtwJEGrOwq6B536QL/EkeEKMgiqlpZJbQIDAQAB";
var stringToVerify = "aaa";
var signature = "hXyRmdQOCiVBNgDdGtiWF/gJwIk0Hs+MZtfEU4sFMEu05xsBjR9uymOJ/8FwhKCB0p+Kc1jqtsZxQqtxC0Du2EYyvjs0j5bbU9ZugZw0+9VHqKm0UA23djmZ1MT6nXt2ZEUEsS0La9yrfEnig/swAku1fQorsxG5FK5GFRjaacNIF+O0GOr0cbzEvlaAof6T6JFMueIw/iZykivs8XohSlghdPzoNmVueY9JF1XbtHZayau17jGhFTbeNNxbDBanPo593eZdgi5aTZMYHbxHx87cfU1sE5cjSioPQLsG9cQwVaWrrZa9BnB8IhR8Rv0NdRXYNTcVhc+sVHJN/QghNQ==";
var KJUR = require("cloud/jsrsasign-4.7.0/npm/lib/jsrsasign.js");
var verifier = new KJUR.crypto.Signature({alg: "SHA1withRSA", prov: "cryptojs/jsrsa"});
verifier.init(publicKey);
verifier.updateString(stringToVerify);
console.log(verifier.verify(signature));
Thanks in advance
You need to base 64 decode both the public key and the signature. They key and signature should then be re-encoded as hexadecimals. Then you hopefully should be able to use this method to generate a public RSAKey object.
I'm saying hopefully, as the the API description is just horrible. Personally I would not recommend to use such an API.

SHA-1 in Amazon Api Authorization in Javascript

I've been trying to get Authorization for Amazon's s3 rest api going. It's pretty damn complicated.
Because I'm trying to make a simple GET request from an admin page on my website, I'm just trying to do this through Javascript. Here are the instructions for constructing the Signature for the Authorization header:
Signature = Base64( HMAC-SHA1( YourSecretAccessKeyID, UTF-8-Encoding-Of( StringToSign ) ))
To keep us sane, they give us a few examples, with the following givens:
var AWSSecretAccessKey = wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY,
StringToSign = 'GET\n\n\nTue, 27 Mar 2007 19:36:42 +0000\n/johnsmith/photos/puppy.jpg;'
The output for this in their docs is bWq2s1WEIj+Ydj0vQ697zp+IXMU=. Based on the following I am getting ZGVjNzNmNTE0MGU4OWQxYTg3NTg0M2MxZDM5NjIyZDI0MGQxZGY0ZQ==:
function encode_utf8(s) {
return unescape(encodeURIComponent(s));
}
I used code.google.com's CryptoJS.HmacSHA1 function for the SHA1 hashing. My final Signature function looks like this:
var signature = btoa( CryptoJS.HmacSHA1( aws_secret, encode_utf8( StringToSign) ) );
What is going wrong here???
I actually found the answer from an SO question with reference to google's older (2.0) CrytpoJs library. You need:
2.0.0-crypto-sha1.js
2.0.0-hmac-min.js
Then you create your signature as so:
Signature = btoa( Crypto.HMAC(Crypto.SHA1, encode_utf8(StringToSign), aws_secret, { asString: true }) )
I couldn't find a way to to get Strings instead of Bits in the new version.

Categories

Resources