I'm trying to create the final step of this example in Flutter and I can't get it right for some reason:
https://docs.aws.amazon.com/general/latest/gr/sigv4-calculate-signature.html
Using their Signing Key + String to sign they get this resulting signature:
5d672d79c15b13162d9279b0855cfba6789a8edb4c82c400e06b5924a6f2b5d7
Using the exact same key / string to sign I end up with:
fe52b221b5173b501c9863cec59554224072ca34c1c827ec5fb8a257f97637b1
Here is my code:
var testSigninKey =
'c4afb1cc5771d871763a393e44b703571b55cc28424d1a5e86da6ed3c154a4b9';
var testStringToSign = 'AWS4-HMAC-SHA256' + '\n' +
'20150830T123600Z' + '\n' +
'20150830/us-east-1/iam/aws4_request' + '\n' +
'f536975d06c0309214f805bb90ccff089219ecd68b2577efef23edd43b7e1a59';
var hmac1 = Hmac(sha256, utf8.encode(testSigninKey));
var digest1 = hmac1.convert(utf8.encode(testStringToSign));
print(digest1);
print(hex.encode(digestBytes.bytes));
//both print: fe52b221b5173b501c9863cec59554224072ca34c1c827ec5fb8a257f97637b1
The hmac1 calls expects you to use the signing key as is, not to use an encoded version of the hex string representation of it.
You should be able to properly construct the Hmac object with this:
var hmac1 = Hmac(sha256, hex.decode(testSigninKey));
Here is a complete example of my flutter code, referring to the s3 signature creation example:
https://docs.aws.amazon.com/AmazonS3/latest/API/sigv4-query-string-auth.html
I know it's not perfect but it took me extremely long to figure it out. Maybe it helps someone.
import 'dart:convert';
import 'package:convert/convert.dart';
import 'package:crypto/crypto.dart';
import 'package:flutter/material.dart';
import 'package:intl/intl.dart';
import 'package:provider/provider.dart';
import 'package:http/http.dart' as http;
class AWS {
var _algorithm = ["X-Amz-Algorithm", "AWS4-HMAC-SHA256"];
final String _awsService = "s3";
final String _aws4Request = "aws4_request";
var _expires = ["X-Amz-Expires", "86400"];
var _signedHeaders = ["X-Amz-SignedHeaders", "host"];
var testbucket = "https://s3.amazonaws.com/examplebucket";
var testfilePath = "/test.txt";
var _testDate = ["X-Amz-Date", "20130524T000000Z"];
var _testDateymd = 20130524;
var _testRegion = "us-east-1";
var _testCredential = [
"X-Amz-Credential",
"AKIAIOSFODNN7EXAMPLE/20130524/us-east-1/s3/aws4_request"
];
var testSecretkey = "wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY";
testString() {
var httpMethod = "GET";
var canUri = testfilePath;
var canQueryStr = Uri.encodeQueryComponent(_algorithm[0]) +
"=" +
Uri.encodeQueryComponent(_algorithm[1]) +
"&" +
Uri.encodeQueryComponent(_testCredential[0]) +
"=" +
Uri.encodeQueryComponent(_testCredential[1]) +
"&" +
Uri.encodeQueryComponent(_testDate[0]) +
"=" +
Uri.encodeQueryComponent(_testDate[1]) +
"&" +
Uri.encodeQueryComponent(_expires[0]) +
"=" +
Uri.encodeQueryComponent(_expires[1]) +
"&" +
Uri.encodeQueryComponent(_signedHeaders[0]) +
"=" +
Uri.encodeQueryComponent(_signedHeaders[1]);
// <CanonicalQueryString>\n
// <CanonicalHeaders>\n
// <SignedHeaders>\n
// <HashedPayload>
var _canonicalRequest = httpMethod +
"\n" +
canUri +
"\n" +
canQueryStr +
"\n" +
"host:examplebucket.s3.amazonaws.com" +
"\n" +
"\n" +
"host" +
"\n" +
"UNSIGNED-PAYLOAD";
var bytes = utf8.encode(_canonicalRequest);
var _stringToSign = "AWS4-HMAC-SHA256" +
"\n" +
"20130524T000000Z" +
"\n" +
"20130524/us-east-1/s3/aws4_request" +
"\n" +
sha256.convert(bytes).toString();
print("String to sign: $_stringToSign");
// HEX und HMAC signature
List<int> _dateKey = utf8.encode("AWS4" + testSecretkey);
List<int> _dateMsg = utf8.encode(_testDateymd.toString());
Hmac dateHmac = Hmac(sha256, _dateKey);
Digest dateDigest = dateHmac.convert(_dateMsg);
String _dateRegionKey = dateDigest.toString();
List<int> _dateRegionMsg = utf8.encode(_testRegion.toString());
Hmac dateRegionHmac = Hmac(sha256, hex.decode(_dateRegionKey.toString()));
Digest dateRegionDigest = dateRegionHmac.convert(_dateRegionMsg);
String _dateRegionServiceKey = dateRegionDigest.toString();
List<int> _dateRegionServiceMsg = utf8.encode(_awsService.toString());
Hmac dateRegionServiceHmac =
Hmac(sha256, hex.decode(_dateRegionServiceKey.toString()));
Digest dateRegionServiceDigest =
dateRegionServiceHmac.convert(_dateRegionServiceMsg);
String _signingKey = dateRegionServiceDigest.toString();
List<int> _signingKeyMsg = utf8.encode(_aws4Request.toString());
Hmac _signingKeyHmac = Hmac(sha256, hex.decode(_signingKey.toString()));
Digest _signingKeyDigest = _signingKeyHmac.convert(_signingKeyMsg);
print("signing key: $_signingKeyDigest");
var _signatureKey = _signingKeyDigest.toString();
List<int> _signatureKeyMsg = utf8.encode(_stringToSign);
Hmac _signatureHmac = Hmac(sha256, hex.decode(_signatureKey));
var _signature = _signatureHmac.convert(_signatureKeyMsg);
print("Signature: $_signature");
}
}
We are trying to call AWS API Gateway from C# Windows Service, for a background job. Which was supposed to trigger API Gateway periodically initialize request?
We used RestSharp to invoke API Endpoint, the class called AwsAuthenticator , which is inherited from RestSharp.Authenticators.IAuthenticator.
But when we invoke API Gateway we received with error as
"The request signature we calculated does not match the signature you provided. Check your AWS Secret Access Key and signing method. Consult the service documentation for details."
using System;
using System.Collections.Generic;
using System.Linq;
using System.Security.Cryptography;
namespace ConsoleApp3
{
public class AwsAuthenticator : RestSharp.Authenticators.IAuthenticator
{
public string AccessKeyId { get; }
public string AccessKeySecret { get; }
public string Region { get; }
public AwsAuthenticator(string accessKeyId, string accessKeySecret, string region)
{
AccessKeyId = accessKeyId;
AccessKeySecret = accessKeySecret;
Region = region;
}
private static HashSet<string> ignoredHeaders = new HashSet<string>() {
"authorization",
"content-length",
"content-type",
"user-agent"
};
public void Authenticate(RestSharp.IRestClient client, RestSharp.IRestRequest request)
{
DateTime signingDate = DateTime.UtcNow;
SetContentMd5(request);
SetContentSha256(request);
SetHostHeader(request, client);
SetDateHeader(request, signingDate);
SortedDictionary<string, string> headersToSign = GetHeadersToSign(request);
string signedHeaders = GetSignedHeaders(headersToSign);
string canonicalRequest = GetCanonicalRequest(client, request, headersToSign);
byte[] canonicalRequestBytes = System.Text.Encoding.UTF8.GetBytes(canonicalRequest);
string canonicalRequestHash = BytesToHex(ComputeSha256(canonicalRequestBytes));
string stringToSign = GetStringToSign(Region, signingDate, canonicalRequestHash);
byte[] signingKey = GenerateSigningKey(Region, signingDate);
byte[] stringToSignBytes = System.Text.Encoding.UTF8.GetBytes(stringToSign);
byte[] signatureBytes = SignHmac(signingKey, stringToSignBytes);
string signature = BytesToHex(signatureBytes);
string authorization = GetAuthorizationHeader(signedHeaders, signature, signingDate, Region);
request.AddHeader("Authorization", authorization);
}
public string GetCredentialString(DateTime signingDate, string region)
{
return AccessKeyId + "/" + GetScope(region, signingDate);
}
private string GetAuthorizationHeader(string signedHeaders, string signature, DateTime signingDate, string region)
{
return "AWS4-HMAC-SHA256 Credential=" + this.AccessKeyId + "/" + GetScope(region, signingDate) +
", SignedHeaders=" + signedHeaders + ", Signature=" + signature;
}
private string GetSignedHeaders(SortedDictionary<string, string> headersToSign)
{
return string.Join(";", headersToSign.Keys);
}
private byte[] GenerateSigningKey(string region, DateTime signingDate)
{
byte[] formattedDateBytes = System.Text.Encoding.UTF8.GetBytes(signingDate.ToString("yyyMMdd"));
byte[] formattedKeyBytes = System.Text.Encoding.UTF8.GetBytes("AWS4" + this.AccessKeySecret);
byte[] dateKey = SignHmac(formattedKeyBytes, formattedDateBytes);
byte[] regionBytes = System.Text.Encoding.UTF8.GetBytes(region);
byte[] dateRegionKey = SignHmac(dateKey, regionBytes);
byte[] serviceBytes = System.Text.Encoding.UTF8.GetBytes("execute-api");
byte[] dateRegionServiceKey = SignHmac(dateRegionKey, serviceBytes);
byte[] requestBytes = System.Text.Encoding.UTF8.GetBytes("aws4_request");
return SignHmac(dateRegionServiceKey, requestBytes);
}
private byte[] SignHmac(byte[] key, byte[] content)
{
HMACSHA256 hmac = new HMACSHA256(key);
hmac.Initialize();
return hmac.ComputeHash(content);
}
private string GetStringToSign(string region, DateTime signingDate, string canonicalRequestHash)
{
return "AWS4-HMAC-SHA256\n" +
signingDate.ToString("yyyyMMddTHHmmssZ") + "\n" +
GetScope(region, signingDate) + "\n" +
canonicalRequestHash;
}
private string GetScope(string region, DateTime signingDate)
{
string formattedDate = signingDate.ToString("yyyyMMdd");
return formattedDate + "/" + region + "/execute-api/aws4_request";
}
private byte[] ComputeSha256(byte[] body)
{
SHA256 sha256 = SHA256.Create();
return sha256.ComputeHash(body);
}
private string BytesToHex(byte[] checkSum)
{
return BitConverter.ToString(checkSum).Replace("-", string.Empty).ToLower();
}
public string PresignPostSignature(string region, DateTime signingDate, string policyBase64)
{
byte[] signingKey = this.GenerateSigningKey(region, signingDate);
byte[] stringToSignBytes = System.Text.Encoding.UTF8.GetBytes(policyBase64);
byte[] signatureBytes = SignHmac(signingKey, stringToSignBytes);
string signature = BytesToHex(signatureBytes);
return signature;
}
public string PresignURL(RestSharp.IRestClient client, RestSharp.IRestRequest request, int expires)
{
DateTime signingDate = DateTime.UtcNow;
string requestQuery = "";
string path = request.Resource;
requestQuery = "X-Amz-Algorithm=AWS4-HMAC-SHA256&";
requestQuery += "X-Amz-Credential="
+ this.AccessKeyId
+ Uri.EscapeDataString("/" + GetScope(Region, signingDate))
+ "&";
requestQuery += "X-Amz-Date="
+ signingDate.ToString("yyyyMMddTHHmmssZ")
+ "&";
requestQuery += "X-Amz-Expires="
+ expires
+ "&";
requestQuery += "X-Amz-SignedHeaders=host";
string canonicalRequest = GetPresignCanonicalRequest(client, request, requestQuery);
byte[] canonicalRequestBytes = System.Text.Encoding.UTF8.GetBytes(canonicalRequest);
string canonicalRequestHash = BytesToHex(ComputeSha256(canonicalRequestBytes));
string stringToSign = GetStringToSign(Region, signingDate, canonicalRequestHash);
byte[] signingKey = GenerateSigningKey(Region, signingDate);
byte[] stringToSignBytes = System.Text.Encoding.UTF8.GetBytes(stringToSign);
byte[] signatureBytes = SignHmac(signingKey, stringToSignBytes);
string signature = BytesToHex(signatureBytes);
// Return presigned url.
return client.BaseUrl + path + "?" + requestQuery + "&X-Amz-Signature=" + signature;
}
private string GetPresignCanonicalRequest(RestSharp.IRestClient client, RestSharp.IRestRequest request, string requestQuery)
{
LinkedList<string> canonicalStringList = new LinkedList<string>();
canonicalStringList.AddLast(request.Method.ToString());
string path = request.Resource;
if (!path.StartsWith("/"))
{
path = "/" + path;
}
canonicalStringList.AddLast(path);
canonicalStringList.AddLast(requestQuery);
canonicalStringList.AddLast("host:" + client.BaseUrl.Host);
canonicalStringList.AddLast("");
canonicalStringList.AddLast("host");
canonicalStringList.AddLast("UNSIGNED-PAYLOAD");
return string.Join("\n", canonicalStringList);
}
private string GetCanonicalRequest(RestSharp.IRestClient client, RestSharp.IRestRequest request,
SortedDictionary<string, string> headersToSign)
{
LinkedList<string> canonicalStringList = new LinkedList<string>();
canonicalStringList.AddLast(request.Method.ToString());
string[] path = request.Resource.Split(new char[] { '?' }, 2);
if (!path[0].StartsWith("/"))
{
path[0] = "/" + path[0];
}
canonicalStringList.AddLast(path[0]);
string query = "";
if (path.Length == 2)
{
var parameterString = path[1];
var parameterList = parameterString.Split('&');
SortedSet<string> sortedQueries = new SortedSet<string>();
foreach (string individualParameterString in parameterList)
{
if (individualParameterString.Contains('='))
{
string[] splitQuery = individualParameterString.Split(new char[] { '=' }, 2);
sortedQueries.Add(splitQuery[0] + "=" + splitQuery[1]);
}
else
{
sortedQueries.Add(individualParameterString + "=");
}
}
query = string.Join("&", sortedQueries);
}
canonicalStringList.AddLast(query);
foreach (string header in headersToSign.Keys)
{
canonicalStringList.AddLast(header + ":" + headersToSign[header]);
}
canonicalStringList.AddLast("");
canonicalStringList.AddLast(string.Join(";", headersToSign.Keys));
if (headersToSign.Keys.Contains("x-amz-content-sha256"))
{
canonicalStringList.AddLast(headersToSign["x-amz-content-sha256"]);
}
else
{
canonicalStringList.AddLast("e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855");
}
return string.Join("\n", canonicalStringList);
}
private SortedDictionary<string, string> GetHeadersToSign(RestSharp.IRestRequest request)
{
var headers = request.Parameters.Where(p => p.Type.Equals(RestSharp.ParameterType.HttpHeader)).ToList();
SortedDictionary<string, string> sortedHeaders = new SortedDictionary<string, string>();
foreach (var header in headers)
{
string headerName = header.Name.ToLower();
string headerValue = header.Value.ToString();
if (!ignoredHeaders.Contains(headerName))
{
sortedHeaders.Add(headerName, headerValue);
}
}
return sortedHeaders;
}
private void SetDateHeader(RestSharp.IRestRequest request, DateTime signingDate)
{
request.AddHeader("x-amz-date", signingDate.ToString("yyyyMMddTHHmmssZ"));
}
private void SetHostHeader(RestSharp.IRestRequest request, RestSharp.IRestClient client)
{
request.AddHeader("Host", client.BaseUrl.Host + (client.BaseUrl.Port != 80 ? ":" + client.BaseUrl.Port : string.Empty));
}
private void SetContentSha256(RestSharp.IRestRequest request)
{
if (request.Method == RestSharp.Method.PUT || request.Method.Equals(RestSharp.Method.POST))
{
var bodyParameter = request.Parameters.Where(p => p.Type.Equals(RestSharp.ParameterType.RequestBody)).FirstOrDefault();
if (bodyParameter == null)
{
request.AddHeader("x-amz-content-sha256", "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855");
return;
}
byte[] body = null;
if (bodyParameter.Value is string)
{
body = System.Text.Encoding.UTF8.GetBytes(bodyParameter.Value as string);
}
if (bodyParameter.Value is byte[])
{
body = bodyParameter.Value as byte[];
}
if (body == null)
{
body = new byte[0];
}
SHA256 sha256 = System.Security.Cryptography.SHA256.Create();
byte[] hash = sha256.ComputeHash(body);
string hex = BitConverter.ToString(hash).Replace("-", string.Empty).ToLower();
request.AddHeader("x-amz-content-sha256", hex);
}
else
{
request.AddHeader("x-amz-content-sha256", "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855");
}
}
private void SetContentMd5(RestSharp.IRestRequest request)
{
if (request.Method == RestSharp.Method.PUT || request.Method.Equals(RestSharp.Method.POST))
{
var bodyParameter = request.Parameters.Where(p => p.Type.Equals(RestSharp.ParameterType.RequestBody)).FirstOrDefault();
if (bodyParameter == null)
{
return;
}
byte[] body = null;
if (bodyParameter.Value is string)
{
body = System.Text.Encoding.UTF8.GetBytes(bodyParameter.Value as string);
}
if (bodyParameter.Value is byte[])
{
body = bodyParameter.Value as byte[];
}
if (body == null)
{
body = new byte[0];
}
MD5 md5 = MD5.Create();
byte[] hash = md5.ComputeHash(body);
string base64 = Convert.ToBase64String(hash);
request.AddHeader("Content-MD5", base64);
}
}
}
////////////////////////
public class MainClass
{
public void Execute()
{
var client = new RestClient("https://nm47849kod.execute-api.ap-southeast1.amazonaws.com/samplegateway/");
var request = new RestRequest("/", Method.POST);
var postData = new { Mode = 4 };
request.AddParameter("application/json",JsonConvert.SerializeObject(postData),ParameterType.RequestBody); AwsAuthenticator awsAuthenticator = new AwsAuthenticator("AccessKeyXXXXX", "SECKEYxxxx12313123123123123", "apsoutheast-1");
awsAuthenticator.Authenticate(client,request);
IRestResponse response = client.Execute(request);
var content = response.Content; // raw content as string
Console.WriteLine(content);
Console.ReadLine();
}
}
Error Details:
{"message":"The request signature we calculated does not match the signature you provided. Check your AWS Secret Access Key and signing method. Consult the service documentation for details.\n\nThe Canonical String for this request should have been\n'POST\n/samplegateway/\n\ncontent-md5:rkT7BbUvFInBgrPCuA0UZw==\nhost:nm47849kod.execute-api.ap-southeast-1.amazonaws.com\nx-amz-content-sha256:0318f62547c9078687e73f987ec26fa557047b67f54bb99b8047c950990ae42c\nx-amz-date:20190601T102835Z\n\ncontent-md5;host;x-amz-content-sha256;x-amz-date\n0318f62547c9078687e73f987ec26fa557047b67f54bb99b8047c950990ae42c'\n\nThe String-to-Sign should have been\n'AWS4-HMAC-SHA256\n20190601T102835Z\n20190601/ap-southeast-1/execute-api/aws4_request\n8f89bd5010655fb26a8de5e29d48d6129ac7875e5eb6bc2faeb8e41864b4d49e'\n"}.
We identified the problem.
Below is the working code and this resolves my issue. I am sharing this so that the group can get benefitted. The above class is entirely rewritten and when invoked it worked.
public class ApiRequest
{
private const string ServiceName = "execute-api";
private const string Algorithm = "AWS4-HMAC-SHA256";
private const string ContentType = "application/json";
private const string SignedHeaders = "content-type;host;x-amz-date;x-api-key";
private const string DateTimeFormat = "yyyyMMddTHHmmssZ";
private const string DateFormat = "yyyyMMdd";
public AwsApiGatewayRequest AwsApiGatewayRequest;
public ApiRequest(AwsApiGatewayRequest request)
{
AwsApiGatewayRequest = request;
if (string.IsNullOrEmpty(AwsApiGatewayRequest.RequestMethod))
AwsApiGatewayRequest.RequestMethod = "POST";
if (string.IsNullOrEmpty(AwsApiGatewayRequest.xApiKey))
AwsApiGatewayRequest.xApiKey = "";
}
public WebResponse GetPostResponse()
{
var request = GetPostRequest();
return request.GetResponse();
}
public WebRequest GetPostRequest()
{
string hashedRequestPayload = CreateRequestPayload(AwsApiGatewayRequest.JsonData);
string authorization = Sign(hashedRequestPayload, AwsApiGatewayRequest.RequestMethod, AwsApiGatewayRequest.AbsolutePath, AwsApiGatewayRequest.QueryString);
string requestDate = DateTime.UtcNow.ToString(DateTimeFormat);
var webRequest = WebRequest.Create($"https://{AwsApiGatewayRequest.Host}{AwsApiGatewayRequest.AbsolutePath}");
webRequest.Timeout = AwsApiGatewayRequest.RequestTimeout.HasValue ? AwsApiGatewayRequest.RequestTimeout.Value : 50000;
webRequest.Method = AwsApiGatewayRequest.RequestMethod;
webRequest.ContentType = ContentType;
webRequest.Headers.Add("X-Amz-date", requestDate);
webRequest.Headers.Add("Authorization", authorization);
webRequest.Headers.Add("x-amz-content-sha256", hashedRequestPayload);
if (!string.IsNullOrEmpty(AwsApiGatewayRequest.AdditionalHeaders))
{
// parse apart and apply the additional headers
string[] headers = AwsApiGatewayRequest.AdditionalHeaders.Split(';');
foreach (string header in headers)
{
var headervalue = header.Split('=');
if (headervalue.Count() == 2)
webRequest.Headers.Add(headervalue[0], headervalue[1]);
}
}
if (!string.IsNullOrEmpty(AwsApiGatewayRequest.SessionToken))
webRequest.Headers.Add("X-Amz-Security-Token", AwsApiGatewayRequest.SessionToken);
webRequest.ContentLength = AwsApiGatewayRequest.JsonData.Length;
var encoding = new ASCIIEncoding();
var data = encoding.GetBytes(AwsApiGatewayRequest.JsonData);
using (var newStream = webRequest.GetRequestStream())
{
newStream.Write(data, 0, data.Length);
newStream.Close();
}
return webRequest;
}
private string CreateRequestPayload(string jsonString)
{
return HexEncode(Hash(ToBytes(jsonString)));
}
private string Sign(string hashedRequestPayload, string requestMethod, string canonicalUri, string canonicalQueryString)
{
var currentDateTime = DateTime.UtcNow;
var dateStamp = currentDateTime.ToString(DateFormat);
var requestDate = currentDateTime.ToString(DateTimeFormat);
var credentialScope = $"{dateStamp}/{AwsApiGatewayRequest.RegionName}/{ServiceName}/aws4_request";
var headers = new SortedDictionary<string, string> {
{ "content-type", ContentType },
{ "host", AwsApiGatewayRequest.Host },
{ "x-amz-date", requestDate },
{ "x-api-key", AwsApiGatewayRequest.xApiKey }
};
var canonicalHeaders = string.Join("\n", headers.Select(x => x.Key.ToLowerInvariant() + ":" + x.Value.Trim())) + "\n";
// Task 1: Create a Canonical Request For Signature Version 4
var canonicalRequest = $"{requestMethod}\n{canonicalUri}\n{canonicalQueryString}\n{canonicalHeaders}\n{SignedHeaders}\n{hashedRequestPayload}";
var hashedCanonicalRequest = HexEncode(Hash(ToBytes(canonicalRequest)));
// Task 2: Create a String to Sign for Signature Version 4
var stringToSign = $"{Algorithm}\n{requestDate}\n{credentialScope}\n{hashedCanonicalRequest}";
// Task 3: Calculate the AWS Signature Version 4
var signingKey = GetSignatureKey(AwsApiGatewayRequest.SecretKey, dateStamp, AwsApiGatewayRequest.RegionName, ServiceName);
var signature = HexEncode(HmacSha256(stringToSign, signingKey));
// Task 4: Prepare a signed request
// Authorization: algorithm Credential=access key ID/credential scope, SignedHeadaers=SignedHeaders, Signature=signature
var authorization = $"{Algorithm} Credential={AwsApiGatewayRequest.AccessKey}/{dateStamp}/{AwsApiGatewayRequest.RegionName}/{ServiceName}/aws4_request, SignedHeaders={SignedHeaders}, Signature={signature}";
return authorization;
}
private byte[] GetSignatureKey(string key, string dateStamp, string regionName, string serviceName)
{
var kDate = HmacSha256(dateStamp, ToBytes("AWS4" + key));
var kRegion = HmacSha256(regionName, kDate);
var kService = HmacSha256(serviceName, kRegion);
return HmacSha256("aws4_request", kService);
}
private byte[] ToBytes(string str)
{
return Encoding.UTF8.GetBytes(str.ToCharArray());
}
private string HexEncode(byte[] bytes)
{
return BitConverter.ToString(bytes).Replace("-", string.Empty).ToLowerInvariant();
}
private byte[] Hash(byte[] bytes)
{
return SHA256.Create().ComputeHash(bytes);
}
private byte[] HmacSha256(string data, byte[] key)
{
return new HMACSHA256(key).ComputeHash(ToBytes(data));
}
}
Execution Parameter:
var request = new AwsApiGatewayRequest()
{
RegionName = "",
Host = ,
AccessKey = "",
SecretKey = "",
RequestMethod = "POST",
AbsolutePath = ,
JsonData = "{\"Mode\":\"4\"}",
SessionToken = ""
};//Invoke this using RestClient...
The problem here is we failed to add an additional header which was required by AWS. In this version, we have added hence it rectified.
Thanks for your support.
I've spent some time looking at all solutions but something still seems off.. The encrypted string I see in .Net does not match the output I see in Cryto-JS. What could be wrong?
public static void Encrypt()
{
string toEncrypt = "123456";
string key = "hello";
TripleDESCryptoServiceProvider tdes = new TripleDESCryptoServiceProvider();
MD5CryptoServiceProvider hashmd5 = new MD5CryptoServiceProvider();
byte[] keyArray = hashmd5.ComputeHash(UnicodeEncoding.Unicode.GetBytes(key));
tdes.Key = keyArray;
tdes.Mode = CipherMode.ECB;
ICryptoTransform cTransform = tdes.CreateEncryptor();
byte[] toEncryptArray = UnicodeEncoding.Unicode.GetBytes(toEncrypt);
byte[] resultArray = cTransform.TransformFinalBlock(toEncryptArray, 0, toEncryptArray.Length);
string finalString = Convert.ToBase64String(resultArray);
Console.WriteLine("Output encrypted .Net: " + finalString);
}
and the equivalent Javascript using crypto-js is
Encrypt = () => {
var CryptoJS = require('crypto-js');
var text = '123456'
var key = "hello";
key = CryptoJS.enc.Utf16LE.parse(key);
key = CryptoJS.MD5(key)
var options = {
mode: CryptoJS.mode.ECB,
};
var textWordArray = CryptoJS.enc.Utf16LE.parse(text);
var encrypted = CryptoJS.TripleDES.encrypt(textWordArray, key, options);
var base64String = encrypted.toString();
console.log('Output JS Encrypted: ' + base64String);
}
I get yGOnLhoVpIHQOCbAn51FTA== in .Net and d5Lg8k8cz68T6akDI0KQrA== in crypto-js.
I have fixed this issue. A console log inside tripledes.js (CryptoJS package) revealed that I was missing 64 more bits in the key after MD5 Hash.
this._des1 = DES.createEncryptor(WordArray.create(keyWords.slice(0, 2)));
this._des2 = DES.createEncryptor(WordArray.create(keyWords.slice(2, 4)));
this._des3 = DES.createEncryptor(WordArray.create(keyWords.slice(4, 6)));
key.words length was 4 instead of 6. So, des3 had an empty wordarray. Solution was to push value of index 0 and 1 into the key word array. With length now being 6, des3 gets the value of des1. i.e keyWords.slice(0, 2) = keyWords.slice(4, 6).
Encrypt = () => {
var CryptoJS = require('crypto-js');
var text = '123456'
var key = "hello";
key = CryptoJS.enc.Utf16LE.parse(key);
key = CryptoJS.MD5(key)
key.words.push(key.words[0], key.words[1]) // FIX FIX FIX
var options = {
mode: CryptoJS.mode.ECB,
};
var textWordArray = CryptoJS.enc.Utf16LE.parse(text);
var encrypted = CryptoJS.TripleDES.encrypt(textWordArray, key, options);
var base64String = encrypted.toString();
console.log('Output JS Encrypted: ' + base64String);
}
I'm trying to generate the signature in Google Scripts without much luck.
My code is as follows:
function getAwsData(){
var AWS_SECRET = '[AWS_SECRET]';
var expiresDt = Math.floor(Date.now() / 1000) + (60 * 60 * 24);
var path = '/MYFOLDER/ACTIVATION.csv'
var stringToSign = 'GET\n\n\n' + expiresDt + '\n' + path;
var hmac = Utilities.computeHmacSignature(Utilities.MacAlgorithm.HMAC_SHA_1, AWS_SECRET,stringToSign, Utilities.Charset.UTF_8);
var signature = encodeURIComponent(Utilities.base64Encode(hmac));
var response = UrlFetchApp.fetch("http://s3-ap-southeast-2.amazonaws.com/MYFOLDER/ACTIVATION.csv?AWSAccessKeyId=[AWS_ACCESS_ID]&Expires="+expiresDt+"&Signature="+signature);
var csvData = Utilities.parseCsv(response, ",");
console.log(csvData);
}
Can someone please see where I've gone wrong? I've followed the document here: http://s3.amazonaws.com/doc/s3-developer-guide/RESTAuthentication.html
It appears that I'm signing the hmac incorrectly.
var hmac = Utilities.computeHmacSignature(Utilities.MacAlgorithm.HMAC_SHA_1, stringToSign, AWS_SECRET,Utilities.Charset.UTF_8);
I am making a merge code to take data from my spreadsheet and populate merge tags in a Google Doc. The part of the code I am unable to write correctly is the part that writes the tags back to the Google Doc. I have been able to locate the tags but the code doesn't replace them.
Here is what I've written so far.
function mergeApplication() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheet = ss.getSheetByName("Merge Data");
var range = sheet.getActiveRange();
var formSheet = ss.getSheetByName("Form Responses");
var lastRow = formSheet.getLastRow();
var lastColumn = sheet.getMaxColumns();
function checkAndComplete() {
var urlColumn = lastColumn;
var checkColumn = (urlColumn - 1);
var checkRange = sheet.getRange(2, checkColumn, (lastRow - 1), 1);
var check = checkRange.getBackgrounds();
var red = "#ff0404";
var yellow = "#ffec0a";
var green = "#3bec3b";
for (var i = 0; i < check.length; i++) {
if (check[i] == green) {
continue;
} else {
var statusCell = sheet.getRange((i+2), checkColumn, 1, 1);
var urlCell = sheet.getRange((i+2), urlColumn, 1, 1);
var dataRow = sheet.getRange((i+2), 1, 1, (lastColumn - 2));
function mergeTasks() {
function docCreator() {
// var templateConditionRange = sheet.getRange((i+2), column);
// var templateConditionCheck = templateConditionRange.getValues();
var docTemplate1 = DriveApp.getFileById(id);
// var docTemplate2 = DriveApp.getFileById(id);
// var docTemplate3 = DriveApp.getFileById(id);
var folderDestination = DriveApp.getFolderById(id);
var clientName = sheet.getRange((i+2), 3).getValue();
var date = sheet.getRange((i+2), 1).getValue();
// if (templateConditionCheck[i] == "") {
var docToUse = docTemplate1;
// }
// if (templateConditionCheck[i] == "") {
// var docToUse = docTemplate2;
// }
// if (templateConditionCheck[i] == "") {
// var docToUse = docTemplate3;
// }
var docName = "Merge Tester Doc for " + clientName + " [" + date + "]";
var docCopy = docToUse.makeCopy(docName, folderDestination);
var docId = docCopy.getId();
var docURL = DriveApp.getFileById(docId).getUrl();
var docToSend = DriveApp.getFileById(docId);
var docBody = DocumentApp.openById(docId).getBody().getText();
function tagReplace() {
var taggedArray = [docBody.match(/\<{2}[\w\d\S]+\>{2}/g)];
var headerArray = [sheet.getRange(1, 1, 1, (lastColumn - 2)).getValues()];
var dataArray = [dataRow.getValues()];
var strippedArray = [];
Logger.log("The preliminary length of taggedArray is " + taggedArray.length);
Logger.log(taggedArray);
function tagStrip() {
for (var t = 0; t < taggedArray.length; t++) {
var strippedString = taggedArray[t].slice(2, -3).toString();
strippedArray.push(strippedString);
Logger.log("The current strippedArray length is " + strippedArray.length);
}
Logger.log("The final strippedArray length is " + strippedArray.length);
Logger.log("The final taggedArray length is " + taggedArray.length);
Logger.log("The final, completed strippedArray is " + strippedArray);
}
function dataMatch() {
for (var s = 0; s < strippedArray.length;) {
for (var h = 0; h < headerArray.length;) {
if (strippedArray[s] == headerArray[h]) {
docBody.replaceText(taggedArray[s].String(), dataArray[h].String());
h=0;
s++;
} else {
h++;
}
}
}
}
tagStrip;
dataMatch;
}
function emailCreator() {
var emailTag = sheet.getRange((i+2), (urlColumn - 2)).getValue();
var emailBody = HtmlService.createHtmlOutputFromFile("Email Template").getContent();
var personalizers = clientName + " [" + date + "]";
var subject = "Merge Tester Email for " + personalizers;
MailApp.sendEmail(emailTag, subject, emailBody, {
name: "Christopher Anderson",
attachments: [docToSend],
html: emailBody,
});
}
tagReplace();
statusCell.setBackground(yellow);
emailCreator();
urlCell.setValue(docURL)
}
statusCell.setBackground(red);
docCreator();
statusCell.setBackground(green);
}
mergeTasks();
}
}
}
checkAndComplete();
}
The problem section is here:
function tagReplace() {
var taggedArray = [docBody.match(/\<{2}[\w\d\S]+\>{2}/g)];
var headerArray = [sheet.getRange(1, 1, 1, (lastColumn - 2)).getValues()];
var dataArray = [dataRow.getValues()];
var strippedArray = new Array();
Logger.log("The preliminary length of taggedArray is " + taggedArray.length);
Logger.log(taggedArray);
function tagStrip() {
for (var t = 0; t < taggedArray.length; t++) {
var strippedString = taggedArray[t].slice(2, -3).toString();
strippedArray.push(strippedString);
Logger.log("The current strippedArray length is " + strippedArray.length);
}
Logger.log("The final strippedArray length is " + strippedArray.length);
Logger.log("The final taggedArray length is " + taggedArray.length);
Logger.log("The final, completed strippedArray is " + strippedArray);
}
function dataMatch() {
for (var s = 0; s < strippedArray.length;) {
for (var h = 0; h < headerArray.length;) {
if (strippedArray[s] == headerArray[h]) {
docBody.replaceText(taggedArray[s].String(), dataArray[h].String());
h=0;
s++;
} else {
h++;
}
}
}
}
tagStrip;
dataMatch;
}
It doesn't even log anything having to do with strippedArray.
It seems to be skipping over that section entirely.
Am I using the correct method of completing this task and/or is there a simpler way of doing it?
It's worth mentioning that my tags in the doc have 2 "<>" around them. That's the reason for my RegEx looking how it does.
Also, when logging the .length of taggedArray, it returns a value of 1.
You never actually call tagStrip which is supposed to work on strippedArray.
You declare it with function tagStrip(){} and later you reference the function with tagStrip; but you never actually call it. The same is happening with dataMatch.
Try calling the two functions by writing
tagStrip();
dataMatch();
If you don't include the parentheses you don't call it you just run the function Objects as statements.
Here is a portion of code I use in my add-on Simply Send, I use the same <<>> merge tags.
//copy template
var mDocDrive = doc.makeCopy(title, folder);
var mDoc = DocumentApp.openById(mDocDrive.getId());
var mDocDriveApp = DriveApp.getFileById(mDoc.getId());
var docToAttach = mDoc.getUrl();
var driveToShare = mDocDriveApp;
// replace text inside template
var body = mDoc.getBody();
body.replaceText("<<SS Title>>", title);
body.replaceText("<<timestamp>>", lastR.timestamp);
body.replaceText("<<username>>", lastR.email);
for (i in lastR.theResponses){
var cur = lastR.theResponses[i];
var name = cur.title;
name = name.replace(/[\-\[\]\/\{\}\(\)\*\+\?\.\\\^\$\|]/g, "\\$&"); // this will allow fields that include special characters.
var response = cur.response;
var searchPattern = "<<"+name+">>";
body.replaceText(searchPattern, response);
}
// this will replace any unused tags with nothing.
var ques = getQuestionList();
for (j in ques){
var curq = ques[j].name;
curq = curq.replace(/[\-\[\]\/\{\}\(\)\*\+\?\.\\\^\$\|]/g, "\\$&");
var searchPattern2 = "<<"+curq+">>";
body.replaceText(searchPattern2, "");
}
mDoc.saveAndClose();
//create pdf
if (mmDefaults.shareAs == "pdf"){
// uncomment if you want to make the pdf in the merge folder
var asPdf = mDoc.getAs('application/pdf');
asPdf.setName(mDoc.getName()+ ".pdf");
var pdf = DriveApp.createFile(asPdf);
folder.addFile(pdf);
DriveApp.removeFile(pdf);
mDocDriveApp.setTrashed(true);
var docToAttach = pdf;
driveToShare = pdf;
}