Пример #1
0
        public void ShouldWrapHttpRequestMessage()
        {
            // Arrange
            HttpMethod          method      = HttpMethod.Post;
            Uri                 uri         = new Uri("http://www.example.website/resource.json");
            string              contentMd5  = Convert.ToBase64String(_md5Hash);
            const string        contentType = "application/json";
            NameValueCollection headers     = new NameValueCollection
            {
                { "X-Test-Header", "TestValue" },
                { "Date", "Tue, 15 Nov 1994 08:12:31 GMT" }
            };

            HttpRequestMessage request = new HttpRequestMessage(method, uri);

            request.Headers.Date = new DateTimeOffset(1994, 11, 15, 8, 12, 31, TimeSpan.Zero);
            request.Headers.Add(headers.Keys[0], headers[0]);
            request.Content = _httpContent;
            request.Content.Headers.ContentType = new MediaTypeHeaderValue(contentType);
            request.Content.Headers.ContentMD5  = _md5Hash;

            // Act
            HmacRequestWrapper wrapper = new HmacRequestWrapper(request);

            // Assert
            AssertWrapper(wrapper,
                          new DateTimeOffset(1994, 11, 15, 8, 12, 31, TimeSpan.Zero),
                          _httpContentStream,
                          headers,
                          method.Method,
                          uri,
                          contentMd5,
                          contentType);
        }
Пример #2
0
        /// <summary>
        /// Gets all required signature data, if found, from an HTTP request message.
        /// </summary>
        /// <param name="request">The request message to get the data from.</param>
        /// <returns>The extracted data as an <see cref="HmacSignatureData"/> object.</returns>
        /// <exception cref="ArgumentNullException">The request is null.</exception>
        /// <exception cref="HmacKeyRepositoryException">A problem occured when trying to retrieve a key based on the request.</exception>
        public virtual HmacSignatureData GetSignatureDataFromHttpRequest(HttpRequestMessage request)
        {
            if (request == null)
            {
                throw new ArgumentNullException(nameof(request), "The request cannot be null.");
            }

            HmacRequestWrapper requestWrapper = new HmacRequestWrapper(request);

            return(GetSignatureDataFromHttpRequest(requestWrapper));
        }
Пример #3
0
        /// <summary>
        /// Validates an entire HTTP request message.
        /// </summary>
        /// <param name="request">The HTTP request to validate.</param>
        /// <returns>The result of the validation as a <see cref="HmacValidationResult"/> object.</returns>
        /// <remarks>
        /// The following validation logic is used:
        /// - The Date header must be present if a maximum request age is configured, but cannot be older than the configured value;
        /// - The username header must be present when the user header name has been configured;
        /// - The key must be found for the request;
        /// - The Authorization header must be present, must have the correct authorization scheme and must contain a signature;
        /// - The signature created from the extracted signature data must match the one on the Authorization header.
        ///
        /// In case the request contains a body:
        /// - The Content-MD5 header value must match an MD5 hash of the body, if Content-MD5 validation was enabled in the configuration.
        /// </remarks>
        /// <exception cref="ArgumentNullException">The request is null.</exception>
        /// <exception cref="HmacConfigurationException">One or more of the configuration parameters are invalid.</exception>
        public virtual HmacValidationResult ValidateHttpRequest(HttpRequestMessage request)
        {
            if (request == null)
            {
                throw new ArgumentNullException(nameof(request), "The request cannot be null.");
            }

            HmacRequestWrapper requestWrapper = new HmacRequestWrapper(request);
            HmacSignatureData  signatureData  = HmacSigner.GetSignatureDataFromHttpRequest(request);

            return(ValidateHttpRequest(requestWrapper, signatureData));
        }
