in Dotnet core console app/AwsIOTMqttOverWebsockets/AwsIOTMqttOverWebsockets/Signers/AWS4SignerForPOST.cs [57:151]
public string ComputeSignature(IDictionary<string, string> headers,
string queryParameters,
string bodyHash,
string awsAccessKey,
string awsSecretKey)
{
// first get the date and time for the subsequent request, and convert to ISO 8601 format
// for use in signature generation
var requestDateTime = DateTime.UtcNow;
var dateTimeStamp = requestDateTime.ToString(ISO8601BasicFormat, CultureInfo.InvariantCulture);
// update the headers with required 'x-amz-date' and 'host' values
headers.Add(X_Amz_Date, dateTimeStamp);
var hostHeader = EndpointUri.Host;
if (!EndpointUri.IsDefaultPort)
hostHeader += ":" + EndpointUri.Port;
headers.Add("Host", hostHeader);
// canonicalize the headers; we need the set of header names as well as the
// names and values to go into the signature process
var canonicalizedHeaderNames = CanonicalizeHeaderNames(headers);
var canonicalizedHeaders = CanonicalizeHeaders(headers);
// if any query string parameters have been supplied, canonicalize them
// (note this sample assumes any required url encoding has been done already)
var canonicalizedQueryParameters = string.Empty;
if (!string.IsNullOrEmpty(queryParameters))
{
var paramDictionary = queryParameters.Split('&').Select(p => p.Split('='))
.ToDictionary(nameval => nameval[0],
nameval => nameval.Length > 1
? nameval[1] : "");
var sb = new StringBuilder();
var paramKeys = new List<string>(paramDictionary.Keys);
paramKeys.Sort(StringComparer.Ordinal);
foreach (var p in paramKeys)
{
if (sb.Length > 0)
sb.Append("&");
sb.AppendFormat("{0}={1}", p, paramDictionary[p]);
}
canonicalizedQueryParameters = sb.ToString();
}
// canonicalize the various components of the request
var canonicalRequest = CanonicalizeRequest(EndpointUri,
HttpMethod,
canonicalizedQueryParameters,
canonicalizedHeaderNames,
canonicalizedHeaders,
bodyHash);
Logger.LogDebug($"\nCanonicalRequest:\n{canonicalRequest}");
// generate a hash of the canonical request, to go into signature computation
var canonicalRequestHashBytes
= CanonicalRequestHashAlgorithm.ComputeHash(Encoding.UTF8.GetBytes(canonicalRequest));
// construct the string to be signed
var stringToSign = new StringBuilder();
var dateStamp = requestDateTime.ToString(DateStringFormat, CultureInfo.InvariantCulture);
var scope = string.Format("{0}/{1}/{2}/{3}",
dateStamp,
Region,
Service,
TERMINATOR);
stringToSign.AppendFormat("{0}-{1}\n{2}\n{3}\n", SCHEME, ALGORITHM, dateTimeStamp, scope);
stringToSign.Append(ToHexString(canonicalRequestHashBytes, true));
Logger.LogDebug($"\nStringToSign:\n{stringToSign}");
// compute the signing key
var kha = KeyedHashAlgorithm.Create(HMACSHA256);
kha.Key = DeriveSigningKey(HMACSHA256, awsSecretKey, Region, dateStamp, Service);
// compute the AWS4 signature and return it
var signature = kha.ComputeHash(Encoding.UTF8.GetBytes(stringToSign.ToString()));
var signatureString = ToHexString(signature, true);
Logger.LogDebug($"\nSignature:\n{signatureString}");
var authString = new StringBuilder();
authString.AppendFormat("{0}-{1} ", SCHEME, ALGORITHM);
authString.AppendFormat("Credential={0}/{1}, ", awsAccessKey, scope);
authString.AppendFormat("SignedHeaders={0}, ", canonicalizedHeaderNames);
authString.AppendFormat("Signature={0}", signatureString);
var authorization = authString.ToString();
Logger.LogDebug($"\nAuthorization:\n{authorization}");
return authorization;
}