3

Following the AWS example on Signature Version 4, I am trying to reproduce the example using a Google Sheets with Google apps scripting. I face an issue with the kregion signature in the example. While I can confirm that the AWS example is valid using an online signature tool, I am never able to reproduce the example output using any Google app script. As HMAC SHA256 signature works fine for an earlier part of the example (kdate), the issue seems to come from the way data is stored or used.

What works for kDate is the following with parts from another stackoverflow entry to convert byte array to hex string and with the help of Google apps script documentation for computeHmacSha256Signature:

var input="20120215";
var key="AWS4"+"wJalrXUtnFEMI/K7MDENG+bPxRfiCYEXAMPLEKEY";//Do not worry, this is an example key, not my actual key
var signature=Utilities.computeHmacSha256Signature(input,key);
signature=signature.map(function(chr){return (chr+256).toString(16).slice(-2)}).join('');//convert byte array to hex string
Logger.log(signature);//valid 969fbb94feb542b71ede6f87fe4d5fa29c789342b0f407474670f0c2489e0a0d

Here is the same thing for the kRegion:

var input="us-east-1";
var key="969fbb94feb542b71ede6f87fe4d5fa29c789342b0f407474670f0c2489e0a0d";
var signature=Utilities.computeHmacSha256Signature(input,key);
signature=signature.map(function(chr){return (chr+256).toString(16).slice(-2)}).join('');
Logger.log(signature);//non-valid a59e30f9d899c47b3dd68ea1c0ab3bb529e03a8f4ed2f54cb64af547330a22a0

I am not surprised that it doesn't produce the valid example kRegion output 69daa0209cd9c5ff5c8ced464a696fd4252e981430b10e3d3fd8e2f197d7a70c because the key is stored as a hex string which doesn't make much sense. Here is the same thing with an attempt at decoding the hex string to a byte array inspired from this github entry:

var input="us-east-1";
var key="969fbb94feb542b71ede6f87fe4d5fa29c789342b0f407474670f0c2489e0a0d";
var a=[];
for(var i=0, len=key.length; i<len; i+=2) {
  a.push(parseInt(key.substr(i,2),16));
}
key=a;
Logger.log(key);//[150.0, 159.0, 187.0, 148.0, 254.0, 181.0, 66.0, 183.0, 30.0, 222.0, 111.0, 135.0, 254.0, 77.0, 95.0, 162.0, 156.0, 120.0, 147.0, 66.0, 176.0, 244.0, 7.0, 71.0, 70.0, 112.0, 240.0, 194.0, 72.0, 158.0, 10.0, 13.0]
var signature=Utilities.computeHmacSha256Signature(input,key);
signature=signature.map(function(chr){return (chr+256).toString(16).slice(-2)}).join('');
Logger.log(signature);//non-valid ac7a5b21190d18b323886d48cd3c4c7486a0f1dd4edb80d245a221a95f5e689b

Using an online converter, i can confirm that the key is well converted. Yet, the output signature is still wrong. I was wondering whether it could be because the key bytes are stored as doubles. So, I tried the following with no difference on the output signature:

var input="us-east-1";
var key=[0x96, 0x9f, 0xbb, 0x94, 0xfe, 0xb5, 0x42, 0xb7, 0x1e, 0xde, 0x6f, 0x87, 0xfe, 0x4d, 0x5f, 0xa2, 0x9c, 0x78, 0x93, 0x42, 0xb0, 0xf4, 0x07, 0x47, 0x46, 0x70, 0xf0, 0xc2, 0x48, 0x9e, 0x0a, 0x0d];
var signature=Utilities.computeHmacSha256Signature(input,key);
signature=signature.map(function(chr){return (chr+256).toString(16).slice(-2)}).join('');
Logger.log(signature);//non-valid ac7a5b21190d18b323886d48cd3c4c7486a0f1dd4edb80d245a221a95f5e689b

