/// <summary> /// Returns the SHA256 HMAC of the given <paramref name="prefix"/> (if non-<see langword="null"/>) followed by /// the given <paramref name="request"/>'s body. /// </summary> /// <param name="request">The current <see cref="HttpRequest"/>.</param> /// <param name="secret">The key data used to initialize the <see cref="HMACSHA256"/>.</param> /// <param name="prefix">If non-<see langword="null"/>, additional <c>byte</c>s to include in the hash.</param> /// <returns> /// A <see cref="Task"/> that on completion provides a <see cref="byte"/> array containing the SHA256 HMAC of /// the <paramref name="prefix"/> followed by the <paramref name="request"/>'s body. /// </returns> protected virtual async Task <byte[]> GetRequestBodyHash_SHA256( HttpRequest request, byte[] secret, byte[] prefix) { await WebHookHttpRequestUtilities.PrepareRequestBody(request); using (var hasher = new HMACSHA256(secret)) { try { if (prefix != null && prefix.Length > 0) { hasher.TransformBlock( inputBuffer: prefix, inputOffset: 0, inputCount: prefix.Length, outputBuffer: null, outputOffset: 0); } var hash = hasher.ComputeHash(request.Body); return(hash); } finally { // Reset Position because JsonInputFormatter et cetera always start from current position. request.Body.Seek(0L, SeekOrigin.Begin); } } }
/// <summary> /// Reads the XML HTTP request entity body. /// </summary> /// <param name="context">The <see cref="ResourceExecutingContext"/>.</param> /// <returns> /// A <see cref="Task"/> that on completion provides a <see cref="XElement"/> containing the HTTP request /// entity body. /// </returns> protected virtual async Task <XElement> ReadAsXmlAsync(ResourceExecutingContext context) { if (context == null) { throw new ArgumentNullException(nameof(context)); } var request = context.HttpContext.Request; if (request.Body == null || !request.ContentLength.HasValue || request.ContentLength.Value == 0L || !HttpMethods.IsPost(request.Method) || !WebHookHttpRequestUtilities.IsXml(request)) { // Other filters will log and return errors about these conditions. return(null); } var modelState = context.ModelState; var actionContext = new ActionContext( context.HttpContext, context.RouteData, context.ActionDescriptor, modelState); var valueProviderFactories = context.ValueProviderFactories; var valueProvider = await CompositeValueProvider.CreateAsync(actionContext, valueProviderFactories); var bindingContext = DefaultModelBindingContext.CreateBindingContext( actionContext, valueProvider, _xElementMetadata, bindingInfo: null, modelName: ModelStateRootKey); // Read request body. await _bodyModelBinder.BindModelAsync(bindingContext); if (!bindingContext.ModelState.IsValid) { return(null); } if (!bindingContext.Result.IsModelSet) { throw new InvalidOperationException(Resources.VerifyOrganization_ModelBindingFailed); } // Success return((XElement)bindingContext.Result.Model); }
/// <summary> /// Returns the SHA1 HMAC of the given <paramref name="request"/>'s body. /// </summary> /// <param name="request">The current <see cref="HttpRequest"/>.</param> /// <param name="secret">The key data used to initialize the <see cref="HMACSHA1"/>.</param> /// <returns> /// A <see cref="Task"/> that on completion provides a <see cref="byte"/> array containing the SHA1 HMAC of the /// <paramref name="request"/>'s body. /// </returns> protected virtual async Task <byte[]> GetRequestBodyHash_SHA1(HttpRequest request, byte[] secret) { await WebHookHttpRequestUtilities.PrepareRequestBody(request); using (var hasher = new HMACSHA1(secret)) { try { var hash = hasher.ComputeHash(request.Body); return(hash); } finally { // Reset Position because JsonInputFormatter et cetera always start from current position. request.Body.Seek(0L, SeekOrigin.Begin); } } }
/// <inheritdoc /> public void OnResourceExecuting(ResourceExecutingContext context) { if (context == null) { throw new ArgumentNullException(nameof(context)); } var request = context.HttpContext.Request; switch (_requestMetadata.BodyType) { case WebHookBodyType.Form: if (!request.HasFormContentType) { context.Result = CreateUnsupportedMediaTypeResult(Resources.VerifyBody_NoFormData); } break; case WebHookBodyType.Json: if (!WebHookHttpRequestUtilities.IsJson(request)) { context.Result = CreateUnsupportedMediaTypeResult(Resources.VerifyBody_NoJson); } break; case WebHookBodyType.Xml: if (!WebHookHttpRequestUtilities.IsXml(request)) { context.Result = CreateUnsupportedMediaTypeResult(Resources.VerifyBody_NoXml); } break; default: var message = string.Format( CultureInfo.CurrentCulture, Resources.General_InvalidEnumValue, nameof(WebHookBodyType), _requestMetadata.BodyType); throw new InvalidOperationException(message); } }
/// <summary> /// Returns the SHA256 HMAC of the given <paramref name="prefix"/>, the given <paramref name="request"/>'s /// body, and the given <paramref name="suffix"/> (in that order). /// </summary> /// <param name="request">The current <see cref="HttpRequest"/>.</param> /// <param name="secret">The key data used to initialize the <see cref="HMACSHA256"/>.</param> /// <param name="prefix"> /// If non-<see langword="null"/> and non-empty, additional <c>byte</c>s to include in the hashed content /// before the <paramref name="request"/>'s body. /// </param> /// <param name="suffix"> /// If non-<see langword="null"/> and non-empty, additional <c>byte</c>s to include in the hashed content /// after the <paramref name="request"/>'s body. /// </param> /// <returns> /// A <see cref="Task"/> that on completion provides a <see cref="byte"/> array containing the SHA256 HMAC of /// the <paramref name="prefix"/>, the <paramref name="request"/>'s body, and the <paramref name="suffix"/> /// (in that order). /// </returns> protected virtual async Task <byte[]> ComputeRequestBodySha256HashAsync( HttpRequest request, byte[] secret, byte[] prefix, byte[] suffix) { if (request == null) { throw new ArgumentNullException(nameof(request)); } if (secret == null) { throw new ArgumentNullException(nameof(secret)); } if (secret.Length == 0) { throw new ArgumentException(Resources.General_ArgumentCannotBeNullOrEmpty); } await WebHookHttpRequestUtilities.PrepareRequestBody(request); using (var hasher = new HMACSHA256(secret)) { try { if (prefix != null && prefix.Length > 0) { hasher.TransformBlock( inputBuffer: prefix, inputOffset: 0, inputCount: prefix.Length, outputBuffer: null, outputOffset: 0); } // Split body into 4K chunks. var buffer = new byte[4096]; var inputStream = request.Body; int bytesRead; while ((bytesRead = await inputStream.ReadAsync(buffer, 0, buffer.Length)) > 0) { hasher.TransformBlock( buffer, inputOffset: 0, inputCount: bytesRead, outputBuffer: null, outputOffset: 0); } if (suffix != null && suffix.Length > 0) { hasher.TransformBlock( suffix, inputOffset: 0, inputCount: suffix.Length, outputBuffer: null, outputOffset: 0); } hasher.TransformFinalBlock(Array.Empty <byte>(), inputOffset: 0, inputCount: 0); return(hasher.Hash); } finally { // Reset Position because JsonInputFormatter et cetera always start from current position. request.Body.Seek(0L, SeekOrigin.Begin); } } }