/// <summary> /// Verifies that the signature header matches that of the actual body. /// </summary> protected virtual async Task <bool> VerifySignature(string id, HttpRequestMessage request) { string secretKey = await GetReceiverConfig(request, Name, id, SecretMinLength, SecretMaxLength); // Get the expected hash from the signature header string signatureHeaderValue = GetRequestHeader(request, SignatureHeaderName); byte[] expectedHash; try { expectedHash = EncodingUtilities.FromHex(signatureHeaderValue); } catch (Exception ex) { string msg = string.Format(CultureInfo.CurrentCulture, InstagramReceiverResources.Receiver_BadHeaderEncoding, SignatureHeaderName); request.GetConfiguration().DependencyResolver.GetLogger().Error(msg, ex); HttpResponseMessage invalidEncoding = request.CreateErrorResponse(HttpStatusCode.BadRequest, msg); throw new HttpResponseException(invalidEncoding); } // Get the actual hash of the request body concatenated with the request URI byte[] actualHash; byte[] secret = Encoding.UTF8.GetBytes(secretKey); using (var hasher = new HMACSHA1(secret)) { byte[] data = await request.Content.ReadAsByteArrayAsync(); actualHash = hasher.ComputeHash(data); } // Now verify that the provided hash matches the expected hash. return(WebHookReceiver.SecretEqual(expectedHash, actualHash)); }
public DropboxWebHookReceiverTests() { byte[] secret = Encoding.UTF8.GetBytes(TestSecret); using (var hasher = new HMACSHA256(secret)) { byte[] data = Encoding.UTF8.GetBytes(TestContent); byte[] testHash = hasher.ComputeHash(data); _testSignature = EncodingUtilities.ToHex(testHash); } }
public TrelloWebHookReceiverTests() { byte[] secret = Encoding.UTF8.GetBytes(TestSecret); using (var hasher = new HMACSHA1(secret)) { byte[] data = Encoding.UTF8.GetBytes(TestContent); byte[] requestUri = Encoding.UTF8.GetBytes(TestAddress); byte[] combo = new byte[data.Length + requestUri.Length]; Buffer.BlockCopy(data, 0, combo, 0, data.Length); Buffer.BlockCopy(requestUri, 0, combo, data.Length, requestUri.Length); byte[] testHash = hasher.ComputeHash(combo); _signature = EncodingUtilities.ToBase64(testHash, uriSafe: false); } }
private static string GetSignatureHeader(string content) { var secret = Encoding.UTF8.GetBytes(TestSecret); using (var hasher = new HMACSHA256(secret)) { var fullContent = $"{TestTimestamp}.{content}"; var data = Encoding.UTF8.GetBytes(fullContent); var testHash = hasher.ComputeHash(data); var signature = EncodingUtilities.ToHex(testHash); return($" {StripeWebHookReceiver.TimestampKey}={TestTimestamp}, " + $"{StripeWebHookReceiver.SignatureKey}={signature} "); } }
/// <summary> /// Verifies that the signature header matches that of the actual body. /// </summary> protected virtual async Task VerifySignature(string id, HttpRequestMessage request) { var secretKey = await GetReceiverConfig(request, Name, id, SecretMinLength, SecretMaxLength); // Get the expected hash from the signature header var header = GetRequestHeader(request, SignatureHeaderName); var values = header.SplitAndTrim('='); if (values.Length != 2 || !string.Equals(values[0], SignatureHeaderKey, StringComparison.OrdinalIgnoreCase)) { var message = string.Format(CultureInfo.CurrentCulture, GitHubReceiverResources.Receiver_BadHeaderValue, SignatureHeaderName, SignatureHeaderKey, "<value>"); request.GetConfiguration().DependencyResolver.GetLogger().Error(message); var invalidHeader = request.CreateErrorResponse(HttpStatusCode.BadRequest, message); throw new HttpResponseException(invalidHeader); } byte[] expectedHash; try { expectedHash = EncodingUtilities.FromHex(values[1]); } catch (Exception ex) { var message = string.Format(CultureInfo.CurrentCulture, GitHubReceiverResources.Receiver_BadHeaderEncoding, SignatureHeaderName); request.GetConfiguration().DependencyResolver.GetLogger().Error(message, ex); var invalidEncoding = request.CreateErrorResponse(HttpStatusCode.BadRequest, message); throw new HttpResponseException(invalidEncoding); } // Get the actual hash of the request body byte[] actualHash; var secret = Encoding.UTF8.GetBytes(secretKey); using (var hasher = new HMACSHA1(secret)) { var data = await request.Content.ReadAsByteArrayAsync(); actualHash = hasher.ComputeHash(data); } // Now verify that the provided hash matches the expected hash. if (!WebHookReceiver.SecretEqual(expectedHash, actualHash)) { var badSignature = CreateBadSignatureResponse(request, SignatureHeaderName); throw new HttpResponseException(badSignature); } }
/// <summary> /// Verifies that the signature header matches that of the actual body. /// </summary> protected virtual async Task <bool> VerifySignature(string id, HttpRequestMessage request) { // Get the expected hash from the signature and app key headers string signatureHeaderValue = GetRequestHeader(request, SignatureHeaderName); string keyHeaderValue = GetRequestHeader(request, KeyHeaderName); // Lookup which secret to use based on key header value IDictionary <string, string> lookupTable = await GetSecretLookupTable(id, request); string secretKey; if (!lookupTable.TryGetValue(keyHeaderValue, out secretKey)) { string msg = string.Format(CultureInfo.CurrentCulture, PusherReceiverResources.Receiver_SecretNotFound, KeyHeaderName, keyHeaderValue); request.GetConfiguration().DependencyResolver.GetLogger().Error(msg); HttpResponseMessage invalidEncoding = request.CreateErrorResponse(HttpStatusCode.BadRequest, msg); throw new HttpResponseException(invalidEncoding); } byte[] expectedHash; try { expectedHash = EncodingUtilities.FromHex(signatureHeaderValue); } catch (Exception ex) { string msg = string.Format(CultureInfo.CurrentCulture, PusherReceiverResources.Receiver_BadHeaderEncoding, SignatureHeaderName); request.GetConfiguration().DependencyResolver.GetLogger().Error(msg, ex); HttpResponseMessage invalidEncoding = request.CreateErrorResponse(HttpStatusCode.BadRequest, msg); throw new HttpResponseException(invalidEncoding); } // Get the actual hash of the request body byte[] actualHash; byte[] secret = Encoding.UTF8.GetBytes(secretKey); using (var hasher = new HMACSHA256(secret)) { byte[] data = await request.Content.ReadAsByteArrayAsync(); actualHash = hasher.ComputeHash(data); } // Now verify that the provided hash matches the expected hash. return(WebHookReceiver.SecretEqual(expectedHash, actualHash)); }
public async Task ReceiveAsync_ReturnError_IfPostHasInvalidSignature() { // Arrange Initialize(TestSecret); string invalid = EncodingUtilities.ToHex(Encoding.UTF8.GetBytes("你好世界")); _postRequest.Headers.Add(DropboxWebHookReceiver.SignatureHeaderName, invalid); // Act HttpResponseMessage actual = await ReceiverMock.Object.ReceiveAsync(TestId, RequestContext, _postRequest); // Assert HttpError error = await actual.Content.ReadAsAsync <HttpError>(); Assert.Equal("The WebHook signature provided by the 'X-Dropbox-Signature' header field does not match the value expected by the 'DropboxWebHookReceiverProxy' receiver. WebHook request is invalid.", error.Message); ReceiverMock.Protected() .Verify <Task <HttpResponseMessage> >("ExecuteWebHookAsync", Times.Never(), TestId, RequestContext, _postRequest, ItExpr.IsAny <IEnumerable <string> >(), ItExpr.IsAny <object>()); }
public async Task ReceiveAsync_Throws_IfPostHasInvalidSignature() { // Arrange Initialize(TestSecret); string invalid = EncodingUtilities.ToHex(Encoding.UTF8.GetBytes("invalid")); _postRequest.Headers.Add(CustomWebHookReceiver.SignatureHeaderName, "sha256=" + invalid); // Act HttpResponseException ex = await Assert.ThrowsAsync <HttpResponseException>(() => ReceiverMock.Object.ReceiveAsync(TestId, RequestContext, _postRequest)); // Assert HttpError error = await ex.Response.Content.ReadAsAsync <HttpError>(); Assert.Equal("The WebHook signature provided by the 'ms-signature' header field does not match the value expected by the 'CustomWebHookReceiverProxy' receiver. WebHook request is invalid.", error.Message); ReceiverMock.Protected() .Verify <Task <HttpResponseMessage> >("ExecuteWebHookAsync", Times.Never(), TestId, RequestContext, _postRequest, ItExpr.IsAny <IEnumerable <string> >(), ItExpr.IsAny <object>()); }
/// <summary> /// Gets the event data for this ID from the authenticated source so that we know that it is valid. /// </summary> protected virtual async Task <JObject> GetEventDataAsync(HttpRequestMessage request, string id, string notificationId) { if (request == null) { throw new ArgumentNullException("request"); } if (id == null) { throw new ArgumentNullException("id"); } if (notificationId == null) { throw new ArgumentNullException("notificationId"); } // Create HTTP request for requesting authoritative event data from Stripe string secretKey = await GetReceiverConfig(request, Name, id, SecretMinLength, SecretMaxLength); string address = string.Format(CultureInfo.InvariantCulture, EventUriTemplate, notificationId); HttpRequestMessage req = new HttpRequestMessage(HttpMethod.Get, address); byte[] challenge = Encoding.UTF8.GetBytes(secretKey + ":"); req.Headers.Authorization = new AuthenticationHeaderValue("Basic", EncodingUtilities.ToBase64(challenge, uriSafe: false)); using (HttpResponseMessage rsp = await _httpClient.SendAsync(req)) { if (!rsp.IsSuccessStatusCode) { string msg = string.Format(CultureInfo.CurrentCulture, StripeReceiverResources.Receiver_BadId, notificationId); request.GetConfiguration().DependencyResolver.GetLogger().Error(msg); HttpResponseMessage badId = request.CreateErrorResponse(HttpStatusCode.BadRequest, msg); throw new HttpResponseException(badId); } JObject result = await rsp.Content.ReadAsAsync <JObject>(); return(result); } }
/// <summary> /// Verifies that the signature header matches that of the actual body. /// </summary> protected virtual async Task <bool> VerifySignature(HttpRequestMessage request, string id) { var secretKey = await GetReceiverConfig(request, Name, id, SecretMinLength, SecretMaxLength); // Get the expected hash from the signature header var signatureHeaderValue = GetRequestHeader(request, SignatureHeaderName); byte[] expectedHash; try { expectedHash = EncodingUtilities.FromBase64(signatureHeaderValue); } catch (Exception ex) { var message = string.Format(CultureInfo.CurrentCulture, TrelloResources.Receiver_BadHeaderEncoding, SignatureHeaderName); request.GetConfiguration().DependencyResolver.GetLogger().Error(message, ex); var invalidEncoding = request.CreateErrorResponse(HttpStatusCode.BadRequest, message); throw new HttpResponseException(invalidEncoding); } // Get the actual hash of the request body concatenated with the request URI byte[] actualHash; var secret = Encoding.UTF8.GetBytes(secretKey); using (var hasher = new HMACSHA1(secret)) { var data = await request.Content.ReadAsByteArrayAsync(); var requestUri = Encoding.UTF8.GetBytes(request.RequestUri.AbsoluteUri); var combinedBytes = new byte[data.Length + requestUri.Length]; Buffer.BlockCopy(data, 0, combinedBytes, 0, data.Length); Buffer.BlockCopy(requestUri, 0, combinedBytes, data.Length, requestUri.Length); actualHash = hasher.ComputeHash(combinedBytes); } // Now verify that the provided hash matches the expected hash. return(WebHookReceiver.SecretEqual(expectedHash, actualHash)); }
/// <summary> /// Adds a SHA 256 signature to the <paramref name="body"/> and adds it to the <paramref name="request"/> as an /// HTTP header to the <see cref="HttpRequestMessage"/> along with the entity body. /// </summary> /// <param name="workItem">The current <see cref="WebHookWorkItem"/>.</param> /// <param name="request">The request to add the signature to.</param> /// <param name="body">The body to sign and add to the request.</param> protected virtual void SignWebHookRequest(WebHookWorkItem workItem, HttpRequestMessage request, JObject body) { if (workItem == null) { throw new ArgumentNullException(nameof(workItem)); } if (workItem.WebHook == null) { string msg = string.Format(CultureInfo.CurrentCulture, CustomResources.Sender_BadWorkItem, this.GetType().Name, "WebHook"); throw new ArgumentException(msg, "workItem"); } if (request == null) { throw new ArgumentNullException(nameof(request)); } if (body == null) { throw new ArgumentNullException(nameof(body)); } byte[] secret = Encoding.UTF8.GetBytes(workItem.WebHook.Secret); using (var hasher = new HMACSHA256(secret)) { string serializedBody = body.ToString(); request.Content = new StringContent(serializedBody, Encoding.UTF8, "application/json"); byte[] data = Encoding.UTF8.GetBytes(serializedBody); byte[] sha256 = hasher.ComputeHash(data); string headerValue = string.Format(CultureInfo.InvariantCulture, SignatureHeaderValueTemplate, EncodingUtilities.ToHex(sha256)); request.Headers.Add(SignatureHeaderName, headerValue); } }
/// <summary> /// Verifies that the signature header matches that of the actual body. /// </summary> protected virtual async Task <bool> VerifySignature(HttpRequestMessage request, string id) { // 1. Ensure configuration for this receiver and id exists. var secretKey = await GetReceiverConfig(request, Name, id, SecretMinLength, SecretMaxLength); // 2. Get the timestamp and expected signatures from the signature header. Header is a comma-separated set // of key=value pairs. var signatureHeaderValue = GetRequestHeader(request, SignatureHeaderName); var pairs = signatureHeaderValue.SplitAndTrim(','); string timestamp = null; var expectedSignatures = new List <string>(); foreach (var pair in pairs) { var keyValuePair = pair.SplitAndTrim('='); if (keyValuePair.Length != 2) { var message = string.Format( CultureInfo.CurrentCulture, StripeReceiverResources.Receiver_InvalidHeaderFormat, SignatureHeaderName); request.GetConfiguration().DependencyResolver.GetLogger().Error(message); var invalidHeader = request.CreateErrorResponse(HttpStatusCode.BadRequest, message); throw new HttpResponseException(invalidHeader); } if (string.Equals(keyValuePair[0], SignatureKey, StringComparison.OrdinalIgnoreCase)) { expectedSignatures.Add(keyValuePair[1]); } else if (string.IsNullOrEmpty(timestamp) && string.Equals(keyValuePair[0], TimestampKey, StringComparison.OrdinalIgnoreCase)) { timestamp = keyValuePair[1]; } } if (string.IsNullOrEmpty(timestamp) || expectedSignatures.Count == 0) { var message = string.Format( CultureInfo.CurrentCulture, StripeReceiverResources.Receiver_HeaderMissingValue, SignatureHeaderName, TimestampKey, SignatureKey); request.GetConfiguration().DependencyResolver.GetLogger().Error(message); var invalidHeader = request.CreateErrorResponse(HttpStatusCode.BadRequest, message); throw new HttpResponseException(invalidHeader); } // 3. Get the actual hash of the timestamp and request body. var secret = Encoding.UTF8.GetBytes(secretKey); var prefix = Encoding.UTF8.GetBytes(timestamp + "."); byte[] actualHash; using (var hasher = new HMACSHA256(secret)) { hasher.TransformBlock( inputBuffer: prefix, inputOffset: 0, inputCount: prefix.Length, outputBuffer: null, outputOffset: 0); var data = await request.Content.ReadAsByteArrayAsync(); actualHash = hasher.ComputeHash(data); } // 4. Verify that the actual hash matches one of the expected hashes. foreach (var expectedSignature in expectedSignatures) { // While this looks repetitious compared to hex-encoding actualHash (once), a single v1 entry in the // header is the normal case. Expect multiple signatures only when rolling secret keys. byte[] expectedHash; try { expectedHash = EncodingUtilities.FromHex(expectedSignature); } catch (Exception ex) { var message = string.Format( CultureInfo.CurrentCulture, StripeReceiverResources.Receiver_BadSignatureEncoding, SignatureHeaderName, SignatureKey); request.GetConfiguration().DependencyResolver.GetLogger().Error(message, ex); var invalidEncoding = request.CreateErrorResponse(HttpStatusCode.BadRequest, message); throw new HttpResponseException(invalidEncoding); } if (SecretEqual(expectedHash, actualHash)) { return(true); } } return(false); }
internal static void SignWebHookRequest(WebHook webHook, HttpRequestMessage request, JObject body) { byte[] secret = Encoding.UTF8.GetBytes(webHook.Secret); using (var hasher = new HMACSHA256(secret)) { string serializedBody = body.ToString(); request.Content = new StringContent(serializedBody, Encoding.UTF8, "application/json"); byte[] data = Encoding.UTF8.GetBytes(serializedBody); byte[] sha256 = hasher.ComputeHash(data); string headerValue = string.Format(CultureInfo.InvariantCulture, SignatureHeaderValueTemplate, EncodingUtilities.ToHex(sha256)); request.Headers.Add(SignatureHeaderName, headerValue); } }