I am current working with Google Apps script and am attempting to write & sign an HTTP request to AWS CloudWatch.
On the Amazon API documentation here regarding how to create a signing key, they use pseudo to explain that the HMAC algorithm is to return binary format.
HMAC(key, data) represents an HMAC-SHA256 function
that returns output in binary format.
Google apps script offers a method to do such a hash,
Utilities.computeHmacSignature(Utilities.MacAlgorithm.HMAC_SHA_256,
data,
key);
but the return type is always a byte array.
Byte[]
How do I convert the Byte[] to the binary data AWS wants? Or is there a vanilla javascript function I can use in Google Apps Script to compute the hash?
Thanks
I am quite sure it is a bug that Utilities.computeHmacSignature take key as an ASCII. But there was no way to parse byte[] to ASCII correctly in GAS
And the library writer is too stupid too just provide function which take key as byte[]
So I use this instead : http://caligatio.github.com/jsSHA/
Just copy SHA.js and SHA-256.js then it work fine
PS. it waste my time for whole 2 days so I'm very annoying
The conversion from byte array to the binary data required should be simple:
kDate = Utilities.computeHmacSignature(Utilities.MacAlgorithm.HMAC_SHA_256,
'20130618', 'AWS4' + kSecret);
kDate = Utilities.newBlob(kDate).getDataAsString();
kRegion = Utilities.computeHmacSignature(Utilities.MacAlgorithm.HMAC_SHA_256,
'eu-west-1', kDate);
BUT you have to look onto this open issue in the bugtracker - there could be some issues in conversion.
maybe you could try to make a String.fromCharCode() loop and avoid negative numers:
kDateB = Utilities.computeHmacSignature(Utilities.MacAlgorithm.HMAC_SHA_256,
'20130618', 'AWS4' + kSecret);
kDate = '';
for (var i=0; i<kDateB.length; i++)
kDate += String.fromCharCode(kDateB[i]<0?256+kDateB[i]:0+kDateB[i]);
kRegion = Utilities.computeHmacSignature(Utilities.MacAlgorithm.HMAC_SHA_256,
'eu-west-1', kDate);
Related
I can successfully encrypt the value using the following code:
final static Charset ENCODING = StandardCharsets.ISO_8859_1;
var awsCreds = AwsBasicCredentials.create(KEY, SECRET_KEY);
kmsClient = KmsClient.builder().credentialsProvider(StaticCredentialsProvider.create(awsCreds)).region(Region.US_EAST_1).build();
var sdkBytesString = SdkBytes.fromString(stringToEncrypt, ENCODING);
var encryptRequest = EncryptRequest.builder().keyId(KEY_ARN).plaintext(sdkBytesString).build();
var encryptResponse = this.kmsClient.encrypt(encryptRequest);
var result = encryptResponse.ciphertextBlob().asString(ENCODING);
In the result I can see encrypted value.
BUT The problem is that I need this value in UTF8 not ISO_8859_1. When trying to get ciphertextBlob in UTF8 - getting conversion error:
Blockquote
java.io.UncheckedIOException: Cannot encode string.
I need to save the string in UTF-8 DB and to send this encrypted string to another service that accepts UTF-8 strings\
Could you please advise how to get UTF-8 string after encryption?
Actually Base64 encrypting solves the problem:
https://github.com/amazon-archives/realworld-serverless-application/blob/master/backend/src/main/java/software/amazon/serverless/apprepo/api/impl/pagination/EncryptedTokenSerializer.java#L51
I'm trying to use the createInvalidtion API that amazon offers, and I'm a bit confused about the authentication part, I saw this documentation about the AWS signature, but, do I really want to do all of this?! It seems too much for a simple request, also, I'm not sure of what each part should be, specially the canonical request part as the API I want to use is simple one with no much to build.
I'm using a programming language (TCL) with no built-in library for cloudFront, So I'm going to build everything from scratch, appreciate any help or if anyone went through a similar situation.
I saw this documentation about the AWS signature, but, do I really
want to do all of this?!
The AWS signature is built using a chain of HMAC computations, there is not all too much to doing this. Departing from the AWS documentation, this could look like:
set key "wJalrXUtnFEMI/K7MDENG+bPxRfiCYEXAMPLEKEY"
set dateStamp "20120215"
set regionName "us-east-1"
set serviceName "iam"
proc hmac-sha256 {str hexKey} {
lindex [exec openssl dgst -sha256 -mac hmac -macopt hexkey:$hexKey << [encoding convertto utf-8 $str]] 1
}
proc getSignatureKey {key dateStamp regionName serviceName} {
binary scan [encoding convertto utf-8 "AWS4$key"] H* hexKey
set kDate [hmac-sha256 $dateStamp $hexKey]
puts "kDate = $kDate"
set kRegion [hmac-sha256 $regionName $kDate];
puts "kRegion = $kRegion"
set kService [hmac-sha256 $serviceName $kRegion];
puts "kService = $kService"
set kSigning [hmac-sha256 "aws4_request" $kService]
puts "kSigning = $kSigning"
return $kSigning
}
getSignatureKey $key $dateStamp $regionName $serviceName
This prints out:
kDate = 969fbb94feb542b71ede6f87fe4d5fa29c789342b0f407474670f0c2489e0a0d
kRegion = 69daa0209cd9c5ff5c8ced464a696fd4252e981430b10e3d3fd8e2f197d7a70c
kService = f72cfd46f26bc4643f06a11eabb6c0ba18780c19a8da0c31ace671265e3c87fa
kSigning = f4780e2d9f65fa895f9c67b32ce1baf0b0d8a43505a000a1a9e090d414db404d
Some background:
Calling out to the openssl executable might not be adequate in some situations, you might need to check out for some Tcl built-ins (I am only aware of an Tcl command binding to OpenSSL/LibreSSL in NaviServer and tcllib's sha2 module)
The above implementation passes around the hexdumps of the binary strings (for educational, debugging purposes), you might want to use the binary strings directly.
As for the REST of the task, it boils down to assembling an HTTP request with custom request parameters and XML payload.
I am trying to use google-kms nodejs library.
What I was expecting is that the end result will be encrypted text but what I get is a buffer if I don't decide base64 or if I do then I get something like $�k+��l�k��:
Does someone know what's wrong r is my expectation wrong about the encoded text.
exports.encryptKMS = async (req, res) => {
let message = req.query.message || req.body.message || 'Hello World!';
const projectId = 'xxx';
const kms = require('#google-cloud/kms');
const client = new kms.KeyManagementServiceClient();
const locationId = 'global';
const keyRingId = 'Test-Ring-01';
const cryptoKeyId = 'Test-Crypto-01';
const cryptoKeyPath = client.cryptoKeyPath(
projectId,
locationId,
keyRingId,
cryptoKeyId
);
const [result] = await client.encrypt({name: cryptoKeyPath, plaintext: message});
console.log(result);
const cryptoText = Buffer.from(result.ciphertext, 'base64').toString('utf-8');
console.log(cryptoText);
res.status(200).send(cryptoText);
}
This looks correct to me, the encrypted object will be a base64 encoded binary string which you won't be able to see any structure in. If you take that result and feed it back into Decrypt you should recover the original message.
If you wanted a readable string, please provide more info -- there's such a thing as "Format-Preserving Encryption" which attempts to encrypt data in such a fashion that it will continue to be in a compatible format to the input, for when systems aren't ready to handle encrypted data. Cloud KMS doesn't support format-preserving encryption, but I may have some suggestions depending on what you need.
Thanks for using GCP and Cloud KMS!
Here is my code in the JSR223 PreProcessor.
import javax.crypto.Mac
import javax.crypto.spec.SecretKeySpec
import java.security.InvalidKeyException
import java.security.MessageDigest
import groovy.json.JsonSlurper
import java.text.SimpleDateFormat
static byte[] HmacSHA256(String data, byte[] key) throws Exception {
String algorithm="HmacSHA256";
Mac mac = Mac.getInstance(algorithm);
mac.init(new SecretKeySpec(key, algorithm));
return mac.doFinal(data.getBytes("UTF8"));
}
static byte[] getSignatureKey(String key, String dateStamp, String regionName, String serviceName) throws Exception {
byte[] kSecret = ("AWS4" + key).getBytes("UTF8");
byte[] kDate = HmacSHA256(dateStamp, kSecret);
byte[] kRegion = HmacSHA256(regionName, kDate);
byte[] kService = HmacSHA256(serviceName, kRegion);
byte[] kSigning = HmacSHA256("aws4_request", kService);
return kSigning;
}
Example parameter values to the function getSignatureKey are (these I am passing to the Parameters section of the JSR223 PreProcessor as variables)
key = eC6hEyRSTXMzsG6+juOObz8LbXb36iEYW7PPN1MJ
dateStamp = 20190123T083434Z
regionName = us-west-2
serviceName = test-mlp-us-west-2-4023179c-7708-4c5e-a831-28259b8a8872.s3.us-west-2.amazonaws.com
This code is not working and not generating the AWS signature.
Here is the sample Signature value I need to get
Signature=2a6092ec4ff49dc9j3b92d436635a57f312753kcc9f553ce1718b9b1594c4362
1.What is wrong with this code?
2.How can I assign the AWS signature to a variable and use in the JMeter?
If "This code is not working" the first thing you should do is to look into jmeter.log file - in case of Groovy script failure it will contain information regarding what exactly failed and why and some hints to fix.
I also think you should generate the signature basing on request details (URL, parameters, headers, etc.)
StringToSign =
Algorithm + \n +
RequestDateTime + \n +
CredentialScope + \n +
HashedCanonicalRequest
See Create a String to Sign for Signature Version 4
The whole process with examples is described in the How to Handle Dynamic AWS SigV4 in JMeter for API Testing and the test plan you can use as the reference is available in the GitHub repo.
I'm testing an application which is hosted on AWS infrastructure using JMeter tool wherein each request triggered needs to have a AWS signed header passed along with the request to validate the request at the IAM level in AWS. I have the access and secret key pertaining to the user role created in AWS console.
Is there any beanshell code available in JMeter which helps in generating AWS signature using the access and secret key for each request in JMeter?
The code is available on AWS docs website, see Deriving the Signing Key with Java for instance. Here is minimal listing just in case:
static byte[] HmacSHA256(String data, byte[] key) throws Exception {
String algorithm="HmacSHA256";
Mac mac = Mac.getInstance(algorithm);
mac.init(new SecretKeySpec(key, algorithm));
return mac.doFinal(data.getBytes("UTF8"));
}
static byte[] getSignatureKey(String key, String dateStamp, String regionName, String serviceName) throws Exception {
byte[] kSecret = ("AWS4" + key).getBytes("UTF8");
byte[] kDate = HmacSHA256(dateStamp, kSecret);
byte[] kRegion = HmacSHA256(regionName, kDate);
byte[] kService = HmacSHA256(serviceName, kRegion);
byte[] kSigning = HmacSHA256("aws4_request", kService);
return kSigning;
}
Be aware that starting from JMeter version 3.1 it is recommended to use JSR223 Elements and Groovy language for scripting as it is more modern (supports all new Java SDK features), has a lot of "syntax sugar" on top of "normal" Java SDK and Groovy performance is much better comparing to Beanshell, JavaScript and other available options. See Apache Groovy - Why and How You Should Use It article for more details.