03-02-20 12:55 PM
04-02-20 08:35 AM
04-02-20 08:42 AM
04-02-20 12:52 PM
04-02-20 02:44 PM
05-02-20 10:24 AM
// SHA256 hash of an empty request body public string EMPTY_BODY_SHA256 = "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855"; public string SCHEME = "AWS4"; public string ALGORITHM = "HMAC-SHA256"; public string TERMINATOR = "aws4_request"; // format strings for the date/time and date stamps required during signing public string ISO8601BasicFormat = "yyyyMMddTHHmmssZ"; public string DateStringFormat = "yyyyMMdd"; // some common x-amz-* parameters string X_Amz_Algorithm = "X-Amz-Algorithm"; string X_Amz_Credential = "X-Amz-Credential"; string X_Amz_SignedHeaders = "X-Amz-SignedHeaders"; string X_Amz_Date = "X-Amz-Date"; string X_Amz_Signature = "X-Amz-Signature"; string X_Amz_Expires = "X-Amz-Expires"; string X_Amz_Content_SHA256 = "X-Amz-Content-SHA256"; string X_Amz_Decoded_Content_Length = "X-Amz-Decoded-Content-Length"; string X_Amz_Meta_UUID = "X-Amz-Meta-UUID"; // the name of the keyed hash algorithm used in signing string HMACSHA256 = "HMACSHA256"; Regex CompressWhitespaceRegex = new Regex("\\s+"); string strAction = "POST"; Uri uri; HashAlgorithm CanonicalRequestHashAlgorithm = HashAlgorithm.Create("SHA-256"); public string ComputeSignature(IDictionary<string, string> headers, string dateTimeStamp, string queryParameters, string bodyHash, string awsAccessKey, string awsSecretKey, string awsUri, string awsRegion, string awsService) { // 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; // update the headers with required 'x-amz-date' and 'host' values headers.Add(X_Amz_Date, dateTimeStamp); uri = new Uri(awsUri); var hostHeader = uri.Host; if (!uri.IsDefaultPort) hostHeader += ":" + uri.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); // canonicalize the various components of the request var canonicalRequest = CanonicalizeRequest(uri, strAction, null, canonicalizedHeaderNames, canonicalizedHeaders, bodyHash); // 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, awsRegion, awsService, TERMINATOR); stringToSign.AppendFormat("{0}-{1}\n{2}\n{3}\n", SCHEME, ALGORITHM, dateTimeStamp, scope); stringToSign.Append(ToHexString(canonicalRequestHashBytes, true)); // compute the signing key var kha = KeyedHashAlgorithm.Create(HMACSHA256); kha.Key = DeriveSigningKey(HMACSHA256, awsSecretKey, awsRegion, dateStamp, awsService); // compute the AWS4 signature and return it var signature = kha.ComputeHash(Encoding.UTF8.GetBytes(stringToSign.ToString())); var signatureString = ToHexString(signature, true); //Console.WriteLine("\nSignature:\n{0}", 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(); return authorization; } public string CanonicalizeRequest(Uri endpointUri, string httpMethod, string queryParameters, string canonicalizedHeaderNames, string canonicalizedHeaders, string bodyHash) { var canonicalRequest = new StringBuilder(); canonicalRequest.AppendFormat("{0}\n", httpMethod); canonicalRequest.AppendFormat("{0}\n", CanonicalResourcePath(endpointUri)); canonicalRequest.AppendFormat("{0}\n", queryParameters); canonicalRequest.AppendFormat("{0}\n", canonicalizedHeaders); canonicalRequest.AppendFormat("{0}\n", canonicalizedHeaderNames); canonicalRequest.Append(bodyHash); return canonicalRequest.ToString(); } public string CanonicalResourcePath(Uri endpointUri) { if (string.IsNullOrEmpty(endpointUri.AbsolutePath)) return "/"; // encode the path per RFC3986 return UrlEncode(endpointUri.AbsolutePath, true); } public byte[] DeriveSigningKey(string algorithm, string awsSecretAccessKey, string awsRegion, string date, string awsService) { string ksecretPrefix = SCHEME; char[] ksecret = null; ksecret = (ksecretPrefix + awsSecretAccessKey).ToCharArray(); byte[] hashDate = ComputeKeyedHash(algorithm, Encoding.UTF8.GetBytes(ksecret), Encoding.UTF8.GetBytes(date)); byte[] hashRegion = ComputeKeyedHash(algorithm, hashDate, Encoding.UTF8.GetBytes(awsRegion)); byte[] hashService = ComputeKeyedHash(algorithm, hashRegion, Encoding.UTF8.GetBytes(awsService)); return ComputeKeyedHash(algorithm, hashService, Encoding.UTF8.GetBytes(TERMINATOR)); } public byte[] ComputeKeyedHash(string algorithm, byte[] key, byte[] data) { var kha = KeyedHashAlgorithm.Create(algorithm); kha.Key = key; return kha.ComputeHash(data); } public string CanonicalizeHeaderNames(IDictionary<string, string> headers) { var headersToSign = new List<string>(headers.Keys); headersToSign.Sort(StringComparer.OrdinalIgnoreCase); var sb = new StringBuilder(); foreach (var header in headersToSign) { if (sb.Length > 0) sb.Append(";"); sb.Append(header.ToLower()); } return sb.ToString(); } public string CanonicalizeHeaders(IDictionary<string, string> headers) { if (headers == null || headers.Count == 0) return string.Empty; // step1: sort the headers into lower-case format; we create a new // map to ensure we can do a subsequent key lookup using a lower-case // key regardless of how 'headers' was created. var sortedHeaderMap = new SortedDictionary<string, string>(); foreach (var header in headers.Keys) { sortedHeaderMap.Add(header.ToLower(), headers[header]); } // step2: form the canonical header:value entries in sorted order. // Multiple white spaces in the values should be compressed to a single // space. var sb = new StringBuilder(); foreach (var header in sortedHeaderMap.Keys) { var headerValue = CompressWhitespaceRegex.Replace(sortedHeaderMap[header], " "); sb.AppendFormat("{0}:{1}\n", header, headerValue.Trim()); } return sb.ToString(); } public string UrlEncode(string data, bool isPath = false) { // The Set of accepted and valid Url characters per RFC3986. Characters outside of this set will be encoded. const string validUrlCharacters = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-_.~"; var encoded = new StringBuilder(data.Length * 2); string unreservedChars = String.Concat(validUrlCharacters, (isPath ? "/:" : "")); foreach (char symbol in System.Text.Encoding.UTF8.GetBytes(data)) { if (unreservedChars.IndexOf(symbol) != -1) encoded.Append(symbol); else encoded.Append("%").Append(String.Format("{0:X2}", (int)symbol)); } return encoded.ToString(); } public string ToHexString(byte[] data, bool lowercase) { var sb = new StringBuilder(); for (var i = 0; i < data.Length; i++) { sb.Append(data[i].ToString(lowercase ? "x2" : "X2")); } return sb.ToString(); }
string strBody = RequestBody; // precompute hash of the body content var contentHash = CanonicalRequestHashAlgorithm.ComputeHash(Encoding.UTF8.GetBytes(strBody)); var contentHashString = ToHexString(contentHash, true); var requestDateTime = DateTime.UtcNow; var dateTimeStamp = requestDateTime.ToString(ISO8601BasicFormat, CultureInfo.InvariantCulture); strAuthDate = dateTimeStamp; var headers = new Dictionary<string, string> { {"content-type", "application/x-amz-json-1.1"}, {"x-amz-target", AWSTarget} }; strAuthSignature = ComputeSignature(headers, dateTimeStamp, "", // no query parameters contentHashString, AccessID, SecretKey, AWSEndpoint, AWSRegion, AWSService);