Пример #4
0
 private void AssertWrapper(HmacRequestWrapper wrapper, DateTimeOffset?date, Stream content, NameValueCollection headers, string method, Uri requestUri, string contentMd5, string contentType)
 {
     Assert.IsNotNull(wrapper.Date);
     Assert.AreEqual(date, wrapper.Date);
     Assert.IsNotNull(wrapper.Content);
     Assert.IsTrue(ReferenceEquals(content, wrapper.Content));
     Assert.IsNotNull(wrapper.Headers);
     Assert.AreEqual(headers.Count, wrapper.Headers.Count);
     Assert.IsTrue(headers.AllKeys.OrderBy(k => k).SequenceEqual(wrapper.Headers.AllKeys.OrderBy(k => k)));
     Assert.IsNotNull(wrapper.Method);
     Assert.AreEqual(method, wrapper.Method);
     Assert.IsNotNull(wrapper.RequestUri);
     Assert.AreEqual(requestUri.ToString(), wrapper.RequestUri.ToString());
     Assert.IsNotNull(wrapper.ContentMd5);
     Assert.AreEqual(contentMd5, wrapper.ContentMd5);
     Assert.IsNotNull(wrapper.ContentType);
     Assert.AreEqual(contentType, wrapper.ContentType);
 }
Пример #5
0
        public void ShouldWrapHttpRequestBase()
        {
            // Arrange
            string       contentMd5  = Convert.ToBase64String(_md5Hash);
            const string contentType = "application/json";

            NameValueCollection headers = new NameValueCollection
            {
                { "X-Test-Header", "TestValue" },
                { "Content-MD5", contentMd5 },
                { "Date", "Tue, 15 Nov 1994 08:12:31 GMT" }
            };
            const string method = "POST";
            Uri          uri    = new Uri("http://www.example.website/resource.json");

            Mock <HttpRequestBase> mockRequest = new Mock <HttpRequestBase>();

            mockRequest.Setup(r => r.InputStream).Returns(_bodyStream);
            mockRequest.Setup(r => r.Headers).Returns(headers);
            mockRequest.Setup(r => r.HttpMethod).Returns(method);
            mockRequest.Setup(r => r.Url).Returns(uri);
            mockRequest.Setup(r => r.ContentType).Returns(contentType);

            // Act
            HmacRequestWrapper wrapper = new HmacRequestWrapper(mockRequest.Object);

            // Assert
            AssertWrapper(wrapper,
                          new DateTimeOffset(1994, 11, 15, 8, 12, 31, TimeSpan.Zero),
                          _bodyStream,
                          headers,
                          method,
                          uri,
                          contentMd5,
                          contentType);
        }
