From 5c41b9669ed5a5d3612f32fa810db196c6a87808 Mon Sep 17 00:00:00 2001 From: Robin Jones Date: Wed, 9 Aug 2023 20:48:51 +0100 Subject: [PATCH 01/14] Add sigv4 initial commit Before we get real dirty. --- .../AwsSignatureVersion4/SignerBase.cs | 231 ++++++++++++ .../SignerForAuthorizationHeader.cs | 137 ++++++++ .../SignerForChunkedUpload.cs | 329 ++++++++++++++++++ .../AwsSignatureVersion4/SignerForPOST.cs | 152 ++++++++ .../SignerForQueryParameterAuth.cs | 164 +++++++++ .../WebsocketConnectionClient.cs | 44 ++- .../nanoFramework.Aws.IoTCore.Devices.nfproj | 11 + .../packages.config | 2 + .../packages.lock.json | 12 + 9 files changed, 1077 insertions(+), 5 deletions(-) create mode 100644 nanoFramework.Aws.IoTCore.Devices/AwsSignatureVersion4/SignerBase.cs create mode 100644 nanoFramework.Aws.IoTCore.Devices/AwsSignatureVersion4/SignerForAuthorizationHeader.cs create mode 100644 nanoFramework.Aws.IoTCore.Devices/AwsSignatureVersion4/SignerForChunkedUpload.cs create mode 100644 nanoFramework.Aws.IoTCore.Devices/AwsSignatureVersion4/SignerForPOST.cs create mode 100644 nanoFramework.Aws.IoTCore.Devices/AwsSignatureVersion4/SignerForQueryParameterAuth.cs diff --git a/nanoFramework.Aws.IoTCore.Devices/AwsSignatureVersion4/SignerBase.cs b/nanoFramework.Aws.IoTCore.Devices/AwsSignatureVersion4/SignerBase.cs new file mode 100644 index 0000000..4bd588d --- /dev/null +++ b/nanoFramework.Aws.IoTCore.Devices/AwsSignatureVersion4/SignerBase.cs @@ -0,0 +1,231 @@ +using System; +using System.Collections; +using System.Net.Http.Headers; +using System.Security.Cryptography; +using System.Text; +using System.Text.RegularExpressions; + +namespace nanoFramework.Aws.SignatureVersion4 +{ + /// + /// Common methods and properties for all AWS Signature Version 4 signer variants + /// + public abstract class SignerBase + { + // SHA256 hash of an empty request body + public const string EMPTY_BODY_SHA256 = "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855"; + + public const string SCHEME = "AWS4"; + public const string ALGORITHM = "HMAC-SHA256"; + public const string TERMINATOR = "aws4_request"; + + // format strings for the date/time and date stamps required during signing + public const string ISO8601BasicFormat = "yyyyMMddTHHmmssZ"; + public const string DateStringFormat = "yyyyMMdd"; + + // some common x-amz-* parameters + public const string X_Amz_Algorithm = "X-Amz-Algorithm"; + public const string X_Amz_Credential = "X-Amz-Credential"; + public const string X_Amz_SignedHeaders = "X-Amz-SignedHeaders"; + public const string X_Amz_Date = "X-Amz-Date"; + public const string X_Amz_Signature = "X-Amz-Signature"; + public const string X_Amz_Expires = "X-Amz-Expires"; + public const string X_Amz_Content_SHA256 = "X-Amz-Content-SHA256"; + public const string X_Amz_Decoded_Content_Length = "X-Amz-Decoded-Content-Length"; + public const string X_Amz_Meta_UUID = "X-Amz-Meta-UUID"; + + // the name of the keyed hash algorithm used in signing + public const string HMACSHA256 = "HMACSHA256"; + + // request canonicalization requires multiple whitespace compression + protected static readonly Regex CompressWhitespaceRegex = new Regex("\\s+"); + + // algorithm used to hash the canonical request that is supplied to + // the signature computation + public static HashAlgorithm CanonicalRequestHashAlgorithm = HashAlgorithm.Create("SHA-256"); + + /// + /// The service endpoint, including the path to any resource. + /// + public Uri EndpointUri { get; set; } + + /// + /// The HTTP verb for the request, e.g. GET. + /// + public string HttpMethod { get; set; } + + /// + /// The signing name of the service, e.g. 's3'. + /// + public string Service { get; set; } + + /// + /// The system name of the AWS region associated with the endpoint, e.g. us-east-1. + /// + public string Region { get; set; } + + /// + /// Returns the canonical collection of header names that will be included in + /// the signature. For AWS Signature Version 4, all header names must be included in the process + /// in sorted canonicalized order. + /// + /// + /// The set of header names and values that will be sent with the request + /// + /// + /// The set of header names canonicalized to a flattened, ;-delimited string + /// + protected string CanonicalizeHeaderNames(IDictionary headers) + { + var headersToSign = new List(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(); + } + + /// + /// Computes the canonical headers with values for the request. + /// For AWS Signature Version 4, all headers must be included in the signing process. + /// + /// The set of headers to be encoded + /// Canonicalized string of headers with values + protected virtual string CanonicalizeHeaders(IDictionary 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(); + 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(); + } + + /// + /// Returns the canonical request string to go into the signer process; this + /// consists of several canonical sub-parts. + /// + /// + /// + /// + /// + /// The set of header names to be included in the signature, formatted as a flattened, ;-delimited string + /// + /// + /// + /// + /// Precomputed SHA256 hash of the request body content. For chunked encoding this + /// should be the fixed string ''. + /// + /// String representing the canonicalized request for signing + protected string CanonicalizeRequest(Uri endpointUri, + string httpMethod, + string queryParameters, + string canonicalizedHeaderNames, + string canonicalizedHeaders, + string bodyHash) + { + var canonicalRequest = new StringBuilder(); + + canonicalRequest.Append(string.Format("{0}\n", httpMethod)); + canonicalRequest.Append(string.Format("{0}\n", CanonicalResourcePath(endpointUri))); + canonicalRequest.Append(string.Format("{0}\n", queryParameters)); + + canonicalRequest.Append(string.Format("{0}\n", canonicalizedHeaders)); + canonicalRequest.Append(string.Format("{0}\n", canonicalizedHeaderNames)); + + canonicalRequest.Append(bodyHash); + + return canonicalRequest.ToString(); + } + + /// + /// Returns the canonicalized resource path for the service endpoint + /// + /// Endpoint to the service/resource + /// Canonicalized resource path for the endpoint + protected string CanonicalResourcePath(Uri endpointUri) + { + if (string.IsNullOrEmpty(endpointUri.AbsolutePath)) + return "/"; + + // encode the path per RFC3986 + return HttpHelpers.UrlEncode(endpointUri.AbsolutePath, true); + } + + /// + /// Compute and return the multi-stage signing key for the request. + /// + /// Hashing algorithm to use + /// The clear-text AWS secret key + /// The region in which the service request will be processed + /// Date of the request, in yyyyMMdd format + /// The name of the service being called by the request + /// Computed signing key + protected byte[] DeriveSigningKey(string algorithm, string awsSecretAccessKey, string region, string date, string service) + { + const 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(region)); + byte[] hashService = ComputeKeyedHash(algorithm, hashRegion, Encoding.UTF8.GetBytes(service)); + return ComputeKeyedHash(algorithm, hashService, Encoding.UTF8.GetBytes(TERMINATOR)); + } + + /// + /// Compute and return the hash of a data blob using the specified algorithm + /// and key + /// + /// Algorithm to use for hashing + /// Hash key + /// Data blob + /// Hash of the data + protected byte[] ComputeKeyedHash(string algorithm, byte[] key, byte[] data) + { + var kha = KeyedHashAlgorithm.Create(algorithm); + kha.Key = key; + return kha.ComputeHash(data); + } + + /// + /// Helper to format a byte array into string + /// + /// The data blob to process + /// If true, returns hex digits in lower case form + /// String version of the data + public static 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(); + } + } +} \ No newline at end of file diff --git a/nanoFramework.Aws.IoTCore.Devices/AwsSignatureVersion4/SignerForAuthorizationHeader.cs b/nanoFramework.Aws.IoTCore.Devices/AwsSignatureVersion4/SignerForAuthorizationHeader.cs new file mode 100644 index 0000000..d43f721 --- /dev/null +++ b/nanoFramework.Aws.IoTCore.Devices/AwsSignatureVersion4/SignerForAuthorizationHeader.cs @@ -0,0 +1,137 @@ +//using System; +//using System.Collections; +//using System.Globalization; +//using System.Net.Http; +//using System.Security.Cryptography; +//using System.Text; + +//namespace nanoFramework.Aws.SignatureVersion4 +//{ +// /// +// /// AWS Signature Version 4 signer for signing requests +// /// using an 'Authorization' header. +// /// +// public class SignerForAuthorizationHeader : SignerBase +// { +// /// +// /// Computes an Version 4 signature for a request, ready for inclusion as an +// /// 'Authorization' header. +// /// +// /// +// /// The request headers; 'Host' and 'X-Amz-Date' will be added to this set. +// /// +// /// +// /// Any query parameters that will be added to the endpoint. The parameters +// /// should be specified in canonical format. +// /// +// /// +// /// Precomputed SHA256 hash of the request body content; this value should also +// /// be set as the header 'X-Amz-Content-SHA256' for non-streaming uploads. +// /// +// /// +// /// The user's AWS Access Key. +// /// +// /// +// /// The user's AWS Secret Key. +// /// +// /// +// /// The computed authorization string for the request. This value needs to be set as the +// /// header 'Authorization' on the subsequent HTTP request. +// /// +// public string ComputeSignature(IDictionary 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(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; +// } +// } +//} diff --git a/nanoFramework.Aws.IoTCore.Devices/AwsSignatureVersion4/SignerForChunkedUpload.cs b/nanoFramework.Aws.IoTCore.Devices/AwsSignatureVersion4/SignerForChunkedUpload.cs new file mode 100644 index 0000000..f79af4d --- /dev/null +++ b/nanoFramework.Aws.IoTCore.Devices/AwsSignatureVersion4/SignerForChunkedUpload.cs @@ -0,0 +1,329 @@ +//using System; +//using System.Collections; +//using System.Globalization; +//using System.Net.Http; +//using System.Security.Cryptography; +//using System.Text; + + +//namespace nanoFramework.Aws.SignatureVersion4 +//{ +// /// +// /// AWS Signature Version 4 signer for signing 'chunked' uploads +// /// using an Authorization header. +// /// +// public class SignerForChunkedUpload : SignerBase +// { +// // SHA256 substitute marker used in place of x-amz-content-sha256 when employing +// // chunked uploads +// public const string STREAMING_BODY_SHA256 = "STREAMING-AWS4-HMAC-SHA256-PAYLOAD"; + +// static readonly string CLRF = "\r\n"; +// static readonly string CHUNK_STRING_TO_SIGN_PREFIX = "AWS4-HMAC-SHA256-PAYLOAD"; +// static readonly string CHUNK_SIGNATURE_HEADER = ";chunk-signature="; +// static readonly int SIGNATURE_LENGTH = 64; +// static byte[] FINAL_CHUNK = new byte[0]; + +// /// +// /// Tracks the previously computed signature value; for chunk 0 this will +// /// contain the signature included in the Authorization header. For subsequent +// /// chunks it contains the computed signature of the prior chunk. +// /// +// public string LastComputedSignature { get; private set; } + +// /// +// /// Date and time of the original signing computation, in ISO 8601 basic format, +// /// reused for each chunk +// /// +// public string DateTimeStamp { get; private set; } + +// /// +// /// The scope value of the original signing computation, reused for each chunk +// /// +// public string Scope { get; private set; } + +// /// +// /// The derived signing key used in the original signature computation and +// /// re-used for each chunk +// /// +// byte[] SigningKey { get; set; } + +// /// +// /// Computes an AWS4 signature for a request, ready for inclusion as an +// /// 'Authorization' header. +// /// +// /// +// /// The request headers; 'Host' and 'X-Amz-Date' will be added to this set. +// /// +// /// +// /// Any query parameters that will be added to the endpoint. The parameters +// /// should be specified in canonical format. +// /// +// /// +// /// Precomputed SHA256 hash of the request body content; this value should also +// /// be set as the header 'X-Amz-Content-SHA256' for non-streaming uploads. +// /// +// /// +// /// The user's AWS Access Key. +// /// +// /// +// /// The user's AWS Secret Key. +// /// +// /// +// /// The computed authorization string for the request. This value needs to be set as the +// /// header 'Authorization' on the subsequent HTTP request. +// /// +// public string ComputeSignature(IDictionary 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; +// 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(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); +// 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 +// SigningKey = DeriveSigningKey(HMACSHA256, awsSecretKey, Region, dateStamp, Service); + +// var kha = KeyedHashAlgorithm.Create(HMACSHA256); +// kha.Key = SigningKey; + +// // 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}"); + +// // cache the computed signature ready for chunk 0 upload +// LastComputedSignature = 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; +// } + +// /// +// /// Calculates the expanded payload size of our data when it is chunked +// /// +// /// +// /// The true size of the data payload to be uploaded +// /// +// /// +// /// The size of each chunk we intend to send; each chunk will be +// /// prefixed with signed header data, expanding the overall size +// /// by a determinable amount +// /// +// /// +// /// The overall payload size to use as content-length on a chunked upload +// /// +// public long CalculateChunkedContentLength(long originalLength, long chunkSize) +// { +// if (originalLength <= 0) +// throw new ArgumentOutOfRangeException("originalLength"); +// if (chunkSize <= 0) +// throw new ArgumentOutOfRangeException("chunkSize"); + +// var maxSizeChunks = originalLength / chunkSize; +// var remainingBytes = originalLength % chunkSize; + +// var chunkedContentLength = maxSizeChunks * CalculateChunkHeaderLength(chunkSize) +// + (remainingBytes > 0 ? CalculateChunkHeaderLength(remainingBytes) : 0) +// + CalculateChunkHeaderLength(0); + +// Logger.LogDebug($"\nComputed chunked content length for original length {originalLength} bytes, chunk size {chunkSize / 1024}KB is {chunkedContentLength} bytes"); +// return chunkedContentLength; +// } + +// /// +// /// Returns the size of a chunk header, which only varies depending +// /// on the selected chunk size +// /// +// /// +// /// The intended size of each chunk; this is placed into the chunk +// /// header +// /// +// /// +// /// The overall size of the header that will prefix the user data in +// /// each chunk +// /// +// static long CalculateChunkHeaderLength(long chunkSize) +// { +// return chunkSize.ToString("X").Length +// + CHUNK_SIGNATURE_HEADER.Length +// + SIGNATURE_LENGTH +// + CLRF.Length +// + chunkSize +// + CLRF.Length; +// } + +// /// +// /// Returns a chunk for upload consisting of the signed 'header' or chunk +// /// prefix plus the user data. The signature of the chunk incorporates the +// /// signature of the previous chunk (or, if the first chunk, the signature +// /// of the headers portion of the request). +// /// +// /// +// /// The length of the user data contained in userData +// /// +// /// +// /// Contains the user data to be sent in the upload chunk +// /// +// /// +// /// A new buffer of data for upload containing the chunk header plus user data +// /// +// public byte[] ConstructSignedChunk(long userDataLen, byte[] userData) +// { +// // to keep our computation routine signatures simple, if the userData +// // buffer contains less data than it could, shrink it. Note the special case +// // to handle the requirement that we send an empty chunk to complete +// // our chunked upload. +// byte[] dataToChunk; +// if (userDataLen == 0) +// dataToChunk = FINAL_CHUNK; +// else +// { +// if (userDataLen < userData.Length) +// { +// // shrink the chunkdata to fit +// dataToChunk = new byte[userDataLen]; +// Array.Copy(userData, 0, dataToChunk, 0, userDataLen); +// } +// else +// dataToChunk = userData; +// } + +// var chunkHeader = new StringBuilder(); + +// // start with size of user data +// chunkHeader.Append(dataToChunk.Length.ToString("X")); + +// // nonsig-extension; we have none in these samples +// const string nonsigExtension = ""; + +// // if this is the first chunk, we package it with the signing result +// // of the request headers, otherwise we use the cached signature +// // of the previous chunk + +// // sig-extension +// var chunkStringToSign = +// CHUNK_STRING_TO_SIGN_PREFIX + "\n" + +// DateTimeStamp + "\n" + +// Scope + "\n" + +// LastComputedSignature + "\n" + +// ToHexString(CanonicalRequestHashAlgorithm.ComputeHash(Encoding.UTF8.GetBytes(nonsigExtension)), true) + "\n" + +// ToHexString(CanonicalRequestHashAlgorithm.ComputeHash(dataToChunk), true); + +// Logger.LogDebug($"\nChunkStringToSign:\n{chunkStringToSign}"); + +// // compute the V4 signature for the chunk +// var chunkSignature +// = ToHexString(ComputeKeyedHash("HMACSHA256", +// SigningKey, +// Encoding.UTF8.GetBytes(chunkStringToSign)), +// true); + +// Logger.LogDebug($"\nChunkSignature:\n{chunkSignature}"); + +// // cache the signature to include with the next chunk's signature computation +// this.LastComputedSignature = chunkSignature; + +// // construct the actual chunk, comprised of the non-signed extensions, the +// // 'headers' we just signed and their signature, plus a newline then copy +// // that plus the user's data to a payload to be written to the request stream +// chunkHeader.Append(nonsigExtension + CHUNK_SIGNATURE_HEADER + chunkSignature); +// chunkHeader.Append(CLRF); + +// Logger.LogDebug($"\nChunkHeader:\n{chunkHeader}"); + +// try +// { +// var header = Encoding.UTF8.GetBytes(chunkHeader.ToString()); +// var trailer = Encoding.UTF8.GetBytes(CLRF); +// var signedChunk = new byte[header.Length + dataToChunk.Length + trailer.Length]; + +// Array.Copy(header, 0, signedChunk, 0, header.Length); +// Array.Copy(dataToChunk, 0, signedChunk, header.Length, dataToChunk.Length); +// Array.Copy(trailer, 0, signedChunk, header.Length + dataToChunk.Length, trailer.Length); + +// // this is the total data for the chunk that will be sent to the request stream +// return signedChunk; +// } +// catch (Exception e) +// { +// throw new Exception("Unable to sign the chunked data. " + e.Message, e); +// } +// } +// } +//} diff --git a/nanoFramework.Aws.IoTCore.Devices/AwsSignatureVersion4/SignerForPOST.cs b/nanoFramework.Aws.IoTCore.Devices/AwsSignatureVersion4/SignerForPOST.cs new file mode 100644 index 0000000..3fa1ca6 --- /dev/null +++ b/nanoFramework.Aws.IoTCore.Devices/AwsSignatureVersion4/SignerForPOST.cs @@ -0,0 +1,152 @@ +//using System; +//using System.Collections; +//using System.Globalization; +//using System.Net.Http; +//using System.Security.Cryptography; +//using System.Text; + +//namespace nanoFramework.Aws.SignatureVersion4 +//{ +// /// +// /// AWS Signature Version 4 signer for signing POST requests +// /// using a policy. +// /// +// public class SignerForPOST : SignerBase +// { +// public string FormatCredentialStringForPolicy(DateTime dateTimeStamp) +// { +// return "AKIAIOSFODNN7EXAMPLE/20130806/cn-north-1/s3/aws4_request"; +// } + +// public string FormatAlgorithmForPolicy +// { +// get { return "AWS4-HMAC-SHA256"; } +// } + +// public string FormatDateTimeForPolicy(DateTime dateTimeStamp) +// { +// return dateTimeStamp.ToString(ISO8601BasicFormat, CultureInfo.InvariantCulture); +// } + +// /// +// /// Computes an AWS Signature Version 4 signature for a request, ready for inclusion as an +// /// 'Authorization' header. +// /// +// /// +// /// The request headers; 'Host' and 'X-Amz-Date' will be added to this set. +// /// +// /// +// /// Any query parameters that will be added to the endpoint. The parameters +// /// should be specified in canonical format. +// /// +// /// +// /// Precomputed SHA256 hash of the request body content; this value should also +// /// be set as the header 'X-Amz-Content-SHA256' for non-streaming uploads. +// /// +// /// +// /// The user's AWS Access Key. +// /// +// /// +// /// The user's AWS Secret Key. +// /// +// /// +// /// The computed authorization string for the request. This value needs to be set as the +// /// header 'Authorization' on the subsequent HTTP request. +// /// +// public string ComputeSignature(IDictionary 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(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; +// } +// } +//} diff --git a/nanoFramework.Aws.IoTCore.Devices/AwsSignatureVersion4/SignerForQueryParameterAuth.cs b/nanoFramework.Aws.IoTCore.Devices/AwsSignatureVersion4/SignerForQueryParameterAuth.cs new file mode 100644 index 0000000..9e93559 --- /dev/null +++ b/nanoFramework.Aws.IoTCore.Devices/AwsSignatureVersion4/SignerForQueryParameterAuth.cs @@ -0,0 +1,164 @@ + +using System; +using System.Collections; +using System.Globalization; +using System.Net.Http.Headers; +using System.Net.Http; +using System.Security.Cryptography; +using System.Text; + +namespace nanoFramework.Aws.SignatureVersion4 +{ + /// + /// AWS Signature Version 4 signer for signing requests + /// using query string parameters. + /// + public class SignerForQueryParameterAuth : SignerBase + { + /// + /// Computes an AWS Signature Version 4 authorization for a request, suitable for embedding + /// in query parameters. + /// + /// + /// The request headers; 'Host' and 'X-Amz-Date' will be added to this set. + /// + /// + /// Any query parameters that will be added to the endpoint. The parameters + /// should be specified in canonical format. + /// + /// + /// Precomputed SHA256 hash of the request body content; this value should also + /// be set as the header 'X-Amz-Content-SHA256' for non-streaming uploads. + /// + /// + /// The user's AWS Access Key. + /// + /// + /// The user's AWS Secret Key. + /// + /// + /// The string expressing the Signature V4 components to add to query parameters. + /// + public string ComputeSignature(IDictionary 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); + + // extract the host portion of the endpoint to include in the signature calculation, + // unless already set + if (!headers.ContainsKey("Host")) + { + var hostHeader = EndpointUri.Host; + if (!EndpointUri.IsDefaultPort) + hostHeader += ":" + EndpointUri.Port; + headers.Add("Host", hostHeader); + } + + var dateStamp = requestDateTime.ToString(DateStringFormat); + var scope = string.Format("{0}/{1}/{2}/{3}", + dateStamp, + Region, + Service, + TERMINATOR); + + // canonicalized headers need to be expressed in the query + // parameters processed in the signature + var canonicalizedHeaderNames = CanonicalizeHeaderNames(headers); + var canonicalizedHeaders = CanonicalizeHeaders(headers); + + // reform the query parameters to (a) add the parameters required for + // Signature V4 and (b) canonicalize the set before they go into the + // signature calculation. Note that this assumes parameter names and + // values added outside this routine are already url encoded + var paramDictionary = new Dictionary(StringComparer.OrdinalIgnoreCase); + if (!string.IsNullOrEmpty(queryParameters)) + { + paramDictionary = queryParameters.Split('&').Select(p => p.Split('=')) + .ToDictionary(nameval => nameval[0], + nameval => nameval.Length > 1 + ? nameval[1] : ""); + } + + // add the fixed authorization params required by Signature V4 + paramDictionary.Add(X_Amz_Algorithm, HttpHelpers.UrlEncode(string.Format("{0}-{1}", SCHEME, ALGORITHM))); + paramDictionary.Add(X_Amz_Credential, HttpHelpers.UrlEncode(string.Format("{0}/{1}", awsAccessKey, scope))); + paramDictionary.Add(X_Amz_SignedHeaders, HttpHelpers.UrlEncode(canonicalizedHeaderNames)); + + // x-amz-date is now added as a query parameter, not a header, but still needs to be in ISO8601 basic form + paramDictionary.Add(X_Amz_Date, HttpHelpers.UrlEncode(dateTimeStamp)); + + // build the expanded canonical query parameter string that will go into the + // signature computation + var sb = new StringBuilder(); + var paramKeys = new List(paramDictionary.Keys); + paramKeys.Sort(StringComparer.Ordinal); + foreach (var p in paramKeys) + { + if (sb.Length > 0) + sb.Append("&"); + sb.Append(string.Format("{0}={1}", p, paramDictionary[p])); + } + var canonicalizedQueryParameters = sb.ToString(); + + // express all the header and query parameter data as a canonical request string + var canonicalRequest = CanonicalizeRequest(EndpointUri, + HttpMethod, + canonicalizedQueryParameters, + canonicalizedHeaderNames, + canonicalizedHeaders, + bodyHash); + //Logger.LogDebug($"\nCanonicalRequest:\n{canonicalRequest}"); + + byte[] canonicalRequestHashBytes + = CanonicalRequestHashAlgorithm.ComputeHash(Encoding.UTF8.GetBytes(canonicalRequest)); + + // construct the string to be signed + var stringToSign = new StringBuilder(); + + stringToSign.Append(string.Format("{0}-{1}\n{2}\n{3}\n", SCHEME, ALGORITHM, dateTimeStamp, scope)); + stringToSign.Append(ToHexString(canonicalRequestHashBytes, true)); + + //Logger.LogDebug($"\nStringToSign:\n{stringToSign}"); + + // compute the multi-stage signing key + KeyedHashAlgorithm kha = KeyedHashAlgorithm.Create(HMACSHA256); + kha.Key = DeriveSigningKey(HMACSHA256, awsSecretKey, Region, dateStamp, Service); + + // compute the final signature for the request, place into the result and return to the + // user to be embedded in the request as needed + var signature = kha.ComputeHash(Encoding.UTF8.GetBytes(stringToSign.ToString())); + var signatureString = ToHexString(signature, true); + //Logger.LogDebug($"\nSignature:\n{signatureString}"); + + // form up the authorization parameters for the caller to place in the query string + var authString = new StringBuilder(); + var authParams = new string[] + { + X_Amz_Algorithm, + X_Amz_Credential, + X_Amz_Date, + X_Amz_SignedHeaders + }; + + foreach (var p in authParams) + { + if (authString.Length > 0) + authString.Append("&"); + authString.Append(string.Format("{0}={1}", p, paramDictionary[p])); + } + + authString.Append(string.Format("&{0}={1}", X_Amz_Signature, signatureString)); + + var authorization = authString.ToString(); + //Logger.LogDebug($"\nAuthorization:\n{authorization}"); + + return authorization; + } + } +} diff --git a/nanoFramework.Aws.IoTCore.Devices/WebsocketConnectionClient.cs b/nanoFramework.Aws.IoTCore.Devices/WebsocketConnectionClient.cs index d1f6692..7fff04f 100644 --- a/nanoFramework.Aws.IoTCore.Devices/WebsocketConnectionClient.cs +++ b/nanoFramework.Aws.IoTCore.Devices/WebsocketConnectionClient.cs @@ -4,22 +4,56 @@ // using System; +using System.Net.WebSockets; +using nanoFramework.Aws.SignatureVersion4; -namespace nanoFramework.Aws.IoTCore.Devices.Devices.Client +namespace nanoFramework.Aws.IoTCore.Devices { /// - /// AWS IoT Core Websocket Connection Client for .NET nanoFramework + /// AWS IoT Core MQTT over Websocket Connection Client for .NET nanoFramework /// - public class WebsocketConnectionClient// : IDisposable + public class WebsocketConnectionClient : IDisposable { + public Uri Host { get; set; } + public string Region { get; set; } + public string AccessKey { get; set; } + public string SecretKey { get; set; } + const int _wssPort = 443; //Default WSS port. + + WebSocket _webSocket = null; + /// - /// Creates a new WebSocket Connection Client + /// Creates a new MQTT over WebSocket Connection Client /// + /// + /// Supports Signature Version 4 and Custom authentication over port 443 + /// public WebsocketConnectionClient() { - //TODO: implement! + // TODO: implement! look to the following: + // https://github.com/dotnet/MQTTnet/blob/master/Source/MQTTnet.Extensions.WebSocket4Net/WebSocket4NetMqttChannel.cs + + //wss://iot-endpoint/mqtt + + // sign with Signature Version 4 + var signer = new SignerForQueryParameterAuth + { + EndpointUri = Host, + HttpMethod = "GET", + Service = "iotdevicegateway", + Region = Region + }; + + // use mqtt client WithWebSocketServer + throw new NotImplementedException(); } + + /// + public void Dispose() + { + + } } } diff --git a/nanoFramework.Aws.IoTCore.Devices/nanoFramework.Aws.IoTCore.Devices.nfproj b/nanoFramework.Aws.IoTCore.Devices/nanoFramework.Aws.IoTCore.Devices.nfproj index c4d12dc..20ffaa1 100644 --- a/nanoFramework.Aws.IoTCore.Devices/nanoFramework.Aws.IoTCore.Devices.nfproj +++ b/nanoFramework.Aws.IoTCore.Devices/nanoFramework.Aws.IoTCore.Devices.nfproj @@ -40,6 +40,11 @@ + + + + + @@ -87,6 +92,12 @@ ..\packages\nanoFramework.System.Net.1.10.77\lib\System.Net.dll True + + ..\packages\nanoFramework.System.Net.Http.1.5.84\lib\System.Net.Http.dll + + + ..\packages\nanoFramework.System.Net.WebSockets.1.1.57\lib\System.Net.WebSockets.dll + ..\packages\nanoFramework.System.Threading.1.1.32\lib\System.Threading.dll True diff --git a/nanoFramework.Aws.IoTCore.Devices/packages.config b/nanoFramework.Aws.IoTCore.Devices/packages.config index af20a20..e804dcf 100644 --- a/nanoFramework.Aws.IoTCore.Devices/packages.config +++ b/nanoFramework.Aws.IoTCore.Devices/packages.config @@ -8,6 +8,8 @@ + + diff --git a/nanoFramework.Aws.IoTCore.Devices/packages.lock.json b/nanoFramework.Aws.IoTCore.Devices/packages.lock.json index d2c0e3e..9aebdaf 100644 --- a/nanoFramework.Aws.IoTCore.Devices/packages.lock.json +++ b/nanoFramework.Aws.IoTCore.Devices/packages.lock.json @@ -50,6 +50,18 @@ "resolved": "1.10.77", "contentHash": "d/zIQqt0Y4e49tmtc2sfluE3DQa3w6hj3DHXVt5B+R2onytIJaKzJy8k8qYVyVy+EQLhdruyvcHSTIaygZrUzQ==" }, + "nanoFramework.System.Net.Http": { + "type": "Direct", + "requested": "[1.5.84, 1.5.84]", + "resolved": "1.5.84", + "contentHash": "XiG+t10FX3Lyb+d4A9EF0IDp7wS7oHXyKaqTBI7tvroxgMScNXVI6M/a6QlISepYUzO4xQ559X8d76wcsBsDnw==" + }, + "nanoFramework.System.Net.WebSockets": { + "type": "Direct", + "requested": "[1.1.57, 1.1.57]", + "resolved": "1.1.57", + "contentHash": "ae4ri515PR8IpzIeRZlYNyYm/W22AMa6I0bOXJweCT2G6GbRKjy15J3S5WtIFQcG/OqY3EGALN/198R3crvOLA==" + }, "nanoFramework.System.Text": { "type": "Direct", "requested": "[1.2.54, 1.2.54]", From 71e63209e48403352185262f94d7ec1c40af975a Mon Sep 17 00:00:00 2001 From: Robin Jones Date: Wed, 9 Aug 2023 20:51:00 +0100 Subject: [PATCH 02/14] Convert to nF Re-comment out un-necessary classes --- Tests/Aws.IoTCore.Devices.UnitTests.nfproj | 5 + Tests/SignatureVersion4Tests.cs | 63 ++++++ Tests/SortExtensionTests.cs | 97 +++++++++ .../AwsSignatureVersion4/HMACSHA256.cs | 90 +++++++++ .../AwsSignatureVersion4/SHA256.cs | 189 ++++++++++++++++++ .../AwsSignatureVersion4/SignerBase.cs | 105 +++++----- .../SignerForAuthorizationHeader.cs | 77 ++++--- .../SignerForChunkedUpload.cs | 96 +++++---- .../AwsSignatureVersion4/SignerForPOST.cs | 85 ++++---- .../SignerForQueryParameterAuth.cs | 77 ++++--- .../nfArrayListExtensions.cs | 137 +++++++++++++ .../WebsocketConnectionClient.cs | 21 +- .../nanoFramework.Aws.IoTCore.Devices.nfproj | 6 + .../packages.config | 1 + .../packages.lock.json | 6 + 15 files changed, 860 insertions(+), 195 deletions(-) create mode 100644 Tests/SignatureVersion4Tests.cs create mode 100644 Tests/SortExtensionTests.cs create mode 100644 nanoFramework.Aws.IoTCore.Devices/AwsSignatureVersion4/HMACSHA256.cs create mode 100644 nanoFramework.Aws.IoTCore.Devices/AwsSignatureVersion4/SHA256.cs create mode 100644 nanoFramework.Aws.IoTCore.Devices/AwsSignatureVersion4/nfArrayListExtensions.cs diff --git a/Tests/Aws.IoTCore.Devices.UnitTests.nfproj b/Tests/Aws.IoTCore.Devices.UnitTests.nfproj index 1ede876..1fbe38b 100644 --- a/Tests/Aws.IoTCore.Devices.UnitTests.nfproj +++ b/Tests/Aws.IoTCore.Devices.UnitTests.nfproj @@ -32,6 +32,8 @@ + + @@ -69,6 +71,9 @@ True + + + diff --git a/Tests/SignatureVersion4Tests.cs b/Tests/SignatureVersion4Tests.cs new file mode 100644 index 0000000..4bba476 --- /dev/null +++ b/Tests/SignatureVersion4Tests.cs @@ -0,0 +1,63 @@ +// +// Copyright (c) .NET Foundation and Contributors +// See LICENSE file in the project root for full license information. +// + +using nanoFramework.TestFramework; +using System; +using System.Collections; +using nanoFramework.Aws.SignatureVersion4; + +namespace nanoFramework.Aws.SignatureVersion4.Tests +{ + // https://github.com/inspiration/uniface-aws-sigv4 + // https://github.com/FantasticFiasco/aws-signature-version-4 + // https://stackoverflow.com/questions/28966075/creating-a-hmac-signature-for-aws-rest-query-in-c-sharp + [TestClass] + public class SignatureVersion4Tests + { + [TestMethod] + public void check_CanonicalizeHeaderNames_when_order_is_canonical_already() // using an already ordered list + { + //// Given that: SigV4 seems to require a header that is canonicalized + //var headers = new Hashtable(); + //headers.Add("aTest", "atestval"); + //headers.Add("Atest2", "Atest2val"); + //headers.Add("btest", "btestval"); + //headers.Add("ctest", "ctestval"); + + //Console.WriteLine(SignerBase.CanonicalizeHeaderNames(headers)); + + // Expect "atest;atest2;btest;ctest" + } + + [TestMethod] + public void check_CanonicalizeHeaderNames_when_order_is_not_sorted() + { + //var headers = new Hashtable(); + //headers.Add("aTest", "atestval"); + //headers.Add("ctest", "ctestval"); + //headers.Add("btest", "btestval"); + //headers.Add("Atest2", "Atest2val"); + + //Console.WriteLine(CanonicalizeHeaderNames(headers)); + + // Expect "atest;atest2;btest;ctest" + } + + + [TestMethod] + public void check_CanonicalizeHeaders_when_order_is_not_sorted() + { + //var headers = new Hashtable(); + //headers.Add("aTest", "atestval"); + //headers.Add("ctest", "ctestval"); + //headers.Add("btest", "btestval"); + //headers.Add("Atest2", "Atest2val"); + //Console.WriteLine(CanonicalizeHeaders(headers)); + + // Expect "atest:atestval\natest2:Atest2val\nbtest:btestval\nctest:ctestval\n" + } + + } +} diff --git a/Tests/SortExtensionTests.cs b/Tests/SortExtensionTests.cs new file mode 100644 index 0000000..b24b76f --- /dev/null +++ b/Tests/SortExtensionTests.cs @@ -0,0 +1,97 @@ +// +// Copyright (c) .NET Foundation and Contributors +// See LICENSE file in the project root for full license information. +// + +using nanoFramework.TestFramework; +using System; +using System.Collections; + +namespace nanoFramework.Aws.SignatureVersion4.Tests +{ + [TestClass] + public class SortExtensionTests + { + [TestMethod] + public void check_ArrayList_Sort_Ordinal_when_order_is_canonical_already() + { + var list = new ArrayList(); + list.Add("aTest"); + list.Add("Atest2"); + list.Add("btest"); + list.Add("ctest"); + + list.Sort(StringComparer.Ordinal); + + foreach (var item in list) + { + Console.WriteLine(item.ToString()); + } + + + + // Expect "atest;atest2;btest;ctest" + } + + [TestMethod] + public void check_check_ArrayList_Sort_Ordinal_when_order_is_not_sorted() + { + var list = new ArrayList(); + list.Add("aTest"); + list.Add("ctest"); + list.Add("btest"); + list.Add("Atest2"); + + list.Sort(StringComparer.Ordinal); + + foreach (var item in list) + { + Console.WriteLine(item.ToString()); + } + + // Expect "atest;atest2;btest;ctest" + } + + + [TestMethod] + public void check_ArrayList_Sort_OrdinalIgnoreCase_when_order_is_canonical_already() + { + var list = new ArrayList(); + list.Add("aTest"); + list.Add("Atest2"); + list.Add("btest"); + list.Add("ctest"); + + list.Sort(StringComparer.OrdinalIgnoreCase); + + foreach (var item in list) + { + Console.WriteLine(item.ToString()); + } + + + + // Expect "atest;atest2;btest;ctest" + } + + [TestMethod] + public void check_check_ArrayList_Sort_OrdinalIgnoreCase_when_order_is_not_sorted() + { + var list = new ArrayList(); + list.Add("aTest"); + list.Add("ctest"); + list.Add("btest"); + list.Add("Atest2"); + + list.Sort(StringComparer.OrdinalIgnoreCase); + + foreach (var item in list) + { + Console.WriteLine(item.ToString()); + } + + // Expect "atest;atest2;btest;ctest" + } + + } +} diff --git a/nanoFramework.Aws.IoTCore.Devices/AwsSignatureVersion4/HMACSHA256.cs b/nanoFramework.Aws.IoTCore.Devices/AwsSignatureVersion4/HMACSHA256.cs new file mode 100644 index 0000000..0797c78 --- /dev/null +++ b/nanoFramework.Aws.IoTCore.Devices/AwsSignatureVersion4/HMACSHA256.cs @@ -0,0 +1,90 @@ +// +// Copyright (c) .NET Foundation and Contributors +// See LICENSE file in the project root for full license information. +// + +// +// Originally by Elze Kool see http://www.microframework.nl/2009/09/05/shahmac-digest-class/ +// Adjustments by Laurent Ellerbach laurelle@microsoft.com 2021/05/31 +// + +namespace System.Security.Cryptography +{ + using System; + + /// + /// Computes a Hash-based Message Authentication Code (HMAC) by using the System.Security.Cryptography.SHA256 + /// hash function. + /// + public class HMACSHA256 + { + /// + /// Gets or sets the key to use in the HMAC calculation. + /// + public byte[] Key { get; set; } + + /// + /// Initializes a new instance of the System.Security.Cryptography.HMACSHA256 class + /// with the specified key data. + /// + /// The secret key for System.Security.Cryptography.HMACSHA256 encryption. The key + /// can be any length. However, the recommended size is 64 bytes max. + public HMACSHA256(byte[] key) + { + Key = key; + } + + /// + /// Computes the hash value for the specified byte array. + /// + /// The input to compute the hash code for. + /// The computed hash code. + /// buffer is null. + public byte[] ComputeHash(byte[] buffer) + { + if (buffer == null) + { + throw new ArgumentNullException(); + } + + return ComputeHMACSHA256(Key, buffer); + } + + /// + /// Compute HMAC SHA-256 + /// + /// Secret + /// Password + /// 32 byte HMAC_SHA256 + private static byte[] ComputeHMACSHA256(byte[] secret, byte[] value) + { + // Create two arrays, bi and bo + var bi = new byte[64 + value.Length]; + var bo = new byte[64 + 32]; + + // Copy secret to both arrays + Array.Copy(secret, bi, secret.Length); + Array.Copy(secret, bo, secret.Length); + + for (var i = 0; i < 64; i++) + { + // Xor bi with 0x36 + bi[i] = (byte)(bi[i] ^ 0x36); + + // Xor bo with 0x5c + bo[i] = (byte)(bo[i] ^ 0x5c); + } + + // Append value to bi + Array.Copy(value, 0, bi, 64, value.Length); + + var sha256 = SHA256.Create(); + // Append SHA256(bi) to bo + var sha_bi = sha256.ComputeHash(bi); + Array.Copy(sha_bi, 0, bo, 64, 32); + + // Return SHA256(bo) + return sha256.ComputeHash(bo); + } + } +} \ No newline at end of file diff --git a/nanoFramework.Aws.IoTCore.Devices/AwsSignatureVersion4/SHA256.cs b/nanoFramework.Aws.IoTCore.Devices/AwsSignatureVersion4/SHA256.cs new file mode 100644 index 0000000..9416c58 --- /dev/null +++ b/nanoFramework.Aws.IoTCore.Devices/AwsSignatureVersion4/SHA256.cs @@ -0,0 +1,189 @@ +// +// Copyright (c) .NET Foundation and Contributors +// See LICENSE file in the project root for full license information. +// + +using System; + +namespace System.Security.Cryptography +{ + /// + /// Computes the SHA256 hash for the input data. + /// + public class SHA256 + { + // Number used in SHA256 hash function + private static readonly uint[] _sha256HashKeys = new uint[] + { + 0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5, 0x3956c25b, 0x59f111f1, 0x923f82a4, 0xab1c5ed5, 0xd807aa98, + 0x12835b01, 0x243185be, 0x550c7dc3, 0x72be5d74, 0x80deb1fe, 0x9bdc06a7, 0xc19bf174, 0xe49b69c1, 0xefbe4786, + 0x0fc19dc6, 0x240ca1cc, 0x2de92c6f, 0x4a7484aa, 0x5cb0a9dc, 0x76f988da, 0x983e5152, 0xa831c66d, 0xb00327c8, + 0xbf597fc7, 0xc6e00bf3, 0xd5a79147, 0x06ca6351, 0x14292967, 0x27b70a85, 0x2e1b2138, 0x4d2c6dfc, 0x53380d13, + 0x650a7354, 0x766a0abb, 0x81c2c92e, 0x92722c85, 0xa2bfe8a1, 0xa81a664b, 0xc24b8b70, 0xc76c51a3, 0xd192e819, + 0xd6990624, 0xf40e3585, 0x106aa070, 0x19a4c116, 0x1e376c08, 0x2748774c, 0x34b0bcb5, 0x391c0cb3, 0x4ed8aa4a, + 0x5b9cca4f, 0x682e6ff3, 0x748f82ee, 0x78a5636f, 0x84c87814, 0x8cc70208, 0x90befffa, 0xa4506ceb, 0xbef9a3f7, + 0xc67178f2 + }; + + /// + /// Creates an instance of the default implementation of System.Security.Cryptography.SHA256. + /// + /// + public static SHA256 Create() => new SHA256(); + + /// + /// Initializes a new instance of SHA256. + /// + protected SHA256() + { } + + /// + /// Compute SHA-256 digest + /// + /// Input array + /// The computed SHA256 + public byte[] ComputeHash(byte[] input) + { + // Initialize working parameters + uint a, b, c, d, e, f, g, h, i, s0, s1, t1, t2; + uint h0 = 0x6a09e667; + var h1 = 0xbb67ae85; + uint h2 = 0x3c6ef372; + var h3 = 0xa54ff53a; + uint h4 = 0x510e527f; + var h5 = 0x9b05688c; + uint h6 = 0x1f83d9ab; + uint h7 = 0x5be0cd19; + uint blockstart = 0; + + // Calculate how long the padded message should be + var newinputlength = input.Length + 1; + while ((newinputlength % 64) != 56) // length mod 512bits = 448bits + { + newinputlength++; + } + + // Create array for padded data + var processed = new byte[newinputlength + 8]; + Array.Copy(input, processed, input.Length); + + // Pad data with an 1 + processed[input.Length] = 0x80; + + // Pad data with big endian 64bit length of message + // We do only 32 bits becouse input.length is 32 bit + processed[processed.Length - 4] = (byte)(((input.Length * 8) & 0xFF000000) >> 24); + processed[processed.Length - 3] = (byte)(((input.Length * 8) & 0x00FF0000) >> 16); + processed[processed.Length - 2] = (byte)(((input.Length * 8) & 0x0000FF00) >> 8); + processed[processed.Length - 1] = (byte)((input.Length * 8) & 0x000000FF); + + // Block of 32 bits values used in calculations + var wordblock = new uint[64]; + + // Now process each 512 bit block + while (blockstart < processed.Length) + { + // break chunk into sixteen 32-bit big-endian words + for (i = 0; i < 16; i++) + wordblock[i] = BigEndianFromBytes(processed, blockstart + (i * 4)); + + // Extend the sixteen 32-bit words into sixty-four 32-bit words: + for (i = 16; i < 64; i++) + { + s0 = RotateRight(wordblock[i - 15], 7) ^ RotateRight(wordblock[i - 15], 18) ^ (wordblock[i - 15] >> 3); + s1 = RotateRight(wordblock[i - 2], 17) ^ RotateRight(wordblock[i - 2], 19) ^ (wordblock[i - 2] >> 10); + wordblock[i] = wordblock[i - 16] + s0 + wordblock[i - 7] + s1; + } + + // Initialize hash value for this chunk: + a = h0; + b = h1; + c = h2; + d = h3; + e = h4; + f = h5; + g = h6; + h = h7; + + // Main loop + for (i = 0; i < 64; i++) + { + t1 = h + (RotateRight(e, 6) ^ RotateRight(e, 11) ^ RotateRight(e, 25)) + Choice(e, f, g) + _sha256HashKeys[i] + wordblock[i]; + t2 = (RotateRight(a, 2) ^ RotateRight(a, 13) ^ RotateRight(a, 22)) + Majority(a, b, c); + h = g; + g = f; + f = e; + e = d + t1; + d = c; + c = b; + b = a; + a = t1 + t2; + } + + // Add this chunk's hash to result so far + h0 += a; + h1 += b; + h2 += c; + h3 += d; + h4 += e; + h5 += f; + h6 += g; + h7 += h; + + // Process next 512bit block + blockstart += 64; + } + + // Prepare output + var output = new byte[32]; + BytesFromBigEndian(h0, ref output, 0); + BytesFromBigEndian(h1, ref output, 4); + BytesFromBigEndian(h2, ref output, 8); + BytesFromBigEndian(h3, ref output, 12); + BytesFromBigEndian(h4, ref output, 16); + BytesFromBigEndian(h5, ref output, 20); + BytesFromBigEndian(h6, ref output, 24); + BytesFromBigEndian(h7, ref output, 28); + + return output; + } + + // Convert 4 bytes to big endian uint32 + internal static uint BigEndianFromBytes(byte[] input, uint start) + { + uint r = 0; + r |= (((uint)input[start]) << 24); + r |= (((uint)input[start + 1]) << 16); + r |= (((uint)input[start + 2]) << 8); + r |= ((uint)input[start + 3]); + return r; + } + + // Rotate bits right + private static uint RotateRight(uint x, int n) + { + return ((x >> n) | (x << (32 - n))); + } + + // SHA-224/SHA-256 choice function + private static uint Choice(uint x, uint y, uint z) + { + return ((x & y) ^ (~x & z)); + } + + // Convert big endian uint32 to bytes + internal static void BytesFromBigEndian(uint input, ref byte[] output, int start) + { + output[start] = (byte)((input & 0xFF000000) >> 24); + output[start + 1] = (byte)((input & 0x00FF0000) >> 16); + output[start + 2] = (byte)((input & 0x0000FF00) >> 8); + output[start + 3] = (byte)(input & 0x000000FF); + } + + // SHA-224/SHA-256 majority function + private static uint Majority(uint x, uint y, uint z) + { + return ((x & y) ^ (x & z) ^ (y & z)); + } + } +} \ No newline at end of file diff --git a/nanoFramework.Aws.IoTCore.Devices/AwsSignatureVersion4/SignerBase.cs b/nanoFramework.Aws.IoTCore.Devices/AwsSignatureVersion4/SignerBase.cs index 4bd588d..2af4f52 100644 --- a/nanoFramework.Aws.IoTCore.Devices/AwsSignatureVersion4/SignerBase.cs +++ b/nanoFramework.Aws.IoTCore.Devices/AwsSignatureVersion4/SignerBase.cs @@ -1,9 +1,14 @@ -using System; +// +// Copyright (c) .NET Foundation and Contributors +// See LICENSE file in the project root for full license information. +// + +using System; using System.Collections; -using System.Net.Http.Headers; using System.Security.Cryptography; using System.Text; using System.Text.RegularExpressions; +using System.Web; namespace nanoFramework.Aws.SignatureVersion4 { @@ -13,36 +18,33 @@ namespace nanoFramework.Aws.SignatureVersion4 public abstract class SignerBase { // SHA256 hash of an empty request body - public const string EMPTY_BODY_SHA256 = "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855"; + internal const string EMPTY_BODY_SHA256 = "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855"; - public const string SCHEME = "AWS4"; - public const string ALGORITHM = "HMAC-SHA256"; - public const string TERMINATOR = "aws4_request"; + internal const string SCHEME = "AWS4"; + internal const string ALGORITHM = "HMAC-SHA256"; + internal const string TERMINATOR = "aws4_request"; // format strings for the date/time and date stamps required during signing - public const string ISO8601BasicFormat = "yyyyMMddTHHmmssZ"; - public const string DateStringFormat = "yyyyMMdd"; + internal const string ISO8601BasicFormat = "yyyyMMddTHHmmssZ"; + internal const string DateStringFormat = "yyyyMMdd"; // some common x-amz-* parameters - public const string X_Amz_Algorithm = "X-Amz-Algorithm"; - public const string X_Amz_Credential = "X-Amz-Credential"; - public const string X_Amz_SignedHeaders = "X-Amz-SignedHeaders"; - public const string X_Amz_Date = "X-Amz-Date"; - public const string X_Amz_Signature = "X-Amz-Signature"; - public const string X_Amz_Expires = "X-Amz-Expires"; - public const string X_Amz_Content_SHA256 = "X-Amz-Content-SHA256"; - public const string X_Amz_Decoded_Content_Length = "X-Amz-Decoded-Content-Length"; - public const string X_Amz_Meta_UUID = "X-Amz-Meta-UUID"; - - // the name of the keyed hash algorithm used in signing - public const string HMACSHA256 = "HMACSHA256"; + internal const string X_Amz_Algorithm = "X-Amz-Algorithm"; + internal const string X_Amz_Credential = "X-Amz-Credential"; + internal const string X_Amz_SignedHeaders = "X-Amz-SignedHeaders"; + internal const string X_Amz_Date = "X-Amz-Date"; + internal const string X_Amz_Signature = "X-Amz-Signature"; + internal const string X_Amz_Expires = "X-Amz-Expires"; + internal const string X_Amz_Content_SHA256 = "X-Amz-Content-SHA256"; + internal const string X_Amz_Decoded_Content_Length = "X-Amz-Decoded-Content-Length"; + internal const string X_Amz_Meta_UUID = "X-Amz-Meta-UUID"; // request canonicalization requires multiple whitespace compression - protected static readonly Regex CompressWhitespaceRegex = new Regex("\\s+"); + internal static readonly Regex CompressWhitespaceRegex = new Regex("\\s+"); // algorithm used to hash the canonical request that is supplied to // the signature computation - public static HashAlgorithm CanonicalRequestHashAlgorithm = HashAlgorithm.Create("SHA-256"); + internal static SHA256 CanonicalRequestHashAlgorithm = SHA256.Create(); /// /// The service endpoint, including the path to any resource. @@ -75,9 +77,13 @@ public abstract class SignerBase /// /// The set of header names canonicalized to a flattened, ;-delimited string /// - protected string CanonicalizeHeaderNames(IDictionary headers) + protected string CanonicalizeHeaderNames(IDictionary headers) { - var headersToSign = new List(headers.Keys); + var headersToSign = new ArrayList(); + foreach (DictionaryEntry kvp in headers) + { + headersToSign.Add(kvp.Key); + } headersToSign.Sort(StringComparer.OrdinalIgnoreCase); var sb = new StringBuilder(); @@ -85,7 +91,7 @@ protected string CanonicalizeHeaderNames(IDictionary headers) { if (sb.Length > 0) sb.Append(";"); - sb.Append(header.ToLower()); + sb.Append(header.ToString().ToLower()); } return sb.ToString(); } @@ -96,28 +102,29 @@ protected string CanonicalizeHeaderNames(IDictionary headers) /// /// The set of headers to be encoded /// Canonicalized string of headers with values - protected virtual string CanonicalizeHeaders(IDictionary headers) + protected virtual string CanonicalizeHeaders(IDictionary headers) { if (headers == null || headers.Count == 0) return string.Empty; - // step1: sort the headers into lower-case format; we create a new + // step1: sort the headers using 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(); - foreach (var header in headers.Keys) + + var headerKeys = new ArrayList(); + foreach (DictionaryEntry kvp in headers) { - sortedHeaderMap.Add(header.ToLower(), headers[header]); + headerKeys.Add(kvp.Key); } + headerKeys.Sort(StringComparer.OrdinalIgnoreCase); // 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) + foreach (var p in headerKeys) { - var headerValue = CompressWhitespaceRegex.Replace(sortedHeaderMap[header], " "); - sb.AppendFormat("{0}:{1}\n", header, headerValue.Trim()); + sb.Append($"{p.ToString().ToLower()}={CompressWhitespaceRegex.Replace(headers[p].ToString().Trim(), " ")}\n"); } return sb.ToString(); @@ -149,12 +156,12 @@ protected string CanonicalizeRequest(Uri endpointUri, { var canonicalRequest = new StringBuilder(); - canonicalRequest.Append(string.Format("{0}\n", httpMethod)); - canonicalRequest.Append(string.Format("{0}\n", CanonicalResourcePath(endpointUri))); - canonicalRequest.Append(string.Format("{0}\n", queryParameters)); + canonicalRequest.Append($"{httpMethod}\n"); + canonicalRequest.Append($"{CanonicalResourcePath(endpointUri)}\n"); + canonicalRequest.Append($"{queryParameters}\n"); - canonicalRequest.Append(string.Format("{0}\n", canonicalizedHeaders)); - canonicalRequest.Append(string.Format("{0}\n", canonicalizedHeaderNames)); + canonicalRequest.Append($"{canonicalizedHeaders}\n"); + canonicalRequest.Append($"{canonicalizedHeaderNames}\n"); canonicalRequest.Append(bodyHash); @@ -172,43 +179,39 @@ protected string CanonicalResourcePath(Uri endpointUri) return "/"; // encode the path per RFC3986 - return HttpHelpers.UrlEncode(endpointUri.AbsolutePath, true); + return HttpUtility.UrlEncode(endpointUri.AbsolutePath); // TODO: check if hash ('#') is allowed/supported as per original implementation. } /// /// Compute and return the multi-stage signing key for the request. /// - /// Hashing algorithm to use /// The clear-text AWS secret key /// The region in which the service request will be processed /// Date of the request, in yyyyMMdd format /// The name of the service being called by the request /// Computed signing key - protected byte[] DeriveSigningKey(string algorithm, string awsSecretAccessKey, string region, string date, string service) + protected byte[] DeriveSigningKey(string awsSecretAccessKey, string region, string date, string service) { const string ksecretPrefix = SCHEME; - char[] ksecret = null; - ksecret = (ksecretPrefix + awsSecretAccessKey).ToCharArray(); + string ksecret = (ksecretPrefix + awsSecretAccessKey); - byte[] hashDate = ComputeKeyedHash(algorithm, Encoding.UTF8.GetBytes(ksecret), Encoding.UTF8.GetBytes(date)); - byte[] hashRegion = ComputeKeyedHash(algorithm, hashDate, Encoding.UTF8.GetBytes(region)); - byte[] hashService = ComputeKeyedHash(algorithm, hashRegion, Encoding.UTF8.GetBytes(service)); - return ComputeKeyedHash(algorithm, hashService, Encoding.UTF8.GetBytes(TERMINATOR)); + byte[] hashDate = ComputeKeyedHash(Encoding.UTF8.GetBytes(ksecret), Encoding.UTF8.GetBytes(date)); + byte[] hashRegion = ComputeKeyedHash(hashDate, Encoding.UTF8.GetBytes(region)); + byte[] hashService = ComputeKeyedHash(hashRegion, Encoding.UTF8.GetBytes(service)); + return ComputeKeyedHash(hashService, Encoding.UTF8.GetBytes(TERMINATOR)); } /// /// Compute and return the hash of a data blob using the specified algorithm /// and key /// - /// Algorithm to use for hashing /// Hash key /// Data blob /// Hash of the data - protected byte[] ComputeKeyedHash(string algorithm, byte[] key, byte[] data) + protected byte[] ComputeKeyedHash(byte[] key, byte[] data) { - var kha = KeyedHashAlgorithm.Create(algorithm); - kha.Key = key; + var kha = new HMACSHA256(key); return kha.ComputeHash(data); } diff --git a/nanoFramework.Aws.IoTCore.Devices/AwsSignatureVersion4/SignerForAuthorizationHeader.cs b/nanoFramework.Aws.IoTCore.Devices/AwsSignatureVersion4/SignerForAuthorizationHeader.cs index d43f721..f93603e 100644 --- a/nanoFramework.Aws.IoTCore.Devices/AwsSignatureVersion4/SignerForAuthorizationHeader.cs +++ b/nanoFramework.Aws.IoTCore.Devices/AwsSignatureVersion4/SignerForAuthorizationHeader.cs @@ -1,7 +1,11 @@ -//using System; +//// +//// Copyright (c) .NET Foundation and Contributors +//// See LICENSE file in the project root for full license information. +//// + +//using System; //using System.Collections; -//using System.Globalization; -//using System.Net.Http; +//using System.Diagnostics; //using System.Security.Cryptography; //using System.Text; @@ -38,23 +42,22 @@ // /// The computed authorization string for the request. This value needs to be set as the // /// header 'Authorization' on the subsequent HTTP request. // /// -// public string ComputeSignature(IDictionary headers, +// public string ComputeSignature(IDictionary 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 +// // first get the date and time for the subsequent request, and convert to ISO8601 format (without '-' and ':') // // for use in signature generation // var requestDateTime = DateTime.UtcNow; -// var dateTimeStamp = requestDateTime.ToString(ISO8601BasicFormat, CultureInfo.InvariantCulture); +// var dateTimeStamp = requestDateTime.ToString(ISO8601BasicFormat); // // 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; +// hostHeader += ":" + EndpointUri.Port; // FIXME: should use //if (!EndpointUri.IsDefaultPort) // headers.Add("Host", hostHeader); // // canonicalize the headers; we need the set of header names as well as the @@ -67,19 +70,36 @@ // 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 paramDictionary = new Hashtable(); + +// var qparam = queryParameters.Split('&'); +// foreach (string p in qparam) +// { +// var items = p.Split('='); +// if (items.Length == 1) +// { +// paramDictionary.Add(items[0], null); +// } +// else +// { +// paramDictionary.Add(items[0], items[1]); +// } +// } // var sb = new StringBuilder(); -// var paramKeys = new List(paramDictionary.Keys); +// var paramKeys = new ArrayList(); + +// foreach (DictionaryEntry kvp in paramDictionary) +// { +// paramKeys.Add(kvp.Key); +// } + // paramKeys.Sort(StringComparer.Ordinal); // foreach (var p in paramKeys) // { // if (sb.Length > 0) // sb.Append("&"); -// sb.AppendFormat("{0}={1}", p, paramDictionary[p]); +// sb.Append($"{p}={paramDictionary[p]}"); // } // canonicalizedQueryParameters = sb.ToString(); @@ -92,7 +112,7 @@ // canonicalizedHeaderNames, // canonicalizedHeaders, // bodyHash); -// Logger.LogDebug($"\nCanonicalRequest:\n{canonicalRequest}"); +// Debug.WriteLine($"\nCanonicalRequest:\n{canonicalRequest}"); // // generate a hash of the canonical request, to go into signature computation // var canonicalRequestHashBytes @@ -101,35 +121,30 @@ // // 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); +// var dateStamp = requestDateTime.ToString(DateStringFormat); +// var scope = $"{dateStamp}/{Region}/{Service}/{TERMINATOR}"; -// stringToSign.AppendFormat("{0}-{1}\n{2}\n{3}\n", SCHEME, ALGORITHM, dateTimeStamp, scope); +// stringToSign.Append($"{SCHEME}-{ALGORITHM}\n{dateTimeStamp}\n{scope}\n"); // stringToSign.Append(ToHexString(canonicalRequestHashBytes, true)); -// Logger.LogDebug($"\nStringToSign:\n{stringToSign}"); +// Debug.WriteLine($"\nStringToSign:\n{stringToSign}"); // // compute the signing key -// var kha = KeyedHashAlgorithm.Create(HMACSHA256); -// kha.Key = DeriveSigningKey(HMACSHA256, awsSecretKey, Region, dateStamp, Service); +// var kha = new HMACSHA256(DeriveSigningKey(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}"); +// Debug.WriteLine($"\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); +// authString.Append($"{SCHEME}-{ALGORITHM} "); +// authString.Append($"Credential={awsAccessKey}/{scope}, "); +// authString.Append($"SignedHeaders={canonicalizedHeaderNames}, "); +// authString.Append($"Signature={signatureString}"); // var authorization = authString.ToString(); -// Logger.LogDebug($"\nAuthorization:\n{authorization}"); +// Debug.WriteLine($"\nAuthorization:\n{authorization}"); // return authorization; // } diff --git a/nanoFramework.Aws.IoTCore.Devices/AwsSignatureVersion4/SignerForChunkedUpload.cs b/nanoFramework.Aws.IoTCore.Devices/AwsSignatureVersion4/SignerForChunkedUpload.cs index f79af4d..50dec03 100644 --- a/nanoFramework.Aws.IoTCore.Devices/AwsSignatureVersion4/SignerForChunkedUpload.cs +++ b/nanoFramework.Aws.IoTCore.Devices/AwsSignatureVersion4/SignerForChunkedUpload.cs @@ -1,7 +1,11 @@ -//using System; +//// +//// Copyright (c) .NET Foundation and Contributors +//// See LICENSE file in the project root for full license information. +//// + +//using System; //using System.Collections; -//using System.Globalization; -//using System.Net.Http; +//using System.Diagnostics; //using System.Security.Cryptography; //using System.Text; @@ -16,7 +20,7 @@ // { // // SHA256 substitute marker used in place of x-amz-content-sha256 when employing // // chunked uploads -// public const string STREAMING_BODY_SHA256 = "STREAMING-AWS4-HMAC-SHA256-PAYLOAD"; +// internal const string STREAMING_BODY_SHA256 = "STREAMING-AWS4-HMAC-SHA256-PAYLOAD"; // static readonly string CLRF = "\r\n"; // static readonly string CHUNK_STRING_TO_SIGN_PREFIX = "AWS4-HMAC-SHA256-PAYLOAD"; @@ -73,23 +77,22 @@ // /// The computed authorization string for the request. This value needs to be set as the // /// header 'Authorization' on the subsequent HTTP request. // /// -// public string ComputeSignature(IDictionary headers, +// public string ComputeSignature(IDictionary 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 +// // first get the date and time for the subsequent request, and convert to ISO8601 format (without '-' and ':') // // for use in signature generation // var requestDateTime = DateTime.UtcNow; -// DateTimeStamp = requestDateTime.ToString(ISO8601BasicFormat, CultureInfo.InvariantCulture); +// DateTimeStamp = requestDateTime.ToString(ISO8601BasicFormat); // // 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; +// hostHeader += ":" + EndpointUri.Port; // FIXME: should use //if (!EndpointUri.IsDefaultPort) // headers.Add("Host", hostHeader); // // canonicalize the headers; we need the set of header names as well as the @@ -102,19 +105,36 @@ // 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 paramDictionary = new Hashtable(); + +// var qparam = queryParameters.Split('&'); +// foreach (string p in qparam) +// { +// var items = p.Split('='); +// if (items.Length == 1) +// { +// paramDictionary.Add(items[0], null); +// } +// else +// { +// paramDictionary.Add(items[0], items[1]); +// } +// } // var sb = new StringBuilder(); -// var paramKeys = new List(paramDictionary.Keys); +// var paramKeys = new ArrayList(); + +// foreach (DictionaryEntry kvp in paramDictionary) +// { +// paramKeys.Add(kvp.Key); +// } + // paramKeys.Sort(StringComparer.Ordinal); // foreach (var p in paramKeys) // { // if (sb.Length > 0) // sb.Append("&"); -// sb.AppendFormat("{0}={1}", p, paramDictionary[p]); +// sb.Append($"{p}={paramDictionary[p]}"); // } // canonicalizedQueryParameters = sb.ToString(); @@ -127,7 +147,7 @@ // canonicalizedHeaderNames, // canonicalizedHeaders, // bodyHash); -// Logger.LogDebug($"\nCanonicalRequest:\n{canonicalRequest}"); +// Debug.WriteLine($"\nCanonicalRequest:\n{canonicalRequest}"); // // generate a hash of the canonical request, to go into signature computation // var canonicalRequestHashBytes @@ -136,40 +156,35 @@ // // construct the string to be signed // var stringToSign = new StringBuilder(); -// var dateStamp = requestDateTime.ToString(DateStringFormat, CultureInfo.InvariantCulture); -// Scope = string.Format("{0}/{1}/{2}/{3}", -// dateStamp, -// Region, -// Service, -// TERMINATOR); +// var dateStamp = requestDateTime.ToString(DateStringFormat); +// Scope = $"{dateStamp}/{Region}/{Service}/{TERMINATOR}"; -// stringToSign.AppendFormat("{0}-{1}\n{2}\n{3}\n", SCHEME, ALGORITHM, DateTimeStamp, Scope); +// stringToSign.Append($"{SCHEME}-{ALGORITHM}\n{DateTimeStamp}\n{Scope}\n"); // stringToSign.Append(ToHexString(canonicalRequestHashBytes, true)); -// Logger.LogDebug($"\nStringToSign:\n{stringToSign}"); +// Debug.WriteLine($"\nStringToSign:\n{stringToSign}"); // // compute the signing key -// SigningKey = DeriveSigningKey(HMACSHA256, awsSecretKey, Region, dateStamp, Service); +// SigningKey = DeriveSigningKey(awsSecretKey, Region, dateStamp, Service); -// var kha = KeyedHashAlgorithm.Create(HMACSHA256); -// kha.Key = SigningKey; +// var kha = new HMACSHA256(SigningKey); // // 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}"); +// Debug.WriteLine($"\nSignature:\n{signatureString}"); // // cache the computed signature ready for chunk 0 upload // LastComputedSignature = 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); +// authString.Append($"{SCHEME}-{ALGORITHM} "); +// authString.Append($"Credential={awsAccessKey}/{Scope}, "); +// authString.Append($"SignedHeaders={canonicalizedHeaderNames}, "); +// authString.Append($"Signature={signatureString}"); // var authorization = authString.ToString(); -// Logger.LogDebug($"\nAuthorization:\n{authorization}"); +// Debug.WriteLine($"\nAuthorization:\n{authorization}"); // return authorization; // } @@ -202,7 +217,7 @@ // + (remainingBytes > 0 ? CalculateChunkHeaderLength(remainingBytes) : 0) // + CalculateChunkHeaderLength(0); -// Logger.LogDebug($"\nComputed chunked content length for original length {originalLength} bytes, chunk size {chunkSize / 1024}KB is {chunkedContentLength} bytes"); +// Debug.WriteLine($"\nComputed chunked content length for original length {originalLength} bytes, chunk size {chunkSize / 1024}KB is {chunkedContentLength} bytes"); // return chunkedContentLength; // } @@ -258,7 +273,7 @@ // { // // shrink the chunkdata to fit // dataToChunk = new byte[userDataLen]; -// Array.Copy(userData, 0, dataToChunk, 0, userDataLen); +// Array.Copy(userData, 0, dataToChunk, 0, (int)userDataLen); // } // else // dataToChunk = userData; @@ -285,16 +300,15 @@ // ToHexString(CanonicalRequestHashAlgorithm.ComputeHash(Encoding.UTF8.GetBytes(nonsigExtension)), true) + "\n" + // ToHexString(CanonicalRequestHashAlgorithm.ComputeHash(dataToChunk), true); -// Logger.LogDebug($"\nChunkStringToSign:\n{chunkStringToSign}"); +// Debug.WriteLine($"\nChunkStringToSign:\n{chunkStringToSign}"); // // compute the V4 signature for the chunk // var chunkSignature -// = ToHexString(ComputeKeyedHash("HMACSHA256", -// SigningKey, +// = ToHexString(ComputeKeyedHash(SigningKey, // Encoding.UTF8.GetBytes(chunkStringToSign)), -// true); +// true); -// Logger.LogDebug($"\nChunkSignature:\n{chunkSignature}"); +// Debug.WriteLine($"\nChunkSignature:\n{chunkSignature}"); // // cache the signature to include with the next chunk's signature computation // this.LastComputedSignature = chunkSignature; @@ -305,7 +319,7 @@ // chunkHeader.Append(nonsigExtension + CHUNK_SIGNATURE_HEADER + chunkSignature); // chunkHeader.Append(CLRF); -// Logger.LogDebug($"\nChunkHeader:\n{chunkHeader}"); +// Debug.WriteLine($"\nChunkHeader:\n{chunkHeader}"); // try // { diff --git a/nanoFramework.Aws.IoTCore.Devices/AwsSignatureVersion4/SignerForPOST.cs b/nanoFramework.Aws.IoTCore.Devices/AwsSignatureVersion4/SignerForPOST.cs index 3fa1ca6..83b99a4 100644 --- a/nanoFramework.Aws.IoTCore.Devices/AwsSignatureVersion4/SignerForPOST.cs +++ b/nanoFramework.Aws.IoTCore.Devices/AwsSignatureVersion4/SignerForPOST.cs @@ -1,7 +1,11 @@ -//using System; +//// +//// Copyright (c) .NET Foundation and Contributors +//// See LICENSE file in the project root for full license information. +//// + +//using System; //using System.Collections; -//using System.Globalization; -//using System.Net.Http; +//using System.Diagnostics; //using System.Security.Cryptography; //using System.Text; @@ -13,19 +17,19 @@ // /// // public class SignerForPOST : SignerBase // { -// public string FormatCredentialStringForPolicy(DateTime dateTimeStamp) +// internal string FormatCredentialStringForPolicy(DateTime dateTimeStamp) // { // return "AKIAIOSFODNN7EXAMPLE/20130806/cn-north-1/s3/aws4_request"; // } -// public string FormatAlgorithmForPolicy +// internal string FormatAlgorithmForPolicy // { // get { return "AWS4-HMAC-SHA256"; } // } -// public string FormatDateTimeForPolicy(DateTime dateTimeStamp) +// internal string FormatDateTimeForPolicy(DateTime dateTimeStamp) // { -// return dateTimeStamp.ToString(ISO8601BasicFormat, CultureInfo.InvariantCulture); +// return dateTimeStamp.ToString(ISO8601BasicFormat); // } // /// @@ -53,23 +57,22 @@ // /// The computed authorization string for the request. This value needs to be set as the // /// header 'Authorization' on the subsequent HTTP request. // /// -// public string ComputeSignature(IDictionary headers, +// public string ComputeSignature(IDictionary 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 +// // first get the date and time for the subsequent request, and convert to ISO8601 format (without '-' and ':') // // for use in signature generation // var requestDateTime = DateTime.UtcNow; -// var dateTimeStamp = requestDateTime.ToString(ISO8601BasicFormat, CultureInfo.InvariantCulture); +// var dateTimeStamp = requestDateTime.ToString(ISO8601BasicFormat); // // 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; +// hostHeader += ":" + EndpointUri.Port; // FIXME: should use //if (!EndpointUri.IsDefaultPort) // headers.Add("Host", hostHeader); // // canonicalize the headers; we need the set of header names as well as the @@ -82,19 +85,36 @@ // 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 paramDictionary = new Hashtable(); + +// var qparam = queryParameters.Split('&'); +// foreach (string p in qparam) +// { +// var items = p.Split('='); +// if (items.Length == 1) +// { +// paramDictionary.Add(items[0], null); +// } +// else +// { +// paramDictionary.Add(items[0], items[1]); +// } +// } // var sb = new StringBuilder(); -// var paramKeys = new List(paramDictionary.Keys); +// var paramKeys = new ArrayList(); + +// foreach (DictionaryEntry kvp in paramDictionary) +// { +// paramKeys.Add(kvp.Key); +// } + // paramKeys.Sort(StringComparer.Ordinal); // foreach (var p in paramKeys) // { // if (sb.Length > 0) // sb.Append("&"); -// sb.AppendFormat("{0}={1}", p, paramDictionary[p]); +// sb.Append($"{p}={paramDictionary[p]}"); // } // canonicalizedQueryParameters = sb.ToString(); @@ -107,7 +127,7 @@ // canonicalizedHeaderNames, // canonicalizedHeaders, // bodyHash); -// Logger.LogDebug($"\nCanonicalRequest:\n{canonicalRequest}"); +// Debug.WriteLine($"\nCanonicalRequest:\n{canonicalRequest}"); // // generate a hash of the canonical request, to go into signature computation // var canonicalRequestHashBytes @@ -116,35 +136,30 @@ // // 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); +// var dateStamp = requestDateTime.ToString(DateStringFormat); +// var scope = $"{dateStamp}/{Region}/{Service}/{TERMINATOR}"; -// stringToSign.AppendFormat("{0}-{1}\n{2}\n{3}\n", SCHEME, ALGORITHM, dateTimeStamp, scope); +// stringToSign.Append($"{SCHEME}-{ALGORITHM}\n{dateTimeStamp}\n{scope}\n"); // stringToSign.Append(ToHexString(canonicalRequestHashBytes, true)); -// Logger.LogDebug($"\nStringToSign:\n{stringToSign}"); +// Debug.WriteLine($"\nStringToSign:\n{stringToSign}"); // // compute the signing key -// var kha = KeyedHashAlgorithm.Create(HMACSHA256); -// kha.Key = DeriveSigningKey(HMACSHA256, awsSecretKey, Region, dateStamp, Service); +// var kha = new HMACSHA256(DeriveSigningKey(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}"); +// Debug.WriteLine($"\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); +// authString.Append($"{SCHEME}-{ALGORITHM} "); +// authString.Append($"Credential={awsAccessKey}/{scope}, "); +// authString.Append($"SignedHeaders={canonicalizedHeaderNames}, "); +// authString.Append($"Signature={signatureString}"); // var authorization = authString.ToString(); -// Logger.LogDebug($"\nAuthorization:\n{authorization}"); +// Debug.WriteLine($"\nAuthorization:\n{authorization}"); // return authorization; // } diff --git a/nanoFramework.Aws.IoTCore.Devices/AwsSignatureVersion4/SignerForQueryParameterAuth.cs b/nanoFramework.Aws.IoTCore.Devices/AwsSignatureVersion4/SignerForQueryParameterAuth.cs index 9e93559..812af6f 100644 --- a/nanoFramework.Aws.IoTCore.Devices/AwsSignatureVersion4/SignerForQueryParameterAuth.cs +++ b/nanoFramework.Aws.IoTCore.Devices/AwsSignatureVersion4/SignerForQueryParameterAuth.cs @@ -1,11 +1,15 @@  +// +// Copyright (c) .NET Foundation and Contributors +// See LICENSE file in the project root for full license information. +// + using System; using System.Collections; -using System.Globalization; -using System.Net.Http.Headers; -using System.Net.Http; +using System.Diagnostics; using System.Security.Cryptography; using System.Text; +using System.Web; namespace nanoFramework.Aws.SignatureVersion4 { @@ -39,33 +43,28 @@ public class SignerForQueryParameterAuth : SignerBase /// /// The string expressing the Signature V4 components to add to query parameters. /// - public string ComputeSignature(IDictionary headers, + public string ComputeSignature(IDictionary 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 + // first get the date and time for the subsequent request, and convert to ISO8601 format (without '-' and ':') // for use in signature generation var requestDateTime = DateTime.UtcNow; var dateTimeStamp = requestDateTime.ToString(ISO8601BasicFormat); // extract the host portion of the endpoint to include in the signature calculation, // unless already set - if (!headers.ContainsKey("Host")) + if (!headers.Contains("Host")) { var hostHeader = EndpointUri.Host; - if (!EndpointUri.IsDefaultPort) - hostHeader += ":" + EndpointUri.Port; + hostHeader += ":" + EndpointUri.Port; // FIXME: should use //if (!EndpointUri.IsDefaultPort) headers.Add("Host", hostHeader); } var dateStamp = requestDateTime.ToString(DateStringFormat); - var scope = string.Format("{0}/{1}/{2}/{3}", - dateStamp, - Region, - Service, - TERMINATOR); + var scope = $"{dateStamp}/{Region}/{Service}/{TERMINATOR}"; // canonicalized headers need to be expressed in the query // parameters processed in the signature @@ -76,33 +75,46 @@ public string ComputeSignature(IDictionary headers, // Signature V4 and (b) canonicalize the set before they go into the // signature calculation. Note that this assumes parameter names and // values added outside this routine are already url encoded - var paramDictionary = new Dictionary(StringComparer.OrdinalIgnoreCase); + var paramDictionary = new Hashtable(); if (!string.IsNullOrEmpty(queryParameters)) { - paramDictionary = queryParameters.Split('&').Select(p => p.Split('=')) - .ToDictionary(nameval => nameval[0], - nameval => nameval.Length > 1 - ? nameval[1] : ""); + var qparam = queryParameters.Split('&'); + foreach (string p in qparam) + { + var items = p.Split('='); + if (items.Length == 1) + { + paramDictionary.Add(items[0], null); + } + else + { + paramDictionary.Add(items[0], items[1]); + } + } } // add the fixed authorization params required by Signature V4 - paramDictionary.Add(X_Amz_Algorithm, HttpHelpers.UrlEncode(string.Format("{0}-{1}", SCHEME, ALGORITHM))); - paramDictionary.Add(X_Amz_Credential, HttpHelpers.UrlEncode(string.Format("{0}/{1}", awsAccessKey, scope))); - paramDictionary.Add(X_Amz_SignedHeaders, HttpHelpers.UrlEncode(canonicalizedHeaderNames)); + paramDictionary.Add(X_Amz_Algorithm, HttpUtility.UrlEncode($"{SCHEME}-{ALGORITHM}")); + paramDictionary.Add(X_Amz_Credential, HttpUtility.UrlEncode($"{awsAccessKey}/{scope}")); + paramDictionary.Add(X_Amz_SignedHeaders, HttpUtility.UrlEncode(canonicalizedHeaderNames)); // x-amz-date is now added as a query parameter, not a header, but still needs to be in ISO8601 basic form - paramDictionary.Add(X_Amz_Date, HttpHelpers.UrlEncode(dateTimeStamp)); + paramDictionary.Add(X_Amz_Date, HttpUtility.UrlEncode(dateTimeStamp)); // build the expanded canonical query parameter string that will go into the // signature computation var sb = new StringBuilder(); - var paramKeys = new List(paramDictionary.Keys); + var paramKeys = new ArrayList(); + foreach (DictionaryEntry kvp in paramDictionary) + { + paramKeys.Add(kvp.Key); + } paramKeys.Sort(StringComparer.Ordinal); foreach (var p in paramKeys) { if (sb.Length > 0) sb.Append("&"); - sb.Append(string.Format("{0}={1}", p, paramDictionary[p])); + sb.Append($"{p}={paramDictionary[p]}"); } var canonicalizedQueryParameters = sb.ToString(); @@ -113,7 +125,7 @@ public string ComputeSignature(IDictionary headers, canonicalizedHeaderNames, canonicalizedHeaders, bodyHash); - //Logger.LogDebug($"\nCanonicalRequest:\n{canonicalRequest}"); + Debug.WriteLine($"\nCanonicalRequest:\n{canonicalRequest}"); byte[] canonicalRequestHashBytes = CanonicalRequestHashAlgorithm.ComputeHash(Encoding.UTF8.GetBytes(canonicalRequest)); @@ -121,20 +133,19 @@ byte[] canonicalRequestHashBytes // construct the string to be signed var stringToSign = new StringBuilder(); - stringToSign.Append(string.Format("{0}-{1}\n{2}\n{3}\n", SCHEME, ALGORITHM, dateTimeStamp, scope)); + stringToSign.Append($"{SCHEME}-{ALGORITHM}\n{dateTimeStamp}\n{scope}\n"); stringToSign.Append(ToHexString(canonicalRequestHashBytes, true)); - //Logger.LogDebug($"\nStringToSign:\n{stringToSign}"); + Debug.WriteLine($"\nStringToSign:\n{stringToSign}"); // compute the multi-stage signing key - KeyedHashAlgorithm kha = KeyedHashAlgorithm.Create(HMACSHA256); - kha.Key = DeriveSigningKey(HMACSHA256, awsSecretKey, Region, dateStamp, Service); + var kha = new HMACSHA256(DeriveSigningKey(awsSecretKey, Region, dateStamp, Service)); // compute the final signature for the request, place into the result and return to the // user to be embedded in the request as needed var signature = kha.ComputeHash(Encoding.UTF8.GetBytes(stringToSign.ToString())); var signatureString = ToHexString(signature, true); - //Logger.LogDebug($"\nSignature:\n{signatureString}"); + Debug.WriteLine($"\nSignature:\n{signatureString}"); // form up the authorization parameters for the caller to place in the query string var authString = new StringBuilder(); @@ -150,13 +161,13 @@ byte[] canonicalRequestHashBytes { if (authString.Length > 0) authString.Append("&"); - authString.Append(string.Format("{0}={1}", p, paramDictionary[p])); + authString.Append($"{p}={paramDictionary[p]}"); } - authString.Append(string.Format("&{0}={1}", X_Amz_Signature, signatureString)); + authString.Append($"&{X_Amz_Signature}={signatureString}"); var authorization = authString.ToString(); - //Logger.LogDebug($"\nAuthorization:\n{authorization}"); + Debug.WriteLine($"\nAuthorization:\n{authorization}"); return authorization; } diff --git a/nanoFramework.Aws.IoTCore.Devices/AwsSignatureVersion4/nfArrayListExtensions.cs b/nanoFramework.Aws.IoTCore.Devices/AwsSignatureVersion4/nfArrayListExtensions.cs new file mode 100644 index 0000000..a64046e --- /dev/null +++ b/nanoFramework.Aws.IoTCore.Devices/AwsSignatureVersion4/nfArrayListExtensions.cs @@ -0,0 +1,137 @@ +// +// Copyright (c) .NET Foundation and Contributors +// See LICENSE file in the project root for full license information. +// + +namespace System.Collections +{ + /// + /// Extension methods used for canonicalization. + /// + public static class nfArrayListExtensions + { + /// + /// Sort an ArrayList + /// + /// Items in the ArrayList + /// The Comparer + public static void Sort(this ArrayList items, IComparer comparer) + { + int i; + int j; + object tmpItem; + + for (i = 0; i <= items.Count -1; i++) + { + tmpItem = items[i]; + j = i; + + while ((j > 0) && (comparer.Compare(items[j - 1], tmpItem) > 0)) + { + items[j] = items[j - 1]; + j--; + } + + items[j] = tmpItem; + } + } + } + + + /// + /// String Comparers + /// + public abstract class StringComparer : IComparer + { + private static readonly StringComparer _ordinal = new OrdinalComparer(false); + private static readonly StringComparer _ordinalIgnoreCase = new OrdinalComparer(true); + + /// + /// Ordinal Comparer + /// + public static StringComparer Ordinal + { + get + { + return _ordinal; + } + } + + /// + /// Ordinal Comparer (ignoring case) + /// + public static StringComparer OrdinalIgnoreCase + { + get + { + return _ordinalIgnoreCase; + } + } + + /// + /// Compares two objects + /// + /// Object x + /// Object y + /// The result + /// + public int Compare(object x, object y) + { + if (x == y) return 0; + if (x == null) return -1; + if (y == null) return 1; + + string sa = x as string; + if (sa != null) + { + string sb = y as string; + if (sb != null) + { + return Compare(sa, sb); + } + } + + IComparable ia = x as IComparable; + if (ia != null) + { + return ia.CompareTo(y); + } + + throw new ArgumentException("Argument_ImplementIComparable"); + } + + /// + /// Compares two strings + /// + /// String x + /// String y + /// The result. + public abstract int Compare(string x, string y); + } + + internal sealed class OrdinalComparer : StringComparer + { + private bool _ignoreCase; + + internal OrdinalComparer(bool ignoreCase) + { + _ignoreCase = ignoreCase; + } + + public override int Compare(string x, string y) + { + if (ReferenceEquals(x, y)) return 0; + if (x == null) return -1; + if (y == null) return 1; + + if (_ignoreCase) + { + return string.Compare(x.ToLower(), y.ToLower()); + } + + return string.Compare(x, y); + } + + } + +} diff --git a/nanoFramework.Aws.IoTCore.Devices/WebsocketConnectionClient.cs b/nanoFramework.Aws.IoTCore.Devices/WebsocketConnectionClient.cs index 7fff04f..515b05f 100644 --- a/nanoFramework.Aws.IoTCore.Devices/WebsocketConnectionClient.cs +++ b/nanoFramework.Aws.IoTCore.Devices/WebsocketConnectionClient.cs @@ -15,13 +15,26 @@ namespace nanoFramework.Aws.IoTCore.Devices public class WebsocketConnectionClient : IDisposable { - public Uri Host { get; set; } + /// + /// The AWS IoT Core fully qualified domain name + /// + public Uri EndpointUri { get; set; } + /// + /// The AWS IoT Core Region + /// public string Region { get; set; } + /// + /// The AWS IoT Core Access Key + /// public string AccessKey { get; set; } + /// + /// The AWS IoT Core Secret Key + /// public string SecretKey { get; set; } + const int _wssPort = 443; //Default WSS port. - WebSocket _webSocket = null; + //WebSocket _webSocket = null; /// /// Creates a new MQTT over WebSocket Connection Client @@ -37,9 +50,9 @@ public WebsocketConnectionClient() //wss://iot-endpoint/mqtt // sign with Signature Version 4 - var signer = new SignerForQueryParameterAuth + var v4signer = new SignerForQueryParameterAuth { - EndpointUri = Host, + EndpointUri = EndpointUri, HttpMethod = "GET", Service = "iotdevicegateway", Region = Region diff --git a/nanoFramework.Aws.IoTCore.Devices/nanoFramework.Aws.IoTCore.Devices.nfproj b/nanoFramework.Aws.IoTCore.Devices/nanoFramework.Aws.IoTCore.Devices.nfproj index 20ffaa1..b0d70d4 100644 --- a/nanoFramework.Aws.IoTCore.Devices/nanoFramework.Aws.IoTCore.Devices.nfproj +++ b/nanoFramework.Aws.IoTCore.Devices/nanoFramework.Aws.IoTCore.Devices.nfproj @@ -31,6 +31,9 @@ + + + @@ -98,6 +101,9 @@ ..\packages\nanoFramework.System.Net.WebSockets.1.1.57\lib\System.Net.WebSockets.dll + + ..\packages\nanoFramework.System.Text.RegularExpressions.1.1.37\lib\System.Text.RegularExpressions.dll + ..\packages\nanoFramework.System.Threading.1.1.32\lib\System.Threading.dll True diff --git a/nanoFramework.Aws.IoTCore.Devices/packages.config b/nanoFramework.Aws.IoTCore.Devices/packages.config index e804dcf..37f9444 100644 --- a/nanoFramework.Aws.IoTCore.Devices/packages.config +++ b/nanoFramework.Aws.IoTCore.Devices/packages.config @@ -11,6 +11,7 @@ + \ No newline at end of file diff --git a/nanoFramework.Aws.IoTCore.Devices/packages.lock.json b/nanoFramework.Aws.IoTCore.Devices/packages.lock.json index 9aebdaf..53d3011 100644 --- a/nanoFramework.Aws.IoTCore.Devices/packages.lock.json +++ b/nanoFramework.Aws.IoTCore.Devices/packages.lock.json @@ -68,6 +68,12 @@ "resolved": "1.2.54", "contentHash": "k3OutSNRMs9di42LQ+5GbpHBY07aMEZWGkaS3Mj3ZU4cWqJc4deFGzRd+LBFQl1mRGdQaM5sl/euTZdcg8R9Zg==" }, + "nanoFramework.System.Text.RegularExpressions": { + "type": "Direct", + "requested": "[1.1.37, 1.1.37]", + "resolved": "1.1.37", + "contentHash": "l0Vv7SEF2+59Ps183Sf8sSVZKvfFBPRP5gTYtnv5cdEm+KntXRhIy3u9Cp8XnueMrLkHEKYDQ6VG6gz4n7tbfA==" + }, "nanoFramework.System.Threading": { "type": "Direct", "requested": "[1.1.32, 1.1.32]", From 6ed5a9431aaaac61051d91a45d5794ab7c1822b0 Mon Sep 17 00:00:00 2001 From: Robin Jones Date: Mon, 14 Aug 2023 01:55:54 +0100 Subject: [PATCH 03/14] Update nuget. --- .../nanoFramework.Aws.IoTCore.Devices.nfproj | 2 +- nanoFramework.Aws.IoTCore.Devices/packages.lock.json | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/nanoFramework.Aws.IoTCore.Devices/nanoFramework.Aws.IoTCore.Devices.nfproj b/nanoFramework.Aws.IoTCore.Devices/nanoFramework.Aws.IoTCore.Devices.nfproj index b0d70d4..f20f442 100644 --- a/nanoFramework.Aws.IoTCore.Devices/nanoFramework.Aws.IoTCore.Devices.nfproj +++ b/nanoFramework.Aws.IoTCore.Devices/nanoFramework.Aws.IoTCore.Devices.nfproj @@ -96,7 +96,7 @@ True - ..\packages\nanoFramework.System.Net.Http.1.5.84\lib\System.Net.Http.dll + ..\packages\nanoFramework.System.Net.Http.1.5.99\lib\System.Net.Http.dll ..\packages\nanoFramework.System.Net.WebSockets.1.1.57\lib\System.Net.WebSockets.dll diff --git a/nanoFramework.Aws.IoTCore.Devices/packages.lock.json b/nanoFramework.Aws.IoTCore.Devices/packages.lock.json index 53d3011..c88f9ca 100644 --- a/nanoFramework.Aws.IoTCore.Devices/packages.lock.json +++ b/nanoFramework.Aws.IoTCore.Devices/packages.lock.json @@ -52,9 +52,9 @@ }, "nanoFramework.System.Net.Http": { "type": "Direct", - "requested": "[1.5.84, 1.5.84]", - "resolved": "1.5.84", - "contentHash": "XiG+t10FX3Lyb+d4A9EF0IDp7wS7oHXyKaqTBI7tvroxgMScNXVI6M/a6QlISepYUzO4xQ559X8d76wcsBsDnw==" + "requested": "[1.5.99, 1.5.99]", + "resolved": "1.5.99", + "contentHash": "q611hrYfuZL+4n/JiFSguLK+0VYWiX34tQzgkC7mogpeNRobtAY9mgQe0urL++o1V1IKzit5W1bvUb2LN0ErWg==" }, "nanoFramework.System.Net.WebSockets": { "type": "Direct", From 91b8121d77991b85cd5d6b3fab8ee2275b7c898f Mon Sep 17 00:00:00 2001 From: Robin Jones Date: Mon, 14 Aug 2023 01:58:13 +0100 Subject: [PATCH 04/14] Uncomment other SigV4 classes. --- .../SignerForAuthorizationHeader.cs | 304 ++++---- .../SignerForChunkedUpload.cs | 686 +++++++++--------- .../AwsSignatureVersion4/SignerForPOST.cs | 334 ++++----- 3 files changed, 662 insertions(+), 662 deletions(-) diff --git a/nanoFramework.Aws.IoTCore.Devices/AwsSignatureVersion4/SignerForAuthorizationHeader.cs b/nanoFramework.Aws.IoTCore.Devices/AwsSignatureVersion4/SignerForAuthorizationHeader.cs index f93603e..e0bddee 100644 --- a/nanoFramework.Aws.IoTCore.Devices/AwsSignatureVersion4/SignerForAuthorizationHeader.cs +++ b/nanoFramework.Aws.IoTCore.Devices/AwsSignatureVersion4/SignerForAuthorizationHeader.cs @@ -1,152 +1,152 @@ -//// -//// Copyright (c) .NET Foundation and Contributors -//// See LICENSE file in the project root for full license information. -//// - -//using System; -//using System.Collections; -//using System.Diagnostics; -//using System.Security.Cryptography; -//using System.Text; - -//namespace nanoFramework.Aws.SignatureVersion4 -//{ -// /// -// /// AWS Signature Version 4 signer for signing requests -// /// using an 'Authorization' header. -// /// -// public class SignerForAuthorizationHeader : SignerBase -// { -// /// -// /// Computes an Version 4 signature for a request, ready for inclusion as an -// /// 'Authorization' header. -// /// -// /// -// /// The request headers; 'Host' and 'X-Amz-Date' will be added to this set. -// /// -// /// -// /// Any query parameters that will be added to the endpoint. The parameters -// /// should be specified in canonical format. -// /// -// /// -// /// Precomputed SHA256 hash of the request body content; this value should also -// /// be set as the header 'X-Amz-Content-SHA256' for non-streaming uploads. -// /// -// /// -// /// The user's AWS Access Key. -// /// -// /// -// /// The user's AWS Secret Key. -// /// -// /// -// /// The computed authorization string for the request. This value needs to be set as the -// /// header 'Authorization' on the subsequent HTTP request. -// /// -// public string ComputeSignature(IDictionary headers, -// string queryParameters, -// string bodyHash, -// string awsAccessKey, -// string awsSecretKey) -// { -// // first get the date and time for the subsequent request, and convert to ISO8601 format (without '-' and ':') -// // for use in signature generation -// var requestDateTime = DateTime.UtcNow; -// var dateTimeStamp = requestDateTime.ToString(ISO8601BasicFormat); - -// // update the headers with required 'x-amz-date' and 'host' values -// headers.Add(X_Amz_Date, dateTimeStamp); - -// var hostHeader = EndpointUri.Host; -// hostHeader += ":" + EndpointUri.Port; // FIXME: should use //if (!EndpointUri.IsDefaultPort) -// 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 = new Hashtable(); - -// var qparam = queryParameters.Split('&'); -// foreach (string p in qparam) -// { -// var items = p.Split('='); -// if (items.Length == 1) -// { -// paramDictionary.Add(items[0], null); -// } -// else -// { -// paramDictionary.Add(items[0], items[1]); -// } -// } - -// var sb = new StringBuilder(); -// var paramKeys = new ArrayList(); - -// foreach (DictionaryEntry kvp in paramDictionary) -// { -// paramKeys.Add(kvp.Key); -// } - -// paramKeys.Sort(StringComparer.Ordinal); -// foreach (var p in paramKeys) -// { -// if (sb.Length > 0) -// sb.Append("&"); -// sb.Append($"{p}={paramDictionary[p]}"); -// } - -// canonicalizedQueryParameters = sb.ToString(); -// } - -// // canonicalize the various components of the request -// var canonicalRequest = CanonicalizeRequest(EndpointUri, -// HttpMethod, -// canonicalizedQueryParameters, -// canonicalizedHeaderNames, -// canonicalizedHeaders, -// bodyHash); -// Debug.WriteLine($"\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); -// var scope = $"{dateStamp}/{Region}/{Service}/{TERMINATOR}"; - -// stringToSign.Append($"{SCHEME}-{ALGORITHM}\n{dateTimeStamp}\n{scope}\n"); -// stringToSign.Append(ToHexString(canonicalRequestHashBytes, true)); - -// Debug.WriteLine($"\nStringToSign:\n{stringToSign}"); - -// // compute the signing key -// var kha = new HMACSHA256(DeriveSigningKey(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); -// Debug.WriteLine($"\nSignature:\n{signatureString}"); - -// var authString = new StringBuilder(); -// authString.Append($"{SCHEME}-{ALGORITHM} "); -// authString.Append($"Credential={awsAccessKey}/{scope}, "); -// authString.Append($"SignedHeaders={canonicalizedHeaderNames}, "); -// authString.Append($"Signature={signatureString}"); - -// var authorization = authString.ToString(); -// Debug.WriteLine($"\nAuthorization:\n{authorization}"); - -// return authorization; -// } -// } -//} +// +// Copyright (c) .NET Foundation and Contributors +// See LICENSE file in the project root for full license information. +// + +using System; +using System.Collections; +using System.Diagnostics; +using System.Security.Cryptography; +using System.Text; + +namespace nanoFramework.Aws.SignatureVersion4 +{ + /// + /// AWS Signature Version 4 signer for signing requests + /// using an 'Authorization' header. + /// + public class SignerForAuthorizationHeader : SignerBase + { + /// + /// Computes an Version 4 signature for a request, ready for inclusion as an + /// 'Authorization' header. + /// + /// + /// The request headers; 'Host' and 'X-Amz-Date' will be added to this set. + /// + /// + /// Any query parameters that will be added to the endpoint. The parameters + /// should be specified in canonical format. + /// + /// + /// Precomputed SHA256 hash of the request body content; this value should also + /// be set as the header 'X-Amz-Content-SHA256' for non-streaming uploads. + /// + /// + /// The user's AWS Access Key. + /// + /// + /// The user's AWS Secret Key. + /// + /// + /// The computed authorization string for the request. This value needs to be set as the + /// header 'Authorization' on the subsequent HTTP request. + /// + public string ComputeSignature(IDictionary headers, + string queryParameters, + string bodyHash, + string awsAccessKey, + string awsSecretKey) + { + // first get the date and time for the subsequent request, and convert to ISO8601 format (without '-' and ':') + // for use in signature generation + var requestDateTime = DateTime.UtcNow; + var dateTimeStamp = requestDateTime.ToString(ISO8601BasicFormat); + + // update the headers with required 'x-amz-date' and 'host' values + headers.Add(X_Amz_Date, dateTimeStamp); + + var hostHeader = EndpointUri.Host; + hostHeader += ":" + EndpointUri.Port; // FIXME: should use //if (!EndpointUri.IsDefaultPort) + 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 = new Hashtable(); + + var qparam = queryParameters.Split('&'); + foreach (string p in qparam) + { + var items = p.Split('='); + if (items.Length == 1) + { + paramDictionary.Add(items[0], null); + } + else + { + paramDictionary.Add(items[0], items[1]); + } + } + + var sb = new StringBuilder(); + var paramKeys = new ArrayList(); + + foreach (DictionaryEntry kvp in paramDictionary) + { + paramKeys.Add(kvp.Key); + } + + paramKeys.Sort(StringComparer.Ordinal); + foreach (var p in paramKeys) + { + if (sb.Length > 0) + sb.Append("&"); + sb.Append($"{p}={paramDictionary[p]}"); + } + + canonicalizedQueryParameters = sb.ToString(); + } + + // canonicalize the various components of the request + var canonicalRequest = CanonicalizeRequest(EndpointUri, + HttpMethod, + canonicalizedQueryParameters, + canonicalizedHeaderNames, + canonicalizedHeaders, + bodyHash); + Debug.WriteLine($"\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); + var scope = $"{dateStamp}/{Region}/{Service}/{TERMINATOR}"; + + stringToSign.Append($"{SCHEME}-{ALGORITHM}\n{dateTimeStamp}\n{scope}\n"); + stringToSign.Append(ToHexString(canonicalRequestHashBytes, true)); + + Debug.WriteLine($"\nStringToSign:\n{stringToSign}"); + + // compute the signing key + var kha = new HMACSHA256(DeriveSigningKey(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); + Debug.WriteLine($"\nSignature:\n{signatureString}"); + + var authString = new StringBuilder(); + authString.Append($"{SCHEME}-{ALGORITHM} "); + authString.Append($"Credential={awsAccessKey}/{scope}, "); + authString.Append($"SignedHeaders={canonicalizedHeaderNames}, "); + authString.Append($"Signature={signatureString}"); + + var authorization = authString.ToString(); + Debug.WriteLine($"\nAuthorization:\n{authorization}"); + + return authorization; + } + } +} diff --git a/nanoFramework.Aws.IoTCore.Devices/AwsSignatureVersion4/SignerForChunkedUpload.cs b/nanoFramework.Aws.IoTCore.Devices/AwsSignatureVersion4/SignerForChunkedUpload.cs index 50dec03..a4c0240 100644 --- a/nanoFramework.Aws.IoTCore.Devices/AwsSignatureVersion4/SignerForChunkedUpload.cs +++ b/nanoFramework.Aws.IoTCore.Devices/AwsSignatureVersion4/SignerForChunkedUpload.cs @@ -1,343 +1,343 @@ -//// -//// Copyright (c) .NET Foundation and Contributors -//// See LICENSE file in the project root for full license information. -//// - -//using System; -//using System.Collections; -//using System.Diagnostics; -//using System.Security.Cryptography; -//using System.Text; - - -//namespace nanoFramework.Aws.SignatureVersion4 -//{ -// /// -// /// AWS Signature Version 4 signer for signing 'chunked' uploads -// /// using an Authorization header. -// /// -// public class SignerForChunkedUpload : SignerBase -// { -// // SHA256 substitute marker used in place of x-amz-content-sha256 when employing -// // chunked uploads -// internal const string STREAMING_BODY_SHA256 = "STREAMING-AWS4-HMAC-SHA256-PAYLOAD"; - -// static readonly string CLRF = "\r\n"; -// static readonly string CHUNK_STRING_TO_SIGN_PREFIX = "AWS4-HMAC-SHA256-PAYLOAD"; -// static readonly string CHUNK_SIGNATURE_HEADER = ";chunk-signature="; -// static readonly int SIGNATURE_LENGTH = 64; -// static byte[] FINAL_CHUNK = new byte[0]; - -// /// -// /// Tracks the previously computed signature value; for chunk 0 this will -// /// contain the signature included in the Authorization header. For subsequent -// /// chunks it contains the computed signature of the prior chunk. -// /// -// public string LastComputedSignature { get; private set; } - -// /// -// /// Date and time of the original signing computation, in ISO 8601 basic format, -// /// reused for each chunk -// /// -// public string DateTimeStamp { get; private set; } - -// /// -// /// The scope value of the original signing computation, reused for each chunk -// /// -// public string Scope { get; private set; } - -// /// -// /// The derived signing key used in the original signature computation and -// /// re-used for each chunk -// /// -// byte[] SigningKey { get; set; } - -// /// -// /// Computes an AWS4 signature for a request, ready for inclusion as an -// /// 'Authorization' header. -// /// -// /// -// /// The request headers; 'Host' and 'X-Amz-Date' will be added to this set. -// /// -// /// -// /// Any query parameters that will be added to the endpoint. The parameters -// /// should be specified in canonical format. -// /// -// /// -// /// Precomputed SHA256 hash of the request body content; this value should also -// /// be set as the header 'X-Amz-Content-SHA256' for non-streaming uploads. -// /// -// /// -// /// The user's AWS Access Key. -// /// -// /// -// /// The user's AWS Secret Key. -// /// -// /// -// /// The computed authorization string for the request. This value needs to be set as the -// /// header 'Authorization' on the subsequent HTTP request. -// /// -// public string ComputeSignature(IDictionary headers, -// string queryParameters, -// string bodyHash, -// string awsAccessKey, -// string awsSecretKey) -// { -// // first get the date and time for the subsequent request, and convert to ISO8601 format (without '-' and ':') -// // for use in signature generation -// var requestDateTime = DateTime.UtcNow; -// DateTimeStamp = requestDateTime.ToString(ISO8601BasicFormat); - -// // update the headers with required 'x-amz-date' and 'host' values -// headers.Add(X_Amz_Date, DateTimeStamp); - -// var hostHeader = EndpointUri.Host; -// hostHeader += ":" + EndpointUri.Port; // FIXME: should use //if (!EndpointUri.IsDefaultPort) -// 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 = new Hashtable(); - -// var qparam = queryParameters.Split('&'); -// foreach (string p in qparam) -// { -// var items = p.Split('='); -// if (items.Length == 1) -// { -// paramDictionary.Add(items[0], null); -// } -// else -// { -// paramDictionary.Add(items[0], items[1]); -// } -// } - -// var sb = new StringBuilder(); -// var paramKeys = new ArrayList(); - -// foreach (DictionaryEntry kvp in paramDictionary) -// { -// paramKeys.Add(kvp.Key); -// } - -// paramKeys.Sort(StringComparer.Ordinal); -// foreach (var p in paramKeys) -// { -// if (sb.Length > 0) -// sb.Append("&"); -// sb.Append($"{p}={paramDictionary[p]}"); -// } - -// canonicalizedQueryParameters = sb.ToString(); -// } - -// // canonicalize the various components of the request -// var canonicalRequest = CanonicalizeRequest(EndpointUri, -// HttpMethod, -// canonicalizedQueryParameters, -// canonicalizedHeaderNames, -// canonicalizedHeaders, -// bodyHash); -// Debug.WriteLine($"\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); -// Scope = $"{dateStamp}/{Region}/{Service}/{TERMINATOR}"; - -// stringToSign.Append($"{SCHEME}-{ALGORITHM}\n{DateTimeStamp}\n{Scope}\n"); -// stringToSign.Append(ToHexString(canonicalRequestHashBytes, true)); - -// Debug.WriteLine($"\nStringToSign:\n{stringToSign}"); - -// // compute the signing key -// SigningKey = DeriveSigningKey(awsSecretKey, Region, dateStamp, Service); - -// var kha = new HMACSHA256(SigningKey); - -// // compute the AWS4 signature and return it -// var signature = kha.ComputeHash(Encoding.UTF8.GetBytes(stringToSign.ToString())); -// var signatureString = ToHexString(signature, true); -// Debug.WriteLine($"\nSignature:\n{signatureString}"); - -// // cache the computed signature ready for chunk 0 upload -// LastComputedSignature = signatureString; - -// var authString = new StringBuilder(); -// authString.Append($"{SCHEME}-{ALGORITHM} "); -// authString.Append($"Credential={awsAccessKey}/{Scope}, "); -// authString.Append($"SignedHeaders={canonicalizedHeaderNames}, "); -// authString.Append($"Signature={signatureString}"); - -// var authorization = authString.ToString(); -// Debug.WriteLine($"\nAuthorization:\n{authorization}"); - -// return authorization; -// } - -// /// -// /// Calculates the expanded payload size of our data when it is chunked -// /// -// /// -// /// The true size of the data payload to be uploaded -// /// -// /// -// /// The size of each chunk we intend to send; each chunk will be -// /// prefixed with signed header data, expanding the overall size -// /// by a determinable amount -// /// -// /// -// /// The overall payload size to use as content-length on a chunked upload -// /// -// public long CalculateChunkedContentLength(long originalLength, long chunkSize) -// { -// if (originalLength <= 0) -// throw new ArgumentOutOfRangeException("originalLength"); -// if (chunkSize <= 0) -// throw new ArgumentOutOfRangeException("chunkSize"); - -// var maxSizeChunks = originalLength / chunkSize; -// var remainingBytes = originalLength % chunkSize; - -// var chunkedContentLength = maxSizeChunks * CalculateChunkHeaderLength(chunkSize) -// + (remainingBytes > 0 ? CalculateChunkHeaderLength(remainingBytes) : 0) -// + CalculateChunkHeaderLength(0); - -// Debug.WriteLine($"\nComputed chunked content length for original length {originalLength} bytes, chunk size {chunkSize / 1024}KB is {chunkedContentLength} bytes"); -// return chunkedContentLength; -// } - -// /// -// /// Returns the size of a chunk header, which only varies depending -// /// on the selected chunk size -// /// -// /// -// /// The intended size of each chunk; this is placed into the chunk -// /// header -// /// -// /// -// /// The overall size of the header that will prefix the user data in -// /// each chunk -// /// -// static long CalculateChunkHeaderLength(long chunkSize) -// { -// return chunkSize.ToString("X").Length -// + CHUNK_SIGNATURE_HEADER.Length -// + SIGNATURE_LENGTH -// + CLRF.Length -// + chunkSize -// + CLRF.Length; -// } - -// /// -// /// Returns a chunk for upload consisting of the signed 'header' or chunk -// /// prefix plus the user data. The signature of the chunk incorporates the -// /// signature of the previous chunk (or, if the first chunk, the signature -// /// of the headers portion of the request). -// /// -// /// -// /// The length of the user data contained in userData -// /// -// /// -// /// Contains the user data to be sent in the upload chunk -// /// -// /// -// /// A new buffer of data for upload containing the chunk header plus user data -// /// -// public byte[] ConstructSignedChunk(long userDataLen, byte[] userData) -// { -// // to keep our computation routine signatures simple, if the userData -// // buffer contains less data than it could, shrink it. Note the special case -// // to handle the requirement that we send an empty chunk to complete -// // our chunked upload. -// byte[] dataToChunk; -// if (userDataLen == 0) -// dataToChunk = FINAL_CHUNK; -// else -// { -// if (userDataLen < userData.Length) -// { -// // shrink the chunkdata to fit -// dataToChunk = new byte[userDataLen]; -// Array.Copy(userData, 0, dataToChunk, 0, (int)userDataLen); -// } -// else -// dataToChunk = userData; -// } - -// var chunkHeader = new StringBuilder(); - -// // start with size of user data -// chunkHeader.Append(dataToChunk.Length.ToString("X")); - -// // nonsig-extension; we have none in these samples -// const string nonsigExtension = ""; - -// // if this is the first chunk, we package it with the signing result -// // of the request headers, otherwise we use the cached signature -// // of the previous chunk - -// // sig-extension -// var chunkStringToSign = -// CHUNK_STRING_TO_SIGN_PREFIX + "\n" + -// DateTimeStamp + "\n" + -// Scope + "\n" + -// LastComputedSignature + "\n" + -// ToHexString(CanonicalRequestHashAlgorithm.ComputeHash(Encoding.UTF8.GetBytes(nonsigExtension)), true) + "\n" + -// ToHexString(CanonicalRequestHashAlgorithm.ComputeHash(dataToChunk), true); - -// Debug.WriteLine($"\nChunkStringToSign:\n{chunkStringToSign}"); - -// // compute the V4 signature for the chunk -// var chunkSignature -// = ToHexString(ComputeKeyedHash(SigningKey, -// Encoding.UTF8.GetBytes(chunkStringToSign)), -// true); - -// Debug.WriteLine($"\nChunkSignature:\n{chunkSignature}"); - -// // cache the signature to include with the next chunk's signature computation -// this.LastComputedSignature = chunkSignature; - -// // construct the actual chunk, comprised of the non-signed extensions, the -// // 'headers' we just signed and their signature, plus a newline then copy -// // that plus the user's data to a payload to be written to the request stream -// chunkHeader.Append(nonsigExtension + CHUNK_SIGNATURE_HEADER + chunkSignature); -// chunkHeader.Append(CLRF); - -// Debug.WriteLine($"\nChunkHeader:\n{chunkHeader}"); - -// try -// { -// var header = Encoding.UTF8.GetBytes(chunkHeader.ToString()); -// var trailer = Encoding.UTF8.GetBytes(CLRF); -// var signedChunk = new byte[header.Length + dataToChunk.Length + trailer.Length]; - -// Array.Copy(header, 0, signedChunk, 0, header.Length); -// Array.Copy(dataToChunk, 0, signedChunk, header.Length, dataToChunk.Length); -// Array.Copy(trailer, 0, signedChunk, header.Length + dataToChunk.Length, trailer.Length); - -// // this is the total data for the chunk that will be sent to the request stream -// return signedChunk; -// } -// catch (Exception e) -// { -// throw new Exception("Unable to sign the chunked data. " + e.Message, e); -// } -// } -// } -//} +// +// Copyright (c) .NET Foundation and Contributors +// See LICENSE file in the project root for full license information. +// + +using System; +using System.Collections; +using System.Diagnostics; +using System.Security.Cryptography; +using System.Text; + + +namespace nanoFramework.Aws.SignatureVersion4 +{ + /// + /// AWS Signature Version 4 signer for signing 'chunked' uploads + /// using an Authorization header. + /// + public class SignerForChunkedUpload : SignerBase + { + // SHA256 substitute marker used in place of x-amz-content-sha256 when employing + // chunked uploads + internal const string STREAMING_BODY_SHA256 = "STREAMING-AWS4-HMAC-SHA256-PAYLOAD"; + + static readonly string CLRF = "\r\n"; + static readonly string CHUNK_STRING_TO_SIGN_PREFIX = "AWS4-HMAC-SHA256-PAYLOAD"; + static readonly string CHUNK_SIGNATURE_HEADER = ";chunk-signature="; + static readonly int SIGNATURE_LENGTH = 64; + static byte[] FINAL_CHUNK = new byte[0]; + + /// + /// Tracks the previously computed signature value; for chunk 0 this will + /// contain the signature included in the Authorization header. For subsequent + /// chunks it contains the computed signature of the prior chunk. + /// + public string LastComputedSignature { get; private set; } + + /// + /// Date and time of the original signing computation, in ISO 8601 basic format, + /// reused for each chunk + /// + public string DateTimeStamp { get; private set; } + + /// + /// The scope value of the original signing computation, reused for each chunk + /// + public string Scope { get; private set; } + + /// + /// The derived signing key used in the original signature computation and + /// re-used for each chunk + /// + byte[] SigningKey { get; set; } + + /// + /// Computes an AWS4 signature for a request, ready for inclusion as an + /// 'Authorization' header. + /// + /// + /// The request headers; 'Host' and 'X-Amz-Date' will be added to this set. + /// + /// + /// Any query parameters that will be added to the endpoint. The parameters + /// should be specified in canonical format. + /// + /// + /// Precomputed SHA256 hash of the request body content; this value should also + /// be set as the header 'X-Amz-Content-SHA256' for non-streaming uploads. + /// + /// + /// The user's AWS Access Key. + /// + /// + /// The user's AWS Secret Key. + /// + /// + /// The computed authorization string for the request. This value needs to be set as the + /// header 'Authorization' on the subsequent HTTP request. + /// + public string ComputeSignature(IDictionary headers, + string queryParameters, + string bodyHash, + string awsAccessKey, + string awsSecretKey) + { + // first get the date and time for the subsequent request, and convert to ISO8601 format (without '-' and ':') + // for use in signature generation + var requestDateTime = DateTime.UtcNow; + DateTimeStamp = requestDateTime.ToString(ISO8601BasicFormat); + + // update the headers with required 'x-amz-date' and 'host' values + headers.Add(X_Amz_Date, DateTimeStamp); + + var hostHeader = EndpointUri.Host; + hostHeader += ":" + EndpointUri.Port; // FIXME: should use //if (!EndpointUri.IsDefaultPort) + 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 = new Hashtable(); + + var qparam = queryParameters.Split('&'); + foreach (string p in qparam) + { + var items = p.Split('='); + if (items.Length == 1) + { + paramDictionary.Add(items[0], null); + } + else + { + paramDictionary.Add(items[0], items[1]); + } + } + + var sb = new StringBuilder(); + var paramKeys = new ArrayList(); + + foreach (DictionaryEntry kvp in paramDictionary) + { + paramKeys.Add(kvp.Key); + } + + paramKeys.Sort(StringComparer.Ordinal); + foreach (var p in paramKeys) + { + if (sb.Length > 0) + sb.Append("&"); + sb.Append($"{p}={paramDictionary[p]}"); + } + + canonicalizedQueryParameters = sb.ToString(); + } + + // canonicalize the various components of the request + var canonicalRequest = CanonicalizeRequest(EndpointUri, + HttpMethod, + canonicalizedQueryParameters, + canonicalizedHeaderNames, + canonicalizedHeaders, + bodyHash); + Debug.WriteLine($"\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); + Scope = $"{dateStamp}/{Region}/{Service}/{TERMINATOR}"; + + stringToSign.Append($"{SCHEME}-{ALGORITHM}\n{DateTimeStamp}\n{Scope}\n"); + stringToSign.Append(ToHexString(canonicalRequestHashBytes, true)); + + Debug.WriteLine($"\nStringToSign:\n{stringToSign}"); + + // compute the signing key + SigningKey = DeriveSigningKey(awsSecretKey, Region, dateStamp, Service); + + var kha = new HMACSHA256(SigningKey); + + // compute the AWS4 signature and return it + var signature = kha.ComputeHash(Encoding.UTF8.GetBytes(stringToSign.ToString())); + var signatureString = ToHexString(signature, true); + Debug.WriteLine($"\nSignature:\n{signatureString}"); + + // cache the computed signature ready for chunk 0 upload + LastComputedSignature = signatureString; + + var authString = new StringBuilder(); + authString.Append($"{SCHEME}-{ALGORITHM} "); + authString.Append($"Credential={awsAccessKey}/{Scope}, "); + authString.Append($"SignedHeaders={canonicalizedHeaderNames}, "); + authString.Append($"Signature={signatureString}"); + + var authorization = authString.ToString(); + Debug.WriteLine($"\nAuthorization:\n{authorization}"); + + return authorization; + } + + /// + /// Calculates the expanded payload size of our data when it is chunked + /// + /// + /// The true size of the data payload to be uploaded + /// + /// + /// The size of each chunk we intend to send; each chunk will be + /// prefixed with signed header data, expanding the overall size + /// by a determinable amount + /// + /// + /// The overall payload size to use as content-length on a chunked upload + /// + public long CalculateChunkedContentLength(long originalLength, long chunkSize) + { + if (originalLength <= 0) + throw new ArgumentOutOfRangeException("originalLength"); + if (chunkSize <= 0) + throw new ArgumentOutOfRangeException("chunkSize"); + + var maxSizeChunks = originalLength / chunkSize; + var remainingBytes = originalLength % chunkSize; + + var chunkedContentLength = maxSizeChunks * CalculateChunkHeaderLength(chunkSize) + + (remainingBytes > 0 ? CalculateChunkHeaderLength(remainingBytes) : 0) + + CalculateChunkHeaderLength(0); + + Debug.WriteLine($"\nComputed chunked content length for original length {originalLength} bytes, chunk size {chunkSize / 1024}KB is {chunkedContentLength} bytes"); + return chunkedContentLength; + } + + /// + /// Returns the size of a chunk header, which only varies depending + /// on the selected chunk size + /// + /// + /// The intended size of each chunk; this is placed into the chunk + /// header + /// + /// + /// The overall size of the header that will prefix the user data in + /// each chunk + /// + static long CalculateChunkHeaderLength(long chunkSize) + { + return chunkSize.ToString("X").Length + + CHUNK_SIGNATURE_HEADER.Length + + SIGNATURE_LENGTH + + CLRF.Length + + chunkSize + + CLRF.Length; + } + + /// + /// Returns a chunk for upload consisting of the signed 'header' or chunk + /// prefix plus the user data. The signature of the chunk incorporates the + /// signature of the previous chunk (or, if the first chunk, the signature + /// of the headers portion of the request). + /// + /// + /// The length of the user data contained in userData + /// + /// + /// Contains the user data to be sent in the upload chunk + /// + /// + /// A new buffer of data for upload containing the chunk header plus user data + /// + public byte[] ConstructSignedChunk(long userDataLen, byte[] userData) + { + // to keep our computation routine signatures simple, if the userData + // buffer contains less data than it could, shrink it. Note the special case + // to handle the requirement that we send an empty chunk to complete + // our chunked upload. + byte[] dataToChunk; + if (userDataLen == 0) + dataToChunk = FINAL_CHUNK; + else + { + if (userDataLen < userData.Length) + { + // shrink the chunkdata to fit + dataToChunk = new byte[userDataLen]; + Array.Copy(userData, 0, dataToChunk, 0, (int)userDataLen); + } + else + dataToChunk = userData; + } + + var chunkHeader = new StringBuilder(); + + // start with size of user data + chunkHeader.Append(dataToChunk.Length.ToString("X")); + + // nonsig-extension; we have none in these samples + const string nonsigExtension = ""; + + // if this is the first chunk, we package it with the signing result + // of the request headers, otherwise we use the cached signature + // of the previous chunk + + // sig-extension + var chunkStringToSign = + CHUNK_STRING_TO_SIGN_PREFIX + "\n" + + DateTimeStamp + "\n" + + Scope + "\n" + + LastComputedSignature + "\n" + + ToHexString(CanonicalRequestHashAlgorithm.ComputeHash(Encoding.UTF8.GetBytes(nonsigExtension)), true) + "\n" + + ToHexString(CanonicalRequestHashAlgorithm.ComputeHash(dataToChunk), true); + + Debug.WriteLine($"\nChunkStringToSign:\n{chunkStringToSign}"); + + // compute the V4 signature for the chunk + var chunkSignature + = ToHexString(ComputeKeyedHash(SigningKey, + Encoding.UTF8.GetBytes(chunkStringToSign)), + true); + + Debug.WriteLine($"\nChunkSignature:\n{chunkSignature}"); + + // cache the signature to include with the next chunk's signature computation + this.LastComputedSignature = chunkSignature; + + // construct the actual chunk, comprised of the non-signed extensions, the + // 'headers' we just signed and their signature, plus a newline then copy + // that plus the user's data to a payload to be written to the request stream + chunkHeader.Append(nonsigExtension + CHUNK_SIGNATURE_HEADER + chunkSignature); + chunkHeader.Append(CLRF); + + Debug.WriteLine($"\nChunkHeader:\n{chunkHeader}"); + + try + { + var header = Encoding.UTF8.GetBytes(chunkHeader.ToString()); + var trailer = Encoding.UTF8.GetBytes(CLRF); + var signedChunk = new byte[header.Length + dataToChunk.Length + trailer.Length]; + + Array.Copy(header, 0, signedChunk, 0, header.Length); + Array.Copy(dataToChunk, 0, signedChunk, header.Length, dataToChunk.Length); + Array.Copy(trailer, 0, signedChunk, header.Length + dataToChunk.Length, trailer.Length); + + // this is the total data for the chunk that will be sent to the request stream + return signedChunk; + } + catch (Exception e) + { + throw new Exception("Unable to sign the chunked data. " + e.Message, e); + } + } + } +} diff --git a/nanoFramework.Aws.IoTCore.Devices/AwsSignatureVersion4/SignerForPOST.cs b/nanoFramework.Aws.IoTCore.Devices/AwsSignatureVersion4/SignerForPOST.cs index 83b99a4..aac004a 100644 --- a/nanoFramework.Aws.IoTCore.Devices/AwsSignatureVersion4/SignerForPOST.cs +++ b/nanoFramework.Aws.IoTCore.Devices/AwsSignatureVersion4/SignerForPOST.cs @@ -1,167 +1,167 @@ -//// -//// Copyright (c) .NET Foundation and Contributors -//// See LICENSE file in the project root for full license information. -//// - -//using System; -//using System.Collections; -//using System.Diagnostics; -//using System.Security.Cryptography; -//using System.Text; - -//namespace nanoFramework.Aws.SignatureVersion4 -//{ -// /// -// /// AWS Signature Version 4 signer for signing POST requests -// /// using a policy. -// /// -// public class SignerForPOST : SignerBase -// { -// internal string FormatCredentialStringForPolicy(DateTime dateTimeStamp) -// { -// return "AKIAIOSFODNN7EXAMPLE/20130806/cn-north-1/s3/aws4_request"; -// } - -// internal string FormatAlgorithmForPolicy -// { -// get { return "AWS4-HMAC-SHA256"; } -// } - -// internal string FormatDateTimeForPolicy(DateTime dateTimeStamp) -// { -// return dateTimeStamp.ToString(ISO8601BasicFormat); -// } - -// /// -// /// Computes an AWS Signature Version 4 signature for a request, ready for inclusion as an -// /// 'Authorization' header. -// /// -// /// -// /// The request headers; 'Host' and 'X-Amz-Date' will be added to this set. -// /// -// /// -// /// Any query parameters that will be added to the endpoint. The parameters -// /// should be specified in canonical format. -// /// -// /// -// /// Precomputed SHA256 hash of the request body content; this value should also -// /// be set as the header 'X-Amz-Content-SHA256' for non-streaming uploads. -// /// -// /// -// /// The user's AWS Access Key. -// /// -// /// -// /// The user's AWS Secret Key. -// /// -// /// -// /// The computed authorization string for the request. This value needs to be set as the -// /// header 'Authorization' on the subsequent HTTP request. -// /// -// public string ComputeSignature(IDictionary headers, -// string queryParameters, -// string bodyHash, -// string awsAccessKey, -// string awsSecretKey) -// { -// // first get the date and time for the subsequent request, and convert to ISO8601 format (without '-' and ':') -// // for use in signature generation -// var requestDateTime = DateTime.UtcNow; -// var dateTimeStamp = requestDateTime.ToString(ISO8601BasicFormat); - -// // update the headers with required 'x-amz-date' and 'host' values -// headers.Add(X_Amz_Date, dateTimeStamp); - -// var hostHeader = EndpointUri.Host; -// hostHeader += ":" + EndpointUri.Port; // FIXME: should use //if (!EndpointUri.IsDefaultPort) -// 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 = new Hashtable(); - -// var qparam = queryParameters.Split('&'); -// foreach (string p in qparam) -// { -// var items = p.Split('='); -// if (items.Length == 1) -// { -// paramDictionary.Add(items[0], null); -// } -// else -// { -// paramDictionary.Add(items[0], items[1]); -// } -// } - -// var sb = new StringBuilder(); -// var paramKeys = new ArrayList(); - -// foreach (DictionaryEntry kvp in paramDictionary) -// { -// paramKeys.Add(kvp.Key); -// } - -// paramKeys.Sort(StringComparer.Ordinal); -// foreach (var p in paramKeys) -// { -// if (sb.Length > 0) -// sb.Append("&"); -// sb.Append($"{p}={paramDictionary[p]}"); -// } - -// canonicalizedQueryParameters = sb.ToString(); -// } - -// // canonicalize the various components of the request -// var canonicalRequest = CanonicalizeRequest(EndpointUri, -// HttpMethod, -// canonicalizedQueryParameters, -// canonicalizedHeaderNames, -// canonicalizedHeaders, -// bodyHash); -// Debug.WriteLine($"\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); -// var scope = $"{dateStamp}/{Region}/{Service}/{TERMINATOR}"; - -// stringToSign.Append($"{SCHEME}-{ALGORITHM}\n{dateTimeStamp}\n{scope}\n"); -// stringToSign.Append(ToHexString(canonicalRequestHashBytes, true)); - -// Debug.WriteLine($"\nStringToSign:\n{stringToSign}"); - -// // compute the signing key -// var kha = new HMACSHA256(DeriveSigningKey(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); -// Debug.WriteLine($"\nSignature:\n{signatureString}"); - -// var authString = new StringBuilder(); -// authString.Append($"{SCHEME}-{ALGORITHM} "); -// authString.Append($"Credential={awsAccessKey}/{scope}, "); -// authString.Append($"SignedHeaders={canonicalizedHeaderNames}, "); -// authString.Append($"Signature={signatureString}"); - -// var authorization = authString.ToString(); -// Debug.WriteLine($"\nAuthorization:\n{authorization}"); - -// return authorization; -// } -// } -//} +// +// Copyright (c) .NET Foundation and Contributors +// See LICENSE file in the project root for full license information. +// + +using System; +using System.Collections; +using System.Diagnostics; +using System.Security.Cryptography; +using System.Text; + +namespace nanoFramework.Aws.SignatureVersion4 +{ + /// + /// AWS Signature Version 4 signer for signing POST requests + /// using a policy. + /// + public class SignerForPOST : SignerBase + { + internal string FormatCredentialStringForPolicy(DateTime dateTimeStamp) + { + return "AKIAIOSFODNN7EXAMPLE/20130806/cn-north-1/s3/aws4_request"; + } + + internal string FormatAlgorithmForPolicy + { + get { return "AWS4-HMAC-SHA256"; } + } + + internal string FormatDateTimeForPolicy(DateTime dateTimeStamp) + { + return dateTimeStamp.ToString(ISO8601BasicFormat); + } + + /// + /// Computes an AWS Signature Version 4 signature for a request, ready for inclusion as an + /// 'Authorization' header. + /// + /// + /// The request headers; 'Host' and 'X-Amz-Date' will be added to this set. + /// + /// + /// Any query parameters that will be added to the endpoint. The parameters + /// should be specified in canonical format. + /// + /// + /// Precomputed SHA256 hash of the request body content; this value should also + /// be set as the header 'X-Amz-Content-SHA256' for non-streaming uploads. + /// + /// + /// The user's AWS Access Key. + /// + /// + /// The user's AWS Secret Key. + /// + /// + /// The computed authorization string for the request. This value needs to be set as the + /// header 'Authorization' on the subsequent HTTP request. + /// + public string ComputeSignature(IDictionary headers, + string queryParameters, + string bodyHash, + string awsAccessKey, + string awsSecretKey) + { + // first get the date and time for the subsequent request, and convert to ISO8601 format (without '-' and ':') + // for use in signature generation + var requestDateTime = DateTime.UtcNow; + var dateTimeStamp = requestDateTime.ToString(ISO8601BasicFormat); + + // update the headers with required 'x-amz-date' and 'host' values + headers.Add(X_Amz_Date, dateTimeStamp); + + var hostHeader = EndpointUri.Host; + hostHeader += ":" + EndpointUri.Port; // FIXME: should use //if (!EndpointUri.IsDefaultPort) + 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 = new Hashtable(); + + var qparam = queryParameters.Split('&'); + foreach (string p in qparam) + { + var items = p.Split('='); + if (items.Length == 1) + { + paramDictionary.Add(items[0], null); + } + else + { + paramDictionary.Add(items[0], items[1]); + } + } + + var sb = new StringBuilder(); + var paramKeys = new ArrayList(); + + foreach (DictionaryEntry kvp in paramDictionary) + { + paramKeys.Add(kvp.Key); + } + + paramKeys.Sort(StringComparer.Ordinal); + foreach (var p in paramKeys) + { + if (sb.Length > 0) + sb.Append("&"); + sb.Append($"{p}={paramDictionary[p]}"); + } + + canonicalizedQueryParameters = sb.ToString(); + } + + // canonicalize the various components of the request + var canonicalRequest = CanonicalizeRequest(EndpointUri, + HttpMethod, + canonicalizedQueryParameters, + canonicalizedHeaderNames, + canonicalizedHeaders, + bodyHash); + Debug.WriteLine($"\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); + var scope = $"{dateStamp}/{Region}/{Service}/{TERMINATOR}"; + + stringToSign.Append($"{SCHEME}-{ALGORITHM}\n{dateTimeStamp}\n{scope}\n"); + stringToSign.Append(ToHexString(canonicalRequestHashBytes, true)); + + Debug.WriteLine($"\nStringToSign:\n{stringToSign}"); + + // compute the signing key + var kha = new HMACSHA256(DeriveSigningKey(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); + Debug.WriteLine($"\nSignature:\n{signatureString}"); + + var authString = new StringBuilder(); + authString.Append($"{SCHEME}-{ALGORITHM} "); + authString.Append($"Credential={awsAccessKey}/{scope}, "); + authString.Append($"SignedHeaders={canonicalizedHeaderNames}, "); + authString.Append($"Signature={signatureString}"); + + var authorization = authString.ToString(); + Debug.WriteLine($"\nAuthorization:\n{authorization}"); + + return authorization; + } + } +} From 80a6939a15a36c9ea9a89e8e688323583270873c Mon Sep 17 00:00:00 2001 From: Robin Jones Date: Mon, 14 Aug 2023 02:05:59 +0100 Subject: [PATCH 05/14] Update nanoFramework.Aws.IoTCore.Devices.nuspec --- nanoFramework.Aws.IoTCore.Devices.nuspec | 2 ++ 1 file changed, 2 insertions(+) diff --git a/nanoFramework.Aws.IoTCore.Devices.nuspec b/nanoFramework.Aws.IoTCore.Devices.nuspec index e0efab3..422eab1 100644 --- a/nanoFramework.Aws.IoTCore.Devices.nuspec +++ b/nanoFramework.Aws.IoTCore.Devices.nuspec @@ -22,6 +22,8 @@ This is an SDK for Aws IoTCore. + + From ac4a368657bb15c2d7273701b62efd62829acec2 Mon Sep 17 00:00:00 2001 From: Robin Jones Date: Tue, 15 Aug 2023 16:52:18 +0100 Subject: [PATCH 06/14] Split classes into separate files --- .../nfArrayListExtensions.cs | 97 ------------------- .../AwsSignatureVersion4/nfOrdinalComparer.cs | 34 +++++++ .../AwsSignatureVersion4/nfStringComparer.cs | 80 +++++++++++++++ .../WebsocketConnectionClient.cs | 8 +- .../nanoFramework.Aws.IoTCore.Devices.nfproj | 2 + 5 files changed, 122 insertions(+), 99 deletions(-) create mode 100644 nanoFramework.Aws.IoTCore.Devices/AwsSignatureVersion4/nfOrdinalComparer.cs create mode 100644 nanoFramework.Aws.IoTCore.Devices/AwsSignatureVersion4/nfStringComparer.cs diff --git a/nanoFramework.Aws.IoTCore.Devices/AwsSignatureVersion4/nfArrayListExtensions.cs b/nanoFramework.Aws.IoTCore.Devices/AwsSignatureVersion4/nfArrayListExtensions.cs index a64046e..c370350 100644 --- a/nanoFramework.Aws.IoTCore.Devices/AwsSignatureVersion4/nfArrayListExtensions.cs +++ b/nanoFramework.Aws.IoTCore.Devices/AwsSignatureVersion4/nfArrayListExtensions.cs @@ -37,101 +37,4 @@ public static void Sort(this ArrayList items, IComparer comparer) } } - - /// - /// String Comparers - /// - public abstract class StringComparer : IComparer - { - private static readonly StringComparer _ordinal = new OrdinalComparer(false); - private static readonly StringComparer _ordinalIgnoreCase = new OrdinalComparer(true); - - /// - /// Ordinal Comparer - /// - public static StringComparer Ordinal - { - get - { - return _ordinal; - } - } - - /// - /// Ordinal Comparer (ignoring case) - /// - public static StringComparer OrdinalIgnoreCase - { - get - { - return _ordinalIgnoreCase; - } - } - - /// - /// Compares two objects - /// - /// Object x - /// Object y - /// The result - /// - public int Compare(object x, object y) - { - if (x == y) return 0; - if (x == null) return -1; - if (y == null) return 1; - - string sa = x as string; - if (sa != null) - { - string sb = y as string; - if (sb != null) - { - return Compare(sa, sb); - } - } - - IComparable ia = x as IComparable; - if (ia != null) - { - return ia.CompareTo(y); - } - - throw new ArgumentException("Argument_ImplementIComparable"); - } - - /// - /// Compares two strings - /// - /// String x - /// String y - /// The result. - public abstract int Compare(string x, string y); - } - - internal sealed class OrdinalComparer : StringComparer - { - private bool _ignoreCase; - - internal OrdinalComparer(bool ignoreCase) - { - _ignoreCase = ignoreCase; - } - - public override int Compare(string x, string y) - { - if (ReferenceEquals(x, y)) return 0; - if (x == null) return -1; - if (y == null) return 1; - - if (_ignoreCase) - { - return string.Compare(x.ToLower(), y.ToLower()); - } - - return string.Compare(x, y); - } - - } - } diff --git a/nanoFramework.Aws.IoTCore.Devices/AwsSignatureVersion4/nfOrdinalComparer.cs b/nanoFramework.Aws.IoTCore.Devices/AwsSignatureVersion4/nfOrdinalComparer.cs new file mode 100644 index 0000000..b58aaa8 --- /dev/null +++ b/nanoFramework.Aws.IoTCore.Devices/AwsSignatureVersion4/nfOrdinalComparer.cs @@ -0,0 +1,34 @@ +// +// Copyright (c) .NET Foundation and Contributors +// See LICENSE file in the project root for full license information. +// + +namespace System.Collections +{ + + internal sealed class OrdinalComparer : StringComparer + { + private bool _ignoreCase; + + internal OrdinalComparer(bool ignoreCase) + { + _ignoreCase = ignoreCase; + } + + public override int Compare(string x, string y) + { + if (ReferenceEquals(x, y)) return 0; + if (x == null) return -1; + if (y == null) return 1; + + if (_ignoreCase) + { + return string.Compare(x.ToLower(), y.ToLower()); + } + + return string.Compare(x, y); + } + + } + +} diff --git a/nanoFramework.Aws.IoTCore.Devices/AwsSignatureVersion4/nfStringComparer.cs b/nanoFramework.Aws.IoTCore.Devices/AwsSignatureVersion4/nfStringComparer.cs new file mode 100644 index 0000000..9b1a6f5 --- /dev/null +++ b/nanoFramework.Aws.IoTCore.Devices/AwsSignatureVersion4/nfStringComparer.cs @@ -0,0 +1,80 @@ +// +// Copyright (c) .NET Foundation and Contributors +// See LICENSE file in the project root for full license information. +// + +namespace System.Collections +{ + + /// + /// String Comparers + /// + public abstract class StringComparer : IComparer + { + private static readonly StringComparer _ordinal = new OrdinalComparer(false); + private static readonly StringComparer _ordinalIgnoreCase = new OrdinalComparer(true); + + /// + /// Ordinal Comparer + /// + public static StringComparer Ordinal + { + get + { + return _ordinal; + } + } + + /// + /// Ordinal Comparer (ignoring case) + /// + public static StringComparer OrdinalIgnoreCase + { + get + { + return _ordinalIgnoreCase; + } + } + + /// + /// Compares two objects + /// + /// Object x + /// Object y + /// The result + /// + public int Compare(object x, object y) + { + if (x == y) return 0; + if (x == null) return -1; + if (y == null) return 1; + + string sa = x as string; + if (sa != null) + { + string sb = y as string; + if (sb != null) + { + return Compare(sa, sb); + } + } + + IComparable ia = x as IComparable; + if (ia != null) + { + return ia.CompareTo(y); + } + + throw new ArgumentException("Argument_ImplementIComparable"); + } + + /// + /// Compares two strings + /// + /// String x + /// String y + /// The result. + public abstract int Compare(string x, string y); + } + +} diff --git a/nanoFramework.Aws.IoTCore.Devices/WebsocketConnectionClient.cs b/nanoFramework.Aws.IoTCore.Devices/WebsocketConnectionClient.cs index 515b05f..82e743b 100644 --- a/nanoFramework.Aws.IoTCore.Devices/WebsocketConnectionClient.cs +++ b/nanoFramework.Aws.IoTCore.Devices/WebsocketConnectionClient.cs @@ -34,7 +34,7 @@ public class WebsocketConnectionClient : IDisposable const int _wssPort = 443; //Default WSS port. - //WebSocket _webSocket = null; + //private WebSocket _webSocket = null; /// /// Creates a new MQTT over WebSocket Connection Client @@ -66,7 +66,11 @@ public WebsocketConnectionClient() /// public void Dispose() { - + //if (_webSocket != null) + //{ + // GC.SuppressFinalize(_webSocket); + // _webSocket = null; + //} } } } diff --git a/nanoFramework.Aws.IoTCore.Devices/nanoFramework.Aws.IoTCore.Devices.nfproj b/nanoFramework.Aws.IoTCore.Devices/nanoFramework.Aws.IoTCore.Devices.nfproj index f20f442..8aa5635 100644 --- a/nanoFramework.Aws.IoTCore.Devices/nanoFramework.Aws.IoTCore.Devices.nfproj +++ b/nanoFramework.Aws.IoTCore.Devices/nanoFramework.Aws.IoTCore.Devices.nfproj @@ -33,6 +33,8 @@ + + From f3a1c7d139d8932d97d1cc164de7ad0a3eae7301 Mon Sep 17 00:00:00 2001 From: Robin Jones Date: Wed, 23 Aug 2023 14:27:27 +0100 Subject: [PATCH 07/14] Fix some sonar warnings. --- ...{nfArrayListExtensions.cs => NanoArrayListExtensions.cs} | 2 +- .../{nfOrdinalComparer.cs => NanoOrdinalComparer.cs} | 0 .../{nfStringComparer.cs => NanoStringComparer.cs} | 0 .../AwsSignatureVersion4/SignerBase.cs | 2 +- .../nanoFramework.Aws.IoTCore.Devices.nfproj | 6 +++--- 5 files changed, 5 insertions(+), 5 deletions(-) rename nanoFramework.Aws.IoTCore.Devices/AwsSignatureVersion4/{nfArrayListExtensions.cs => NanoArrayListExtensions.cs} (95%) rename nanoFramework.Aws.IoTCore.Devices/AwsSignatureVersion4/{nfOrdinalComparer.cs => NanoOrdinalComparer.cs} (100%) rename nanoFramework.Aws.IoTCore.Devices/AwsSignatureVersion4/{nfStringComparer.cs => NanoStringComparer.cs} (100%) diff --git a/nanoFramework.Aws.IoTCore.Devices/AwsSignatureVersion4/nfArrayListExtensions.cs b/nanoFramework.Aws.IoTCore.Devices/AwsSignatureVersion4/NanoArrayListExtensions.cs similarity index 95% rename from nanoFramework.Aws.IoTCore.Devices/AwsSignatureVersion4/nfArrayListExtensions.cs rename to nanoFramework.Aws.IoTCore.Devices/AwsSignatureVersion4/NanoArrayListExtensions.cs index c370350..5c85127 100644 --- a/nanoFramework.Aws.IoTCore.Devices/AwsSignatureVersion4/nfArrayListExtensions.cs +++ b/nanoFramework.Aws.IoTCore.Devices/AwsSignatureVersion4/NanoArrayListExtensions.cs @@ -8,7 +8,7 @@ namespace System.Collections /// /// Extension methods used for canonicalization. /// - public static class nfArrayListExtensions + public static class NanoArrayListExtensions { /// /// Sort an ArrayList diff --git a/nanoFramework.Aws.IoTCore.Devices/AwsSignatureVersion4/nfOrdinalComparer.cs b/nanoFramework.Aws.IoTCore.Devices/AwsSignatureVersion4/NanoOrdinalComparer.cs similarity index 100% rename from nanoFramework.Aws.IoTCore.Devices/AwsSignatureVersion4/nfOrdinalComparer.cs rename to nanoFramework.Aws.IoTCore.Devices/AwsSignatureVersion4/NanoOrdinalComparer.cs diff --git a/nanoFramework.Aws.IoTCore.Devices/AwsSignatureVersion4/nfStringComparer.cs b/nanoFramework.Aws.IoTCore.Devices/AwsSignatureVersion4/NanoStringComparer.cs similarity index 100% rename from nanoFramework.Aws.IoTCore.Devices/AwsSignatureVersion4/nfStringComparer.cs rename to nanoFramework.Aws.IoTCore.Devices/AwsSignatureVersion4/NanoStringComparer.cs diff --git a/nanoFramework.Aws.IoTCore.Devices/AwsSignatureVersion4/SignerBase.cs b/nanoFramework.Aws.IoTCore.Devices/AwsSignatureVersion4/SignerBase.cs index 2af4f52..5d0ed3d 100644 --- a/nanoFramework.Aws.IoTCore.Devices/AwsSignatureVersion4/SignerBase.cs +++ b/nanoFramework.Aws.IoTCore.Devices/AwsSignatureVersion4/SignerBase.cs @@ -44,7 +44,7 @@ public abstract class SignerBase // algorithm used to hash the canonical request that is supplied to // the signature computation - internal static SHA256 CanonicalRequestHashAlgorithm = SHA256.Create(); + internal static readonly SHA256 CanonicalRequestHashAlgorithm = SHA256.Create(); /// /// The service endpoint, including the path to any resource. diff --git a/nanoFramework.Aws.IoTCore.Devices/nanoFramework.Aws.IoTCore.Devices.nfproj b/nanoFramework.Aws.IoTCore.Devices/nanoFramework.Aws.IoTCore.Devices.nfproj index 8aa5635..fb770db 100644 --- a/nanoFramework.Aws.IoTCore.Devices/nanoFramework.Aws.IoTCore.Devices.nfproj +++ b/nanoFramework.Aws.IoTCore.Devices/nanoFramework.Aws.IoTCore.Devices.nfproj @@ -31,10 +31,10 @@ - + - - + + From c284ebd37cbcea6e1f2888745610526bc9e0d8c3 Mon Sep 17 00:00:00 2001 From: Robin Jones Date: Wed, 23 Aug 2023 18:49:21 +0100 Subject: [PATCH 08/14] Work on tests. --- Tests/Aws.IoTCore.Devices.UnitTests.nfproj | 12 +++ Tests/SignatureVersion4Tests.cs | 83 ++++++++++++------- Tests/packages.config | 4 + Tests/packages.lock.json | 24 ++++++ .../SignerForAuthorizationHeader.cs | 8 +- .../SignerForChunkedUpload.cs | 16 ++-- .../AwsSignatureVersion4/SignerForPOST.cs | 8 +- .../SignerForQueryParameterAuth.cs | 8 +- 8 files changed, 113 insertions(+), 50 deletions(-) diff --git a/Tests/Aws.IoTCore.Devices.UnitTests.nfproj b/Tests/Aws.IoTCore.Devices.UnitTests.nfproj index 1fbe38b..bd2f0d0 100644 --- a/Tests/Aws.IoTCore.Devices.UnitTests.nfproj +++ b/Tests/Aws.IoTCore.Devices.UnitTests.nfproj @@ -50,6 +50,9 @@ ..\packages\nanoFramework.Json.2.2.117\lib\nanoFramework.Json.dll True + + ..\packages\nanoFramework.Runtime.Events.1.11.6\lib\nanoFramework.Runtime.Events.dll + ..\packages\nanoFramework.System.Collections.1.5.31\lib\nanoFramework.System.Collections.dll True @@ -70,6 +73,15 @@ ..\packages\nanoFramework.System.IO.Streams.1.1.59\lib\System.IO.Streams.dll True + + ..\packages\nanoFramework.System.Net.1.10.52\lib\System.Net.dll + + + ..\packages\nanoFramework.System.Net.Http.1.5.99\lib\System.Net.Http.dll + + + ..\packages\nanoFramework.System.Threading.1.1.19\lib\System.Threading.dll + diff --git a/Tests/SignatureVersion4Tests.cs b/Tests/SignatureVersion4Tests.cs index 4bba476..53fff8a 100644 --- a/Tests/SignatureVersion4Tests.cs +++ b/Tests/SignatureVersion4Tests.cs @@ -6,7 +6,6 @@ using nanoFramework.TestFramework; using System; using System.Collections; -using nanoFramework.Aws.SignatureVersion4; namespace nanoFramework.Aws.SignatureVersion4.Tests { @@ -16,47 +15,71 @@ namespace nanoFramework.Aws.SignatureVersion4.Tests [TestClass] public class SignatureVersion4Tests { + [TestMethod] - public void check_CanonicalizeHeaderNames_when_order_is_canonical_already() // using an already ordered list + public void check_SignerForQueryParameters_ComputeSignature_Get_iotGateway_generated_correctly() { - //// Given that: SigV4 seems to require a header that is canonicalized - //var headers = new Hashtable(); - //headers.Add("aTest", "atestval"); - //headers.Add("Atest2", "Atest2val"); - //headers.Add("btest", "btestval"); - //headers.Add("ctest", "ctestval"); + // See: https://docs.aws.amazon.com/AmazonS3/latest/API/sigv4-query-string-auth.html + // https://github.com/aws/aws-sdk-java-v2/blob/master/core/auth/src/test/java/software/amazon/awssdk/auth/signer/Aws4SignerTest.java + // https://github.com/aws/aws-sdk-net/blob/master/sdk/test/Services/Signer/UnitTests/Generated/Endpoints/SignerEndpointProviderTests.cs + + var v4signer = new SignerForQueryParameterAuth + { + EndpointUri = new System.Uri("https://test.us-east-1.amazonaws.com"), + HttpMethod = "GET", + Service = "iotdevicegateway", + Region = "us-east-1" + }; - //Console.WriteLine(SignerBase.CanonicalizeHeaderNames(headers)); + Console.WriteLine(v4signer.ComputeSignature(new Hashtable(), "", "", "fakeAccessKey", "fakeSecret")); - // Expect "atest;atest2;btest;ctest" + //http://demo.us-east-1.amazonaws.com + //?X-Amz-Algorithm=AWS4-HMAC-SHA256 + //&X-Amz-Credential=/20130721/us-east-1/iotdevicegateway/aws4_request + //&X-Amz-Date=20130721T201207Z + //&X-Amz-SignedHeaders=host + //&X-Amz-Signature= + + // replace '' with "fakeAccessKey" + // replace '' + // replace '/' with '%2F' in url + // ignore or inject datetime? } [TestMethod] - public void check_CanonicalizeHeaderNames_when_order_is_not_sorted() + public void check_SignerForQueryParameters_ComputeSignature_Post_S3_generated_correctly() { - //var headers = new Hashtable(); - //headers.Add("aTest", "atestval"); - //headers.Add("ctest", "ctestval"); - //headers.Add("btest", "btestval"); - //headers.Add("Atest2", "Atest2val"); + // See: https://docs.aws.amazon.com/AmazonS3/latest/API/sigv4-query-string-auth.html - //Console.WriteLine(CanonicalizeHeaderNames(headers)); - // Expect "atest;atest2;btest;ctest" - } + var v4signer = new SignerForQueryParameterAuth + { + EndpointUri = new System.Uri("https://test.us-east-1.amazonaws.com"), + HttpMethod = "POST", + Service = "s3", + Region = "us-east-1" + }; + // FIXME: would fail! + var sigparams = new Hashtable + { + { "X-Amz-Expires", "86400" } + }; - [TestMethod] - public void check_CanonicalizeHeaders_when_order_is_not_sorted() - { - //var headers = new Hashtable(); - //headers.Add("aTest", "atestval"); - //headers.Add("ctest", "ctestval"); - //headers.Add("btest", "btestval"); - //headers.Add("Atest2", "Atest2val"); - //Console.WriteLine(CanonicalizeHeaders(headers)); - - // Expect "atest:atestval\natest2:Atest2val\nbtest:btestval\nctest:ctestval\n" + Console.WriteLine(v4signer.ComputeSignature(sigparams, "", "", "fakeAccessKey", "fakeSecret")); + + //http://demo.us-east-1.amazonaws.com + //?X-Amz-Algorithm=AWS4-HMAC-SHA256 + //&X-Amz-Credential=/20130721/us-east-1/s3/aws4_request + //&X-Amz-Date=20130721T201207Z + //&X-Amz-Expires=86400 + //&X-Amz-SignedHeaders=host + //&X-Amz-Signature= + + // replace '' with "fakeAccessKey" + // replace '' + // replace '/' with '%2F' in url + // ignore or inject datetime? } } diff --git a/Tests/packages.config b/Tests/packages.config index b90b830..ae0d5a6 100644 --- a/Tests/packages.config +++ b/Tests/packages.config @@ -2,8 +2,12 @@ + + + + \ No newline at end of file diff --git a/Tests/packages.lock.json b/Tests/packages.lock.json index cd61b3e..eebc568 100644 --- a/Tests/packages.lock.json +++ b/Tests/packages.lock.json @@ -14,6 +14,12 @@ "resolved": "2.2.117", "contentHash": "PKkZrsZ+Go8CoQXOBFa+9+6v8r8LI4qdVpcxIsce/1xL1ZJJrcRkYiKws27L9dpe05Z052wI4Lr/+6UMcowvwg==" }, + "nanoFramework.Runtime.Events": { + "type": "Direct", + "requested": "[1.11.6, 1.11.6]", + "resolved": "1.11.6", + "contentHash": "xkltRh/2xKaZ9zmPHbVr32s1k+e17AInUBhzxKKkUDicJKF39yzTShSklb1OL6DBER5z71SpkGLyl9IdMK9l6w==" + }, "nanoFramework.System.Collections": { "type": "Direct", "requested": "[1.5.31, 1.5.31]", @@ -26,12 +32,30 @@ "resolved": "1.1.59", "contentHash": "A8DSQ9ygUnhDltHsVuJjxs3aRrig9hmeEDZgAFfQ4roaaHpHS/IN1gwCEYcV+I6wfsnEnojn3FQJh43Iw07ukg==" }, + "nanoFramework.System.Net": { + "type": "Direct", + "requested": "[1.10.52, 1.10.52]", + "resolved": "1.10.52", + "contentHash": "XKxtB7DYUnuePJRgBi37kLLAOzVcGk4l3YU7MgHv5RlIdN0pECQiv/DJXVt1HiGBQc318Zn8Zrd4uO5Nelxrig==" + }, + "nanoFramework.System.Net.Http": { + "type": "Direct", + "requested": "[1.5.99, 1.5.99]", + "resolved": "1.5.99", + "contentHash": "q611hrYfuZL+4n/JiFSguLK+0VYWiX34tQzgkC7mogpeNRobtAY9mgQe0urL++o1V1IKzit5W1bvUb2LN0ErWg==" + }, "nanoFramework.System.Text": { "type": "Direct", "requested": "[1.2.54, 1.2.54]", "resolved": "1.2.54", "contentHash": "k3OutSNRMs9di42LQ+5GbpHBY07aMEZWGkaS3Mj3ZU4cWqJc4deFGzRd+LBFQl1mRGdQaM5sl/euTZdcg8R9Zg==" }, + "nanoFramework.System.Threading": { + "type": "Direct", + "requested": "[1.1.19, 1.1.19]", + "resolved": "1.1.19", + "contentHash": "HjnY0DNoFnU+t1GiH8Wgf7pJCD9yMOcLVtAZXVAhstMKisVN/MDj9TvfXxZScRIz7ZDoZYUnb+Ixfl6rqyppvA==" + }, "nanoFramework.TestFramework": { "type": "Direct", "requested": "[2.1.94, 2.1.94]", diff --git a/nanoFramework.Aws.IoTCore.Devices/AwsSignatureVersion4/SignerForAuthorizationHeader.cs b/nanoFramework.Aws.IoTCore.Devices/AwsSignatureVersion4/SignerForAuthorizationHeader.cs index e0bddee..76ab8fc 100644 --- a/nanoFramework.Aws.IoTCore.Devices/AwsSignatureVersion4/SignerForAuthorizationHeader.cs +++ b/nanoFramework.Aws.IoTCore.Devices/AwsSignatureVersion4/SignerForAuthorizationHeader.cs @@ -112,7 +112,7 @@ public string ComputeSignature(IDictionary headers, canonicalizedHeaderNames, canonicalizedHeaders, bodyHash); - Debug.WriteLine($"\nCanonicalRequest:\n{canonicalRequest}"); + Debug.WriteLine($"\nDEBUG-CanonicalRequest:\n{canonicalRequest}"); // generate a hash of the canonical request, to go into signature computation var canonicalRequestHashBytes @@ -127,7 +127,7 @@ var canonicalRequestHashBytes stringToSign.Append($"{SCHEME}-{ALGORITHM}\n{dateTimeStamp}\n{scope}\n"); stringToSign.Append(ToHexString(canonicalRequestHashBytes, true)); - Debug.WriteLine($"\nStringToSign:\n{stringToSign}"); + Debug.WriteLine($"\nDEBUG-StringToSign:\n{stringToSign}"); // compute the signing key var kha = new HMACSHA256(DeriveSigningKey(awsSecretKey, Region, dateStamp, Service)); @@ -135,7 +135,7 @@ var canonicalRequestHashBytes // compute the AWS4 signature and return it var signature = kha.ComputeHash(Encoding.UTF8.GetBytes(stringToSign.ToString())); var signatureString = ToHexString(signature, true); - Debug.WriteLine($"\nSignature:\n{signatureString}"); + Debug.WriteLine($"\nDEBUG-Signature:\n{signatureString}"); var authString = new StringBuilder(); authString.Append($"{SCHEME}-{ALGORITHM} "); @@ -144,7 +144,7 @@ var canonicalRequestHashBytes authString.Append($"Signature={signatureString}"); var authorization = authString.ToString(); - Debug.WriteLine($"\nAuthorization:\n{authorization}"); + Debug.WriteLine($"\nDEBUG-Authorization:\n{authorization}"); return authorization; } diff --git a/nanoFramework.Aws.IoTCore.Devices/AwsSignatureVersion4/SignerForChunkedUpload.cs b/nanoFramework.Aws.IoTCore.Devices/AwsSignatureVersion4/SignerForChunkedUpload.cs index a4c0240..01b5939 100644 --- a/nanoFramework.Aws.IoTCore.Devices/AwsSignatureVersion4/SignerForChunkedUpload.cs +++ b/nanoFramework.Aws.IoTCore.Devices/AwsSignatureVersion4/SignerForChunkedUpload.cs @@ -147,7 +147,7 @@ public string ComputeSignature(IDictionary headers, canonicalizedHeaderNames, canonicalizedHeaders, bodyHash); - Debug.WriteLine($"\nCanonicalRequest:\n{canonicalRequest}"); + Debug.WriteLine($"\nDEBUG-CanonicalRequest:\n{canonicalRequest}"); // generate a hash of the canonical request, to go into signature computation var canonicalRequestHashBytes @@ -162,7 +162,7 @@ var canonicalRequestHashBytes stringToSign.Append($"{SCHEME}-{ALGORITHM}\n{DateTimeStamp}\n{Scope}\n"); stringToSign.Append(ToHexString(canonicalRequestHashBytes, true)); - Debug.WriteLine($"\nStringToSign:\n{stringToSign}"); + Debug.WriteLine($"\nDEBUG-StringToSign:\n{stringToSign}"); // compute the signing key SigningKey = DeriveSigningKey(awsSecretKey, Region, dateStamp, Service); @@ -172,7 +172,7 @@ var canonicalRequestHashBytes // compute the AWS4 signature and return it var signature = kha.ComputeHash(Encoding.UTF8.GetBytes(stringToSign.ToString())); var signatureString = ToHexString(signature, true); - Debug.WriteLine($"\nSignature:\n{signatureString}"); + Debug.WriteLine($"\nDEBUG-Signature:\n{signatureString}"); // cache the computed signature ready for chunk 0 upload LastComputedSignature = signatureString; @@ -184,7 +184,7 @@ var canonicalRequestHashBytes authString.Append($"Signature={signatureString}"); var authorization = authString.ToString(); - Debug.WriteLine($"\nAuthorization:\n{authorization}"); + Debug.WriteLine($"\nDEBUG-Authorization:\n{authorization}"); return authorization; } @@ -217,7 +217,7 @@ public long CalculateChunkedContentLength(long originalLength, long chunkSize) + (remainingBytes > 0 ? CalculateChunkHeaderLength(remainingBytes) : 0) + CalculateChunkHeaderLength(0); - Debug.WriteLine($"\nComputed chunked content length for original length {originalLength} bytes, chunk size {chunkSize / 1024}KB is {chunkedContentLength} bytes"); + Debug.WriteLine($"\nDEBUG-Computed chunked content length for original length {originalLength} bytes, chunk size {chunkSize / 1024}KB is {chunkedContentLength} bytes"); return chunkedContentLength; } @@ -300,7 +300,7 @@ public byte[] ConstructSignedChunk(long userDataLen, byte[] userData) ToHexString(CanonicalRequestHashAlgorithm.ComputeHash(Encoding.UTF8.GetBytes(nonsigExtension)), true) + "\n" + ToHexString(CanonicalRequestHashAlgorithm.ComputeHash(dataToChunk), true); - Debug.WriteLine($"\nChunkStringToSign:\n{chunkStringToSign}"); + Debug.WriteLine($"\nDEBUG-ChunkStringToSign:\n{chunkStringToSign}"); // compute the V4 signature for the chunk var chunkSignature @@ -308,7 +308,7 @@ var chunkSignature Encoding.UTF8.GetBytes(chunkStringToSign)), true); - Debug.WriteLine($"\nChunkSignature:\n{chunkSignature}"); + Debug.WriteLine($"\nDEBUG-ChunkSignature:\n{chunkSignature}"); // cache the signature to include with the next chunk's signature computation this.LastComputedSignature = chunkSignature; @@ -319,7 +319,7 @@ var chunkSignature chunkHeader.Append(nonsigExtension + CHUNK_SIGNATURE_HEADER + chunkSignature); chunkHeader.Append(CLRF); - Debug.WriteLine($"\nChunkHeader:\n{chunkHeader}"); + Debug.WriteLine($"\nDEBUG-ChunkHeader:\n{chunkHeader}"); try { diff --git a/nanoFramework.Aws.IoTCore.Devices/AwsSignatureVersion4/SignerForPOST.cs b/nanoFramework.Aws.IoTCore.Devices/AwsSignatureVersion4/SignerForPOST.cs index aac004a..e1b3adc 100644 --- a/nanoFramework.Aws.IoTCore.Devices/AwsSignatureVersion4/SignerForPOST.cs +++ b/nanoFramework.Aws.IoTCore.Devices/AwsSignatureVersion4/SignerForPOST.cs @@ -127,7 +127,7 @@ public string ComputeSignature(IDictionary headers, canonicalizedHeaderNames, canonicalizedHeaders, bodyHash); - Debug.WriteLine($"\nCanonicalRequest:\n{canonicalRequest}"); + //Debug.WriteLine($"\nDEBUG-CanonicalRequest:\n{canonicalRequest}"); // generate a hash of the canonical request, to go into signature computation var canonicalRequestHashBytes @@ -142,7 +142,7 @@ var canonicalRequestHashBytes stringToSign.Append($"{SCHEME}-{ALGORITHM}\n{dateTimeStamp}\n{scope}\n"); stringToSign.Append(ToHexString(canonicalRequestHashBytes, true)); - Debug.WriteLine($"\nStringToSign:\n{stringToSign}"); + //Debug.WriteLine($"\nDEBUG-StringToSign:\n{stringToSign}"); // compute the signing key var kha = new HMACSHA256(DeriveSigningKey(awsSecretKey, Region, dateStamp, Service)); @@ -150,7 +150,7 @@ var canonicalRequestHashBytes // compute the AWS4 signature and return it var signature = kha.ComputeHash(Encoding.UTF8.GetBytes(stringToSign.ToString())); var signatureString = ToHexString(signature, true); - Debug.WriteLine($"\nSignature:\n{signatureString}"); + //Debug.WriteLine($"\nDEBUG-Signature:\n{signatureString}"); var authString = new StringBuilder(); authString.Append($"{SCHEME}-{ALGORITHM} "); @@ -159,7 +159,7 @@ var canonicalRequestHashBytes authString.Append($"Signature={signatureString}"); var authorization = authString.ToString(); - Debug.WriteLine($"\nAuthorization:\n{authorization}"); + //Debug.WriteLine($"\nDEBUG-Authorization:\n{authorization}"); return authorization; } diff --git a/nanoFramework.Aws.IoTCore.Devices/AwsSignatureVersion4/SignerForQueryParameterAuth.cs b/nanoFramework.Aws.IoTCore.Devices/AwsSignatureVersion4/SignerForQueryParameterAuth.cs index 812af6f..554178f 100644 --- a/nanoFramework.Aws.IoTCore.Devices/AwsSignatureVersion4/SignerForQueryParameterAuth.cs +++ b/nanoFramework.Aws.IoTCore.Devices/AwsSignatureVersion4/SignerForQueryParameterAuth.cs @@ -125,7 +125,7 @@ public string ComputeSignature(IDictionary headers, canonicalizedHeaderNames, canonicalizedHeaders, bodyHash); - Debug.WriteLine($"\nCanonicalRequest:\n{canonicalRequest}"); + //Debug.WriteLine($"\nDEBUG-CanonicalRequest:\n{canonicalRequest}"); byte[] canonicalRequestHashBytes = CanonicalRequestHashAlgorithm.ComputeHash(Encoding.UTF8.GetBytes(canonicalRequest)); @@ -136,7 +136,7 @@ byte[] canonicalRequestHashBytes stringToSign.Append($"{SCHEME}-{ALGORITHM}\n{dateTimeStamp}\n{scope}\n"); stringToSign.Append(ToHexString(canonicalRequestHashBytes, true)); - Debug.WriteLine($"\nStringToSign:\n{stringToSign}"); + //Debug.WriteLine($"\nDEBUG-StringToSign:\n{stringToSign}"); // compute the multi-stage signing key var kha = new HMACSHA256(DeriveSigningKey(awsSecretKey, Region, dateStamp, Service)); @@ -145,7 +145,7 @@ byte[] canonicalRequestHashBytes // user to be embedded in the request as needed var signature = kha.ComputeHash(Encoding.UTF8.GetBytes(stringToSign.ToString())); var signatureString = ToHexString(signature, true); - Debug.WriteLine($"\nSignature:\n{signatureString}"); + //Debug.WriteLine($"\nDEBUG-Signature:\n{signatureString}"); // form up the authorization parameters for the caller to place in the query string var authString = new StringBuilder(); @@ -167,7 +167,7 @@ byte[] canonicalRequestHashBytes authString.Append($"&{X_Amz_Signature}={signatureString}"); var authorization = authString.ToString(); - Debug.WriteLine($"\nAuthorization:\n{authorization}"); + //Debug.WriteLine($"\nDEBUG-Authorization:\n{authorization}"); return authorization; } From 8316673075e7495e882f34a374d7345514191a8b Mon Sep 17 00:00:00 2001 From: Robin Jones Date: Tue, 9 Apr 2024 23:01:52 +0100 Subject: [PATCH 09/14] Update nugets --- Tests/Aws.IoTCore.Devices.UnitTests.nfproj | 8 +++---- Tests/packages.config | 8 +++---- Tests/packages.lock.json | 24 +++++++++---------- .../nanoFramework.Aws.IoTCore.Devices.nfproj | 6 ++--- .../packages.config | 6 ++--- .../packages.lock.json | 18 +++++++------- 6 files changed, 35 insertions(+), 35 deletions(-) diff --git a/Tests/Aws.IoTCore.Devices.UnitTests.nfproj b/Tests/Aws.IoTCore.Devices.UnitTests.nfproj index bd2f0d0..115e6e2 100644 --- a/Tests/Aws.IoTCore.Devices.UnitTests.nfproj +++ b/Tests/Aws.IoTCore.Devices.UnitTests.nfproj @@ -51,7 +51,7 @@ True - ..\packages\nanoFramework.Runtime.Events.1.11.6\lib\nanoFramework.Runtime.Events.dll + ..\packages\nanoFramework.Runtime.Events.1.11.15\lib\nanoFramework.Runtime.Events.dll ..\packages\nanoFramework.System.Collections.1.5.31\lib\nanoFramework.System.Collections.dll @@ -74,13 +74,13 @@ True - ..\packages\nanoFramework.System.Net.1.10.52\lib\System.Net.dll + ..\packages\nanoFramework.System.Net.1.10.77\lib\System.Net.dll - ..\packages\nanoFramework.System.Net.Http.1.5.99\lib\System.Net.Http.dll + ..\packages\nanoFramework.System.Net.Http.1.5.134\lib\System.Net.Http.dll - ..\packages\nanoFramework.System.Threading.1.1.19\lib\System.Threading.dll + ..\packages\nanoFramework.System.Threading.1.1.32\lib\System.Threading.dll diff --git a/Tests/packages.config b/Tests/packages.config index ae0d5a6..0efeedb 100644 --- a/Tests/packages.config +++ b/Tests/packages.config @@ -2,12 +2,12 @@ - + - - + + - + \ No newline at end of file diff --git a/Tests/packages.lock.json b/Tests/packages.lock.json index eebc568..22026ad 100644 --- a/Tests/packages.lock.json +++ b/Tests/packages.lock.json @@ -16,9 +16,9 @@ }, "nanoFramework.Runtime.Events": { "type": "Direct", - "requested": "[1.11.6, 1.11.6]", - "resolved": "1.11.6", - "contentHash": "xkltRh/2xKaZ9zmPHbVr32s1k+e17AInUBhzxKKkUDicJKF39yzTShSklb1OL6DBER5z71SpkGLyl9IdMK9l6w==" + "requested": "[1.11.15, 1.11.15]", + "resolved": "1.11.15", + "contentHash": "3uDNSTfiaewDAyi6fOMWYru0JCn/gr8DEv+Ro/V12SzojU9Dyxl5nSVOBtBXts7vErfIthB6SPiK180AMnrI8A==" }, "nanoFramework.System.Collections": { "type": "Direct", @@ -34,15 +34,15 @@ }, "nanoFramework.System.Net": { "type": "Direct", - "requested": "[1.10.52, 1.10.52]", - "resolved": "1.10.52", - "contentHash": "XKxtB7DYUnuePJRgBi37kLLAOzVcGk4l3YU7MgHv5RlIdN0pECQiv/DJXVt1HiGBQc318Zn8Zrd4uO5Nelxrig==" + "requested": "[1.10.77, 1.10.77]", + "resolved": "1.10.77", + "contentHash": "d/zIQqt0Y4e49tmtc2sfluE3DQa3w6hj3DHXVt5B+R2onytIJaKzJy8k8qYVyVy+EQLhdruyvcHSTIaygZrUzQ==" }, "nanoFramework.System.Net.Http": { "type": "Direct", - "requested": "[1.5.99, 1.5.99]", - "resolved": "1.5.99", - "contentHash": "q611hrYfuZL+4n/JiFSguLK+0VYWiX34tQzgkC7mogpeNRobtAY9mgQe0urL++o1V1IKzit5W1bvUb2LN0ErWg==" + "requested": "[1.5.134, 1.5.134]", + "resolved": "1.5.134", + "contentHash": "bRAjyreJCvwljphELYpZHaHoR06/RhgHvexn/r+9u2YXrIVoC3i+uXC8JxHK+86W3oSLC47LHXjq/NOQY7RRdQ==" }, "nanoFramework.System.Text": { "type": "Direct", @@ -52,9 +52,9 @@ }, "nanoFramework.System.Threading": { "type": "Direct", - "requested": "[1.1.19, 1.1.19]", - "resolved": "1.1.19", - "contentHash": "HjnY0DNoFnU+t1GiH8Wgf7pJCD9yMOcLVtAZXVAhstMKisVN/MDj9TvfXxZScRIz7ZDoZYUnb+Ixfl6rqyppvA==" + "requested": "[1.1.32, 1.1.32]", + "resolved": "1.1.32", + "contentHash": "6o7Y4gH15FLuo2FWGLecABiCD57V5QMf5g/hEneV64VmhoXI8Bk7r6BDBPTfAePs738xbc1ECpA5dJmbSmtilg==" }, "nanoFramework.TestFramework": { "type": "Direct", diff --git a/nanoFramework.Aws.IoTCore.Devices/nanoFramework.Aws.IoTCore.Devices.nfproj b/nanoFramework.Aws.IoTCore.Devices/nanoFramework.Aws.IoTCore.Devices.nfproj index fb770db..4bc2772 100644 --- a/nanoFramework.Aws.IoTCore.Devices/nanoFramework.Aws.IoTCore.Devices.nfproj +++ b/nanoFramework.Aws.IoTCore.Devices/nanoFramework.Aws.IoTCore.Devices.nfproj @@ -98,13 +98,13 @@ True - ..\packages\nanoFramework.System.Net.Http.1.5.99\lib\System.Net.Http.dll + ..\packages\nanoFramework.System.Net.Http.1.5.134\lib\System.Net.Http.dll - ..\packages\nanoFramework.System.Net.WebSockets.1.1.57\lib\System.Net.WebSockets.dll + ..\packages\nanoFramework.System.Net.WebSockets.1.1.92\lib\System.Net.WebSockets.dll - ..\packages\nanoFramework.System.Text.RegularExpressions.1.1.37\lib\System.Text.RegularExpressions.dll + ..\packages\nanoFramework.System.Text.RegularExpressions.1.1.51\lib\System.Text.RegularExpressions.dll ..\packages\nanoFramework.System.Threading.1.1.32\lib\System.Threading.dll diff --git a/nanoFramework.Aws.IoTCore.Devices/packages.config b/nanoFramework.Aws.IoTCore.Devices/packages.config index 37f9444..3424f20 100644 --- a/nanoFramework.Aws.IoTCore.Devices/packages.config +++ b/nanoFramework.Aws.IoTCore.Devices/packages.config @@ -8,10 +8,10 @@ - - + + - + \ No newline at end of file diff --git a/nanoFramework.Aws.IoTCore.Devices/packages.lock.json b/nanoFramework.Aws.IoTCore.Devices/packages.lock.json index c88f9ca..f264000 100644 --- a/nanoFramework.Aws.IoTCore.Devices/packages.lock.json +++ b/nanoFramework.Aws.IoTCore.Devices/packages.lock.json @@ -52,15 +52,15 @@ }, "nanoFramework.System.Net.Http": { "type": "Direct", - "requested": "[1.5.99, 1.5.99]", - "resolved": "1.5.99", - "contentHash": "q611hrYfuZL+4n/JiFSguLK+0VYWiX34tQzgkC7mogpeNRobtAY9mgQe0urL++o1V1IKzit5W1bvUb2LN0ErWg==" + "requested": "[1.5.134, 1.5.134]", + "resolved": "1.5.134", + "contentHash": "bRAjyreJCvwljphELYpZHaHoR06/RhgHvexn/r+9u2YXrIVoC3i+uXC8JxHK+86W3oSLC47LHXjq/NOQY7RRdQ==" }, "nanoFramework.System.Net.WebSockets": { "type": "Direct", - "requested": "[1.1.57, 1.1.57]", - "resolved": "1.1.57", - "contentHash": "ae4ri515PR8IpzIeRZlYNyYm/W22AMa6I0bOXJweCT2G6GbRKjy15J3S5WtIFQcG/OqY3EGALN/198R3crvOLA==" + "requested": "[1.1.92, 1.1.92]", + "resolved": "1.1.92", + "contentHash": "HlF1famuQWsQWfCvLh4N52Jl36XwUWq8tyQkscuXiNCPWNXBvNmjTeHj1QlJ3DFOyMN4YX+9xWUMQrgW1dgq4Q==" }, "nanoFramework.System.Text": { "type": "Direct", @@ -70,9 +70,9 @@ }, "nanoFramework.System.Text.RegularExpressions": { "type": "Direct", - "requested": "[1.1.37, 1.1.37]", - "resolved": "1.1.37", - "contentHash": "l0Vv7SEF2+59Ps183Sf8sSVZKvfFBPRP5gTYtnv5cdEm+KntXRhIy3u9Cp8XnueMrLkHEKYDQ6VG6gz4n7tbfA==" + "requested": "[1.1.51, 1.1.51]", + "resolved": "1.1.51", + "contentHash": "Y3PDWE1XW34tKXdij3p4OOo/AOOz8fU1JrOuQT9SGJMviISHdKlHEKKr8ynshbE8GDSe6y410WdrCg5rDa7nlA==" }, "nanoFramework.System.Threading": { "type": "Direct", From c566466696ce872b068bfebe7da605dee0d9e60b Mon Sep 17 00:00:00 2001 From: Robin Jones Date: Tue, 9 Apr 2024 23:26:34 +0100 Subject: [PATCH 10/14] Update nuspec --- nanoFramework.Aws.IoTCore.Devices.nuspec | 4 ++-- .../AwsSignatureVersion4/SignerForQueryParameterAuth.cs | 1 - 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/nanoFramework.Aws.IoTCore.Devices.nuspec b/nanoFramework.Aws.IoTCore.Devices.nuspec index 422eab1..813cdf0 100644 --- a/nanoFramework.Aws.IoTCore.Devices.nuspec +++ b/nanoFramework.Aws.IoTCore.Devices.nuspec @@ -22,8 +22,8 @@ This is an SDK for Aws IoTCore. - - + + diff --git a/nanoFramework.Aws.IoTCore.Devices/AwsSignatureVersion4/SignerForQueryParameterAuth.cs b/nanoFramework.Aws.IoTCore.Devices/AwsSignatureVersion4/SignerForQueryParameterAuth.cs index 554178f..a1c8a38 100644 --- a/nanoFramework.Aws.IoTCore.Devices/AwsSignatureVersion4/SignerForQueryParameterAuth.cs +++ b/nanoFramework.Aws.IoTCore.Devices/AwsSignatureVersion4/SignerForQueryParameterAuth.cs @@ -6,7 +6,6 @@ using System; using System.Collections; -using System.Diagnostics; using System.Security.Cryptography; using System.Text; using System.Web; From a95896fb961d75c85004b98b0423faebc1b4b053 Mon Sep 17 00:00:00 2001 From: Robin Jones Date: Sat, 10 Aug 2024 16:52:37 +0100 Subject: [PATCH 11/14] Update nugets --- Tests/Aws.IoTCore.Devices.UnitTests.nfproj | 21 ++++------ Tests/packages.config | 10 ++--- Tests/packages.lock.json | 30 ++++++------- .../nanoFramework.Aws.IoTCore.Devices.nfproj | 29 ++++++------- .../packages.config | 14 +++---- .../packages.lock.json | 42 +++++++++---------- 6 files changed, 69 insertions(+), 77 deletions(-) diff --git a/Tests/Aws.IoTCore.Devices.UnitTests.nfproj b/Tests/Aws.IoTCore.Devices.UnitTests.nfproj index 115e6e2..9df1a20 100644 --- a/Tests/Aws.IoTCore.Devices.UnitTests.nfproj +++ b/Tests/Aws.IoTCore.Devices.UnitTests.nfproj @@ -46,12 +46,11 @@ ..\packages\nanoFramework.CoreLibrary.1.15.5\lib\mscorlib.dll True - - ..\packages\nanoFramework.Json.2.2.117\lib\nanoFramework.Json.dll - True + + ..\packages\nanoFramework.Json.2.2.122\lib\nanoFramework.Json.dll - ..\packages\nanoFramework.Runtime.Events.1.11.15\lib\nanoFramework.Runtime.Events.dll + ..\packages\nanoFramework.Runtime.Events.1.11.18\lib\nanoFramework.Runtime.Events.dll ..\packages\nanoFramework.System.Collections.1.5.31\lib\nanoFramework.System.Collections.dll @@ -61,23 +60,21 @@ ..\packages\nanoFramework.System.Text.1.2.54\lib\nanoFramework.System.Text.dll True - - ..\packages\nanoFramework.TestFramework.2.1.94\lib\nanoFramework.TestFramework.dll - True + + ..\packages\nanoFramework.TestFramework.2.1.107\lib\nanoFramework.TestFramework.dll - - ..\packages\nanoFramework.TestFramework.2.1.94\lib\nanoFramework.UnitTestLauncher.exe - True + + ..\packages\nanoFramework.TestFramework.2.1.107\lib\nanoFramework.UnitTestLauncher.exe ..\packages\nanoFramework.System.IO.Streams.1.1.59\lib\System.IO.Streams.dll True - ..\packages\nanoFramework.System.Net.1.10.77\lib\System.Net.dll + ..\packages\nanoFramework.System.Net.1.11.1\lib\System.Net.dll - ..\packages\nanoFramework.System.Net.Http.1.5.134\lib\System.Net.Http.dll + ..\packages\nanoFramework.System.Net.Http.1.5.145\lib\System.Net.Http.dll ..\packages\nanoFramework.System.Threading.1.1.32\lib\System.Threading.dll diff --git a/Tests/packages.config b/Tests/packages.config index 0efeedb..38bfd73 100644 --- a/Tests/packages.config +++ b/Tests/packages.config @@ -1,13 +1,13 @@  - - + + - - + + - + \ No newline at end of file diff --git a/Tests/packages.lock.json b/Tests/packages.lock.json index 22026ad..bb02233 100644 --- a/Tests/packages.lock.json +++ b/Tests/packages.lock.json @@ -10,15 +10,15 @@ }, "nanoFramework.Json": { "type": "Direct", - "requested": "[2.2.117, 2.2.117]", - "resolved": "2.2.117", - "contentHash": "PKkZrsZ+Go8CoQXOBFa+9+6v8r8LI4qdVpcxIsce/1xL1ZJJrcRkYiKws27L9dpe05Z052wI4Lr/+6UMcowvwg==" + "requested": "[2.2.122, 2.2.122]", + "resolved": "2.2.122", + "contentHash": "rIQKszJnbsy72JQGVxBx2PV7s5Ei2WnhMHtoz7wiCl47A6C4wKiayE5Ath07gw9HtRqrUcq3hnIwDEWD65T0sQ==" }, "nanoFramework.Runtime.Events": { "type": "Direct", - "requested": "[1.11.15, 1.11.15]", - "resolved": "1.11.15", - "contentHash": "3uDNSTfiaewDAyi6fOMWYru0JCn/gr8DEv+Ro/V12SzojU9Dyxl5nSVOBtBXts7vErfIthB6SPiK180AMnrI8A==" + "requested": "[1.11.18, 1.11.18]", + "resolved": "1.11.18", + "contentHash": "t0XpUkdyBBBv/0S4oGx3yUJG1iPYWc38odvZW8mVoioSxZOJrRkRHpNfwYxTxtP4LIEyyesOPEH42d05FHfHzA==" }, "nanoFramework.System.Collections": { "type": "Direct", @@ -34,15 +34,15 @@ }, "nanoFramework.System.Net": { "type": "Direct", - "requested": "[1.10.77, 1.10.77]", - "resolved": "1.10.77", - "contentHash": "d/zIQqt0Y4e49tmtc2sfluE3DQa3w6hj3DHXVt5B+R2onytIJaKzJy8k8qYVyVy+EQLhdruyvcHSTIaygZrUzQ==" + "requested": "[1.11.1, 1.11.1]", + "resolved": "1.11.1", + "contentHash": "TgHj3JFlGScw+0/l2Rix7uSLrb+pc/IFHJTQwcEogUlF4gtV18B52JUtrxYiLAR1KyFxwsGiA9txrTHPAZBvXQ==" }, "nanoFramework.System.Net.Http": { "type": "Direct", - "requested": "[1.5.134, 1.5.134]", - "resolved": "1.5.134", - "contentHash": "bRAjyreJCvwljphELYpZHaHoR06/RhgHvexn/r+9u2YXrIVoC3i+uXC8JxHK+86W3oSLC47LHXjq/NOQY7RRdQ==" + "requested": "[1.5.145, 1.5.145]", + "resolved": "1.5.145", + "contentHash": "hyxQsn+u/ZXBiO01Z0lHCM22fNlUM9jyQJdLgGpXIt99XhdX1L8nZTr8QmZA31V0BZ4SslwwMZJ4Pl8KYW/0dA==" }, "nanoFramework.System.Text": { "type": "Direct", @@ -58,9 +58,9 @@ }, "nanoFramework.TestFramework": { "type": "Direct", - "requested": "[2.1.94, 2.1.94]", - "resolved": "2.1.94", - "contentHash": "jBbtJ7/7UUiqUbZ2ZshzHEarKZEHSGfnqli5QCMafRW9YxdsI5XYqTjRDgW3kyX2EX6zWk+cIgeLIPJiAzoWRA==" + "requested": "[2.1.107, 2.1.107]", + "resolved": "2.1.107", + "contentHash": "6pLzZwKdAsFoLaeP9iRivJaCs8AbaoX8wvB4oLH0+CBBQK209mfnkqqUzsL9sGwJIM9/bKoq6AMGmMZzI4iXww==" } } } diff --git a/nanoFramework.Aws.IoTCore.Devices/nanoFramework.Aws.IoTCore.Devices.nfproj b/nanoFramework.Aws.IoTCore.Devices/nanoFramework.Aws.IoTCore.Devices.nfproj index d452682..44f4782 100644 --- a/nanoFramework.Aws.IoTCore.Devices/nanoFramework.Aws.IoTCore.Devices.nfproj +++ b/nanoFramework.Aws.IoTCore.Devices/nanoFramework.Aws.IoTCore.Devices.nfproj @@ -61,21 +61,17 @@ ..\packages\nanoFramework.CoreLibrary.1.15.5\lib\mscorlib.dll True - - ..\packages\nanoFramework.Json.2.2.117\lib\nanoFramework.Json.dll - True + + ..\packages\nanoFramework.Json.2.2.122\lib\nanoFramework.Json.dll - - ..\packages\nanoFramework.M2Mqtt.5.1.126\lib\nanoFramework.M2Mqtt.dll - True + + ..\packages\nanoFramework.M2Mqtt.5.1.138\lib\nanoFramework.M2Mqtt.dll - - ..\packages\nanoFramework.M2Mqtt.5.1.126\lib\nanoFramework.M2Mqtt.Core.dll - True + + ..\packages\nanoFramework.M2Mqtt.5.1.138\lib\nanoFramework.M2Mqtt.Core.dll - - ..\packages\nanoFramework.Runtime.Events.1.11.15\lib\nanoFramework.Runtime.Events.dll - True + + ..\packages\nanoFramework.Runtime.Events.1.11.18\lib\nanoFramework.Runtime.Events.dll ..\packages\nanoFramework.Runtime.Native.1.6.12\lib\nanoFramework.Runtime.Native.dll @@ -93,15 +89,14 @@ ..\packages\nanoFramework.System.IO.Streams.1.1.59\lib\System.IO.Streams.dll True - - ..\packages\nanoFramework.System.Net.1.10.77\lib\System.Net.dll - True + + ..\packages\nanoFramework.System.Net.1.11.1\lib\System.Net.dll - ..\packages\nanoFramework.System.Net.Http.1.5.134\lib\System.Net.Http.dll + ..\packages\nanoFramework.System.Net.Http.1.5.145\lib\System.Net.Http.dll - ..\packages\nanoFramework.System.Net.WebSockets.1.1.92\lib\System.Net.WebSockets.dll + ..\packages\nanoFramework.System.Net.WebSockets.1.1.102\lib\System.Net.WebSockets.dll ..\packages\nanoFramework.System.Text.RegularExpressions.1.1.51\lib\System.Text.RegularExpressions.dll diff --git a/nanoFramework.Aws.IoTCore.Devices/packages.config b/nanoFramework.Aws.IoTCore.Devices/packages.config index 32cc6db..d4430b7 100644 --- a/nanoFramework.Aws.IoTCore.Devices/packages.config +++ b/nanoFramework.Aws.IoTCore.Devices/packages.config @@ -1,17 +1,17 @@  - - - + + + - - - + + + - + \ No newline at end of file diff --git a/nanoFramework.Aws.IoTCore.Devices/packages.lock.json b/nanoFramework.Aws.IoTCore.Devices/packages.lock.json index 0e2663a..bad342f 100644 --- a/nanoFramework.Aws.IoTCore.Devices/packages.lock.json +++ b/nanoFramework.Aws.IoTCore.Devices/packages.lock.json @@ -10,21 +10,21 @@ }, "nanoFramework.Json": { "type": "Direct", - "requested": "[2.2.117, 2.2.117]", - "resolved": "2.2.117", - "contentHash": "PKkZrsZ+Go8CoQXOBFa+9+6v8r8LI4qdVpcxIsce/1xL1ZJJrcRkYiKws27L9dpe05Z052wI4Lr/+6UMcowvwg==" + "requested": "[2.2.122, 2.2.122]", + "resolved": "2.2.122", + "contentHash": "rIQKszJnbsy72JQGVxBx2PV7s5Ei2WnhMHtoz7wiCl47A6C4wKiayE5Ath07gw9HtRqrUcq3hnIwDEWD65T0sQ==" }, "nanoFramework.M2Mqtt": { "type": "Direct", - "requested": "[5.1.126, 5.1.126]", - "resolved": "5.1.126", - "contentHash": "a88BLC3rFCsxdUCdBZaHU96TTE102Y2SJnKPz8DfOlVZr2UJU3qLaX7R2fCFInS+XEakVcAFWLW+Bygsd4oQ/A==" + "requested": "[5.1.138, 5.1.138]", + "resolved": "5.1.138", + "contentHash": "TeVops3YiAP96csyD+5dtgW5ZuY1WX4WUP3TJjnimJlsEUoJBIfVPk3U4YPfzGWkkLPhXao4EmBuKqIoiSvX9A==" }, "nanoFramework.Runtime.Events": { "type": "Direct", - "requested": "[1.11.15, 1.11.15]", - "resolved": "1.11.15", - "contentHash": "3uDNSTfiaewDAyi6fOMWYru0JCn/gr8DEv+Ro/V12SzojU9Dyxl5nSVOBtBXts7vErfIthB6SPiK180AMnrI8A==" + "requested": "[1.11.18, 1.11.18]", + "resolved": "1.11.18", + "contentHash": "t0XpUkdyBBBv/0S4oGx3yUJG1iPYWc38odvZW8mVoioSxZOJrRkRHpNfwYxTxtP4LIEyyesOPEH42d05FHfHzA==" }, "nanoFramework.Runtime.Native": { "type": "Direct", @@ -46,21 +46,21 @@ }, "nanoFramework.System.Net": { "type": "Direct", - "requested": "[1.10.77, 1.10.77]", - "resolved": "1.10.77", - "contentHash": "d/zIQqt0Y4e49tmtc2sfluE3DQa3w6hj3DHXVt5B+R2onytIJaKzJy8k8qYVyVy+EQLhdruyvcHSTIaygZrUzQ==" + "requested": "[1.11.1, 1.11.1]", + "resolved": "1.11.1", + "contentHash": "TgHj3JFlGScw+0/l2Rix7uSLrb+pc/IFHJTQwcEogUlF4gtV18B52JUtrxYiLAR1KyFxwsGiA9txrTHPAZBvXQ==" }, "nanoFramework.System.Net.Http": { "type": "Direct", - "requested": "[1.5.134, 1.5.134]", - "resolved": "1.5.134", - "contentHash": "bRAjyreJCvwljphELYpZHaHoR06/RhgHvexn/r+9u2YXrIVoC3i+uXC8JxHK+86W3oSLC47LHXjq/NOQY7RRdQ==" + "requested": "[1.5.145, 1.5.145]", + "resolved": "1.5.145", + "contentHash": "hyxQsn+u/ZXBiO01Z0lHCM22fNlUM9jyQJdLgGpXIt99XhdX1L8nZTr8QmZA31V0BZ4SslwwMZJ4Pl8KYW/0dA==" }, "nanoFramework.System.Net.WebSockets": { "type": "Direct", - "requested": "[1.1.92, 1.1.92]", - "resolved": "1.1.92", - "contentHash": "HlF1famuQWsQWfCvLh4N52Jl36XwUWq8tyQkscuXiNCPWNXBvNmjTeHj1QlJ3DFOyMN4YX+9xWUMQrgW1dgq4Q==" + "requested": "[1.1.102, 1.1.102]", + "resolved": "1.1.102", + "contentHash": "K5s3I5OKLj9+IQrU54DhZ5rTdLHMYHnzN6CMH/ZyFkYBUxT3mWF44TLINKEOv4NQMUQ/U7byrKRj2WLAx2HTaw==" }, "nanoFramework.System.Text": { "type": "Direct", @@ -82,9 +82,9 @@ }, "Nerdbank.GitVersioning": { "type": "Direct", - "requested": "[3.6.133, 3.6.133]", - "resolved": "3.6.133", - "contentHash": "VZWMd5YAeDxpjWjAP/X6bAxnRMiEf6tES/ITN0X5CHJgkWLLeHGmEALivmTAfYM6P+P/3Szy6VCITUAkqjcHVw==" + "requested": "[3.6.141, 3.6.141]", + "resolved": "3.6.141", + "contentHash": "A2+obbz6Xl6nHdDDMiqHCrn6Lktrs/pLq4BIQXDUD2O2gd5fz2WobQyX30Kp0mPPXjEOgBA1r5s6iYVurTkUig==" } } } From 279ef86cc40f6a259483dfee28f19abe44ab79ce Mon Sep 17 00:00:00 2001 From: Robin Jones Date: Sat, 10 Aug 2024 17:02:33 +0100 Subject: [PATCH 12/14] Update nanoFramework.Aws.IoTCore.Devices.nuspec --- nanoFramework.Aws.IoTCore.Devices.nuspec | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/nanoFramework.Aws.IoTCore.Devices.nuspec b/nanoFramework.Aws.IoTCore.Devices.nuspec index 344eaf1..5d9f160 100644 --- a/nanoFramework.Aws.IoTCore.Devices.nuspec +++ b/nanoFramework.Aws.IoTCore.Devices.nuspec @@ -20,9 +20,9 @@ This is an SDK for Aws IoTCore. nanoFramework C# csharp netmf netnf AWS IoTCore Aws IoTCore - - - + + + From ca666daec98225be5eafb27d5e5172ad84544152 Mon Sep 17 00:00:00 2001 From: Robin Jones Date: Mon, 23 Jun 2025 19:11:38 +0100 Subject: [PATCH 13/14] Update nugets --- Tests/Aws.IoTCore.Devices.UnitTests.nfproj | 8 +++---- Tests/packages.config | 8 +++---- Tests/packages.lock.json | 24 +++++++++---------- .../nanoFramework.Aws.IoTCore.Devices.nfproj | 6 ++--- .../packages.config | 6 ++--- .../packages.lock.json | 18 +++++++------- 6 files changed, 35 insertions(+), 35 deletions(-) diff --git a/Tests/Aws.IoTCore.Devices.UnitTests.nfproj b/Tests/Aws.IoTCore.Devices.UnitTests.nfproj index 3afbcd3..ad7bba8 100644 --- a/Tests/Aws.IoTCore.Devices.UnitTests.nfproj +++ b/Tests/Aws.IoTCore.Devices.UnitTests.nfproj @@ -49,7 +49,7 @@ ..\packages\nanoFramework.Json.2.2.199\lib\nanoFramework.Json.dll - ..\packages\nanoFramework.Runtime.Events.1.11.18\lib\nanoFramework.Runtime.Events.dll + ..\packages\nanoFramework.Runtime.Events.1.11.32\lib\nanoFramework.Runtime.Events.dll ..\packages\nanoFramework.System.Collections.1.5.67\lib\nanoFramework.System.Collections.dll @@ -67,13 +67,13 @@ ..\packages\nanoFramework.System.IO.Streams.1.1.96\lib\System.IO.Streams.dll - ..\packages\nanoFramework.System.Net.1.11.1\lib\System.Net.dll + ..\packages\nanoFramework.System.Net.1.11.43\lib\System.Net.dll - ..\packages\nanoFramework.System.Net.Http.1.5.145\lib\System.Net.Http.dll + ..\packages\nanoFramework.System.Net.Http.1.5.196\lib\System.Net.Http.dll - ..\packages\nanoFramework.System.Threading.1.1.32\lib\System.Threading.dll + ..\packages\nanoFramework.System.Threading.1.1.52\lib\System.Threading.dll diff --git a/Tests/packages.config b/Tests/packages.config index 2c7ec8b..51ba884 100644 --- a/Tests/packages.config +++ b/Tests/packages.config @@ -2,12 +2,12 @@ - + - - + + - + \ No newline at end of file diff --git a/Tests/packages.lock.json b/Tests/packages.lock.json index 8e6d0ba..bb32c5c 100644 --- a/Tests/packages.lock.json +++ b/Tests/packages.lock.json @@ -16,9 +16,9 @@ }, "nanoFramework.Runtime.Events": { "type": "Direct", - "requested": "[1.11.18, 1.11.18]", - "resolved": "1.11.18", - "contentHash": "t0XpUkdyBBBv/0S4oGx3yUJG1iPYWc38odvZW8mVoioSxZOJrRkRHpNfwYxTxtP4LIEyyesOPEH42d05FHfHzA==" + "requested": "[1.11.32, 1.11.32]", + "resolved": "1.11.32", + "contentHash": "NyLUIwJDlpl5VKSd+ljmdDtO2WHHBvPvruo1ccaL+hd79z+6XMYze1AccOVXKGiZenLBCwDmFHwpgIQyHkM7GA==" }, "nanoFramework.System.Collections": { "type": "Direct", @@ -34,15 +34,15 @@ }, "nanoFramework.System.Net": { "type": "Direct", - "requested": "[1.11.1, 1.11.1]", - "resolved": "1.11.1", - "contentHash": "TgHj3JFlGScw+0/l2Rix7uSLrb+pc/IFHJTQwcEogUlF4gtV18B52JUtrxYiLAR1KyFxwsGiA9txrTHPAZBvXQ==" + "requested": "[1.11.43, 1.11.43]", + "resolved": "1.11.43", + "contentHash": "USwz59gxcNUzsiXfQohWSi8ANNwGDsp+qG4zBtHZU3rKMtvTsLI3rxdfMC77VehKqsCPn7aK3PU2oCRFo+1Rgg==" }, "nanoFramework.System.Net.Http": { "type": "Direct", - "requested": "[1.5.145, 1.5.145]", - "resolved": "1.5.145", - "contentHash": "hyxQsn+u/ZXBiO01Z0lHCM22fNlUM9jyQJdLgGpXIt99XhdX1L8nZTr8QmZA31V0BZ4SslwwMZJ4Pl8KYW/0dA==" + "requested": "[1.5.196, 1.5.196]", + "resolved": "1.5.196", + "contentHash": "2qfUnvJa55Wx5C86HopeoUZVfXY+L6stufrlcKNHlalqIk4cc3Orv0Eqc0oroy3iB6aQGDw+tji7WYJS8LQNoA==" }, "nanoFramework.System.Text": { "type": "Direct", @@ -52,9 +52,9 @@ }, "nanoFramework.System.Threading": { "type": "Direct", - "requested": "[1.1.32, 1.1.32]", - "resolved": "1.1.32", - "contentHash": "6o7Y4gH15FLuo2FWGLecABiCD57V5QMf5g/hEneV64VmhoXI8Bk7r6BDBPTfAePs738xbc1ECpA5dJmbSmtilg==" + "requested": "[1.1.52, 1.1.52]", + "resolved": "1.1.52", + "contentHash": "kv+US/+7QKV1iT/snxBh032vwZ+3krJ4vujlSsvmS2nNj/nK64R3bq/ST3bCFquxHDD0mog8irtCBCsFazr4kA==" }, "nanoFramework.TestFramework": { "type": "Direct", diff --git a/nanoFramework.Aws.IoTCore.Devices/nanoFramework.Aws.IoTCore.Devices.nfproj b/nanoFramework.Aws.IoTCore.Devices/nanoFramework.Aws.IoTCore.Devices.nfproj index 16a226e..9796821 100644 --- a/nanoFramework.Aws.IoTCore.Devices/nanoFramework.Aws.IoTCore.Devices.nfproj +++ b/nanoFramework.Aws.IoTCore.Devices/nanoFramework.Aws.IoTCore.Devices.nfproj @@ -88,13 +88,13 @@ ..\packages\nanoFramework.System.Net.1.11.43\lib\System.Net.dll - ..\packages\nanoFramework.System.Net.Http.1.5.145\lib\System.Net.Http.dll + ..\packages\nanoFramework.System.Net.Http.1.5.196\lib\System.Net.Http.dll - ..\packages\nanoFramework.System.Net.WebSockets.1.1.102\lib\System.Net.WebSockets.dll + ..\packages\nanoFramework.System.Net.WebSockets.1.1.151\lib\System.Net.WebSockets.dll - ..\packages\nanoFramework.System.Text.RegularExpressions.1.1.51\lib\System.Text.RegularExpressions.dll + ..\packages\nanoFramework.System.Text.RegularExpressions.1.1.128\lib\System.Text.RegularExpressions.dll ..\packages\nanoFramework.System.Threading.1.1.52\lib\System.Threading.dll diff --git a/nanoFramework.Aws.IoTCore.Devices/packages.config b/nanoFramework.Aws.IoTCore.Devices/packages.config index 7c321cf..dda19fa 100644 --- a/nanoFramework.Aws.IoTCore.Devices/packages.config +++ b/nanoFramework.Aws.IoTCore.Devices/packages.config @@ -8,10 +8,10 @@ - - + + - + \ No newline at end of file diff --git a/nanoFramework.Aws.IoTCore.Devices/packages.lock.json b/nanoFramework.Aws.IoTCore.Devices/packages.lock.json index 7213097..f6d8754 100644 --- a/nanoFramework.Aws.IoTCore.Devices/packages.lock.json +++ b/nanoFramework.Aws.IoTCore.Devices/packages.lock.json @@ -52,15 +52,15 @@ }, "nanoFramework.System.Net.Http": { "type": "Direct", - "requested": "[1.5.145, 1.5.145]", - "resolved": "1.5.145", - "contentHash": "hyxQsn+u/ZXBiO01Z0lHCM22fNlUM9jyQJdLgGpXIt99XhdX1L8nZTr8QmZA31V0BZ4SslwwMZJ4Pl8KYW/0dA==" + "requested": "[1.5.196, 1.5.196]", + "resolved": "1.5.196", + "contentHash": "2qfUnvJa55Wx5C86HopeoUZVfXY+L6stufrlcKNHlalqIk4cc3Orv0Eqc0oroy3iB6aQGDw+tji7WYJS8LQNoA==" }, "nanoFramework.System.Net.WebSockets": { "type": "Direct", - "requested": "[1.1.102, 1.1.102]", - "resolved": "1.1.102", - "contentHash": "K5s3I5OKLj9+IQrU54DhZ5rTdLHMYHnzN6CMH/ZyFkYBUxT3mWF44TLINKEOv4NQMUQ/U7byrKRj2WLAx2HTaw==" + "requested": "[1.1.151, 1.1.151]", + "resolved": "1.1.151", + "contentHash": "1xblkj0W+3yFq9OMUIgzUSna43ss2qtj5N6kS8n/DyTNmYplmFAIVhyGIXpAJghQuLFPoSND63KsU+GY37Uftg==" }, "nanoFramework.System.Text": { "type": "Direct", @@ -70,9 +70,9 @@ }, "nanoFramework.System.Text.RegularExpressions": { "type": "Direct", - "requested": "[1.1.51, 1.1.51]", - "resolved": "1.1.51", - "contentHash": "Y3PDWE1XW34tKXdij3p4OOo/AOOz8fU1JrOuQT9SGJMviISHdKlHEKKr8ynshbE8GDSe6y410WdrCg5rDa7nlA==" + "requested": "[1.1.128, 1.1.128]", + "resolved": "1.1.128", + "contentHash": "6INVYaaVjHFQ05BfIwm8hI9MFwVbuiE7zM87oADZm5XOBindOG9mOx7ooDaX+0Ni+JXm+ZvkdwBqLye1vZBI8Q==" }, "nanoFramework.System.Threading": { "type": "Direct", From e154af69367107608bf3f635db83cf1b1a58a302 Mon Sep 17 00:00:00 2001 From: Robin Jones Date: Mon, 23 Jun 2025 19:28:50 +0100 Subject: [PATCH 14/14] Update nanoFramework.Aws.IoTCore.Devices.nuspec Fixes --- nanoFramework.Aws.IoTCore.Devices.nuspec | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/nanoFramework.Aws.IoTCore.Devices.nuspec b/nanoFramework.Aws.IoTCore.Devices.nuspec index eff8bfc..b0784f8 100644 --- a/nanoFramework.Aws.IoTCore.Devices.nuspec +++ b/nanoFramework.Aws.IoTCore.Devices.nuspec @@ -22,8 +22,8 @@ This is an SDK for Aws IoTCore. - - + +