Then I thought maybe both parameters of the computeHmacSha256Signature function need to be byte arrays:

var input=[0x75, 0x73, 0x2d, 0x65, 0x61, 0x73, 0x74, 0x2d, 0x31];
var key=[0x96, 0x9f, 0xbb, 0x94, 0xfe, 0xb5, 0x42, 0xb7, 0x1e, 0xde, 0x6f, 0x87, 0xfe, 0x4d, 0x5f, 0xa2, 0x9c, 0x78, 0x93, 0x42, 0xb0, 0xf4, 0x07, 0x47, 0x46, 0x70, 0xf0, 0xc2, 0x48, 0x9e, 0x0a, 0x0d];
var signature=Utilities.computeHmacSha256Signature(input,key);//leads to error "Cannot convert Array to (class)[]."
signature=signature.map(function(chr){return (chr+256).toString(16).slice(-2)}).join('');
Logger.log(signature);

The above leads to error "Cannot convert Array to (class)[].". Which seems to indicate that the computeHmacSha256Signature function is more comfortable with the string input.

I got yet another invalid kRegion output signature by using the output signature of kDate without converting it to a string:

var input="20120215";
var key="AWS4"+"wJalrXUtnFEMI/K7MDENG+bPxRfiCYEXAMPLEKEY";
var signature=Utilities.computeHmacSha256Signature(input,key);
input="us-east-1";
key=signature;
signature = Utilities.computeHmacSha256Signature(input,key);
signature=signature.map(function(chr){return (chr+256).toString(16).slice(-2)}).join('');
Logger.log(signature);//non-valid c3b37a4dc2e085fcd35411493526592a33ef1d7d38454a25e574a34fe190d7be

I tried many other conversions with no success.

luvzfootball
  • 710
  • 1
  • 9
  • 21

1 Answers1

6
  • You want to achieve the result of the sample scripts of "Examples of How to Derive a Signing Key for Signature Version 4" using Google Apps Script.

    • In your case, you want to retrieve the value of f4780e2d9f65fa895f9c67b32ce1baf0b0d8a43505a000a1a9e090d414db404d from the following sample values as kSigning.

      key = 'wJalrXUtnFEMI/K7MDENG+bPxRfiCYEXAMPLEKEY'
      dateStamp = '20120215'
      regionName = 'us-east-1'
      serviceName = 'iam'
      

If my understanding is correct, how about this sample script?

Important points:

  • At Google Apps Script, the data which was encrypted by Utilities.computeHmacSha256Signature() is the bytes array of the signed hexadecimal. In the sample scripts, the bytes array is converted to the unsigned hexadecimal. So it is required to be converted.
    • But, when the byte array is created by Utilities.computeHmacSha256Signature(), the created byte array can be used for Utilities.computeHmacSha256Signature() without converting.

From above situation, the sample script for Google Apps Script can be made as follows.

Sample script:

In this sample script, in order to check whether the script is correct, I tested the script using the sample values at https://docs.aws.amazon.com/general/latest/gr/signature-v4-examples.html.

function myFunction() {
  // These are the sample values of https://docs.aws.amazon.com/general/latest/gr/signature-v4-examples.html
  var key = 'wJalrXUtnFEMI/K7MDENG+bPxRfiCYEXAMPLEKEY';
  var dateStamp = '20120215';
  var regionName = 'us-east-1';
  var serviceName = 'iam';

  // I prepared the following script.
  var kDate = Utilities.computeHmacSha256Signature(dateStamp, "AWS4" + key);
  var kRegion = Utilities.computeHmacSha256Signature(Utilities.newBlob(regionName).getBytes(), kDate);
  var kService = Utilities.computeHmacSha256Signature(Utilities.newBlob(serviceName).getBytes(), kRegion);
  var kSigning = Utilities.computeHmacSha256Signature(Utilities.newBlob("aws4_request").getBytes(), kService);
  kSigning = kSigning.map(function(e) {return ("0" + (e < 0 ? e + 256 : e).toString(16)).slice(-2)}).join("");

  Logger.log(kSigning) // Result
}
  • About above script, for example, kDate is the byte array. So regionName is required to be converted to the byte array. Please be careful this.