Пример #6
0
        private HmacValidationResult ValidateHttpRequest(HmacRequestWrapper request, HmacSignatureData signatureData)
        {
            if (string.IsNullOrEmpty(HmacConfiguration.AuthorizationScheme))
            {
                throw new HmacConfigurationException("The AuthorizationScheme cannot be null or empty.");
            }

            // Note: the Content-MD5 and Content-Type headers are only required if the request contains a body

            // If configured, the request date is validated to prevent replay attacks
            if (HmacConfiguration.MaxRequestAge.HasValue)
            {
                if (!request.Date.HasValue)
                {
                    return(new HmacValidationResult(HmacValidationResultCode.DateMissing, "The request date was not found."));
                }
                if (!IsValidRequestDate(request.Date.Value))
                {
                    return(new HmacValidationResult(HmacValidationResultCode.DateInvalid, "The request date is invalid."));
                }
            }

            // The username is always required when the header has been configured
            if (!string.IsNullOrEmpty(HmacConfiguration.UserHeaderName) && string.IsNullOrEmpty(signatureData.Username))
            {
                return(new HmacValidationResult(HmacValidationResultCode.UsernameMissing, "The username is required but was not found."));
            }

            // The key must be found
            if (string.IsNullOrEmpty(signatureData.Key))
            {
                return(new HmacValidationResult(HmacValidationResultCode.KeyMissing, "The key was not found."));
            }

            // If configured, an MD5 hash of the body is generated and compared with the Content-MD5 header value to check if the body hasn't been altered
            if (HmacConfiguration.ValidateContentMd5 && !IsValidContentMd5(signatureData.ContentMd5, request.Content))
            {
                if (string.IsNullOrEmpty(signatureData.ContentMd5))
                {
                    return(new HmacValidationResult(HmacValidationResultCode.BodyHashMissing, "The MD5 body hash was not found."));
                }
                return(new HmacValidationResult(HmacValidationResultCode.BodyHashMismatch, "The body content differs."));
            }

            // The Authorization header is always required and should contain the scheme and signature

            IList <string> authorizations = request.Headers.GetValues(HmacConstants.AuthorizationHeaderName);
            string         authorization;

            if (authorizations == null || string.IsNullOrEmpty(authorization = authorizations.FirstOrDefault()))
            {
                return(new HmacValidationResult(HmacValidationResultCode.AuthorizationMissing, "The signature was not found."));
            }

            string[] authorizationParts = authorization.Split(' ');

            if (authorizationParts.Length < 2 || authorizationParts[0] != HmacConfiguration.AuthorizationScheme)
            {
                return(new HmacValidationResult(HmacValidationResultCode.AuthorizationInvalid, "The signature was not correctly specified."));
            }

            // Finally, the signature from the Authorization header should match the newly created signature

            string signature = authorizationParts[1];

            string newSignature = HmacSigner.CreateSignature(signatureData);

            if (!IsValidSignature(signature, newSignature))
            {
                return(new HmacValidationResult(HmacValidationResultCode.SignatureMismatch, "The signature does not match."));
            }

            return(HmacValidationResult.Ok);
        }
Пример #7
0
        private HmacSignatureData GetSignatureDataFromHttpRequest(HmacRequestWrapper request)
        {
            HmacSignatureData signatureData = new HmacSignatureData
            {
                HttpMethod = request.Method.ToUpperInvariant()
            };

            // Get the request URI if configured
            if (HmacConfiguration.SignRequestUri)
            {
                signatureData.RequestUri = request.RequestUri.AbsoluteUri;
            }

            // Get the request date if a maximum request age is configured
            if (HmacConfiguration.MaxRequestAge.HasValue && request.Date.HasValue)
            {
                DateTime date = request.Date.Value.UtcDateTime;
                signatureData.Date = date.ToString(HmacConstants.DateHeaderFormat, DateHeaderCulture);
            }

            // Get the content type and, if configured, the MD5 body hash
            signatureData.ContentType = request.ContentType;
            if (HmacConfiguration.ValidateContentMd5)
            {
                signatureData.ContentMd5 = request.ContentMd5;
            }

            // Get the username
            if (!string.IsNullOrEmpty(HmacConfiguration.UserHeaderName))
            {
                signatureData.Username = request.Headers[HmacConfiguration.UserHeaderName];
            }

            // Get the key
            try
            {
                signatureData.Key = HmacKeyRepository.GetHmacKeyForUsername(signatureData.Username);
            }
            catch (Exception ex)
            {
                throw new HmacKeyRepositoryException("Failed to retrieve the key.", ex);
            }

            // Add full additional headers
            if (HmacConfiguration.Headers != null && HmacConfiguration.Headers.Count > 0)
            {
                signatureData.Headers = new NameValueCollection();

                foreach (string headerName in HmacConfiguration.Headers.Distinct(StringComparer.OrdinalIgnoreCase))
                {
                    if (string.IsNullOrEmpty(headerName))
                    {
                        continue;
                    }

                    IList <string> headerValues = request.Headers.GetValues(headerName);
                    if (headerValues == null || headerValues.Count == 0)
                    {
                        continue;
                    }

                    foreach (string headerValue in headerValues)
                    {
                        signatureData.Headers.Add(headerName, headerValue);
                    }
                }
            }

            return(signatureData);
        }