Result:

f4780e2d9f65fa895f9c67b32ce1baf0b0d8a43505a000a1a9e090d414db404d

This value is the same with the sample value. By this, it was found that the prepared script returns the correct value.

References:

If I misunderstood your question and this was not the result you want, I apologize.

Tanaike
  • 181,128
  • 11
  • 97
  • 165
  • Yes, you understood very well my question and solved my issue! The issue I had was what you mentioned: if the key is a byte array, the input must also be a byte array. I though so but my attempts at applying this failed for a yet unknown reason. Anyway, I have your solution now.Thank you so much @Tanaike for your great help ! – luvzfootball Aug 10 '19 at 10:49
  • 1
    @luvzfootball Thank you for replying and testing it. I'm glad your issue was resolved. Thank you, too. – Tanaike Aug 10 '19 at 23:14
  • 2
    you are my hero :) – Mike M Sep 27 '19 at 02:04
  • is the 2012 dateStamp correct? – Justin Thomas Mar 23 '22 at 00:58
  • @Justin Thomas I have to apologize for my poor English skill. Unfortunately, I cannot understand `is the 2012 dateStamp correct?`. Can I ask you about the detail of your comment? By the way, this answer is the script for converted from `key = 'wJalrXUtnFEMI/K7MDENG+bPxRfiCYEXAMPLEKEY'`, `dateStamp = '20120215'`, `regionName = 'us-east-1'`, `serviceName = 'iam'` to `f4780e2d9f65fa895f9c67b32ce1baf0b0d8a43505a000a1a9e090d414db404d`. If this was not useful, the reason of this is due to my poor skill. At that time, I deeply apologize for my poor skill. – Tanaike Mar 23 '22 at 01:05
  • Hi, first of all: var kDate = Utilities.computeHmacSha256Signature(dateStamp, "AWS4" + key); isn't doing the newBlob the other lines are doing. I'm a little unclear what dateStamp is supposed to be. On the header it requests Date: new Date().toUTCString(). Should it be that YYYYMMDD format or should it be the new Date().toUTCString(). I'm getting an error the signature is not expected. – Justin Thomas Mar 23 '22 at 01:21
  • @Justin Thomas Thank you for replying. I would like to support you. But I have to apologize for my poor English skill again. Unfortunately, I cannot understand your reply. So in order to correctly understand your question, can you post it as a new question by including more information? By this, I would like to try to understand your question. If you can cooperate to resolve your question, I'm glad. Can you cooperate to do it? – Tanaike Mar 23 '22 at 01:25
  • Just asking what dateStamp should be? Should it be 20220322 because that is today? – Justin Thomas Mar 23 '22 at 01:41
  • @Justin Thomas Thank you for replying. In the sample situation, when `key = 'wJalrXUtnFEMI/K7MDENG+bPxRfiCYEXAMPLEKEY', dateStamp = '20120215', regionName = 'us-east-1', serviceName = 'iam'` is used the valid value of `f4780e2d9f65fa895f9c67b32ce1baf0b0d8a43505a000a1a9e090d414db404d` is retrieved. But it seems that when `dateStamp = '2012'` is used, the invalid value is retrieved. If this was not your expected explain, I apologize. The reason of this is due to my very poor skill. I have to study more and more. I would be grateful if you can forgive my very poor skill. – Tanaike Mar 23 '22 at 01:45
  • I get the same answer as you do. I just can't get the header to work. var csvContent = UrlFetchApp.fetch(csvUrl,{headers:{Date: now,"Authorization":auth}}).getContentText(); I've tried base64 and hex. Neither accepts the signature. Thanks for your help! – Justin Thomas Mar 23 '22 at 03:45