diff --git a/examples/eventwebhook/RequestValidator.cs b/examples/eventwebhook/RequestValidator.cs
new file mode 100644
index 000000000..e849b8544
--- /dev/null
+++ b/examples/eventwebhook/RequestValidator.cs
@@ -0,0 +1,24 @@
+using Microsoft.AspNetCore.Http;
+using SendGrid.Helpers.EventWebhook;
+using System.IO;
+
+public bool IsValidSignature(HttpRequest request)
+{
+ var publicKey = "base64-encoded public key";
+ string requestBody;
+
+ using (var reader = new StreamReader(request.Body))
+ {
+ requestBody = reader.ReadToEnd();
+ }
+
+ var validator = new RequestValidator();
+ var ecPublicKey = validator.ConvertPublicKeyToECDSA(publicKey);
+
+ return validator.VerifySignature(
+ ecPublicKey,
+ requestBody,
+ request.Headers[RequestValidator.SIGNATURE_HEADER],
+ request.Headers[RequestValidator.TIMESTAMP_HEADER]
+ );
+}
diff --git a/src/SendGrid/Helpers/EventWebhook/RequestValidator.cs b/src/SendGrid/Helpers/EventWebhook/RequestValidator.cs
new file mode 100644
index 000000000..5b708eb5b
--- /dev/null
+++ b/src/SendGrid/Helpers/EventWebhook/RequestValidator.cs
@@ -0,0 +1,47 @@
+using EllipticCurve;
+
+namespace SendGrid.Helpers.EventWebhook
+{
+ ///
+ /// This class allows you to use the Event Webhook feature. Read the docs for
+ /// more details: https://sendgrid.com/docs/for-developers/tracking-events/event
+ ///
+ public class RequestValidator
+ {
+ ///
+ /// Signature verification HTTP header name for the signature being sent.
+ ///
+ public const string SIGNATURE_HEADER = "X-Twilio-Email-Event-Webhook-Signature";
+
+ ///
+ /// Timestamp HTTP header name for timestamp.
+ ///
+ public const string TIMESTAMP_HEADER = "X-Twilio-Email-Event-Webhook-Timestamp";
+
+ ///
+ /// Convert the public key string to a .
+ ///
+ /// verification key under Mail Settings
+ /// public key using the ECDSA algorithm
+ public PublicKey ConvertPublicKeyToECDSA(string publicKey)
+ {
+ return PublicKey.fromPem(publicKey);
+ }
+
+ ///
+ /// Verify signed event webhook requests.
+ ///
+ /// elliptic curve public key
+ /// event payload in the request body
+ /// value obtained from the 'X-Twilio-Email-Event-Webhook-Signature' header
+ /// value obtained from the 'X-Twilio-Email-Event-Webhook-Timestamp' header
+ /// true or false if signature is valid
+ public bool VerifySignature(PublicKey publicKey, string payload, string signature, string timestamp)
+ {
+ var timestampedPayload = timestamp + payload;
+ var decodedSignature = Signature.fromBase64(signature);
+
+ return Ecdsa.verify(timestampedPayload, decodedSignature, publicKey);
+ }
+ }
+}
diff --git a/tests/SendGrid.Tests/Helpers/EventWebhook/RequestValidatorTests.cs b/tests/SendGrid.Tests/Helpers/EventWebhook/RequestValidatorTests.cs
new file mode 100644
index 000000000..d05a94fe8
--- /dev/null
+++ b/tests/SendGrid.Tests/Helpers/EventWebhook/RequestValidatorTests.cs
@@ -0,0 +1,85 @@
+using Xunit;
+using SendGrid.Helpers.EventWebhook;
+
+namespace SendGrid.Tests.Helpers.EventWebhook
+{
+ public class RequestValidatorTests
+ {
+ private const string PUBLIC_KEY = "MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEEDr2LjtURuePQzplybdC+u4CwrqDqBaWjcMMsTbhdbcwHBcepxo7yAQGhHPTnlvFYPAZFceEu/1FwCM/QmGUhA==";
+ private const string PAYLOAD = "{\"category\":\"example_payload\",\"event\":\"test_event\",\"message_id\":\"message_id\"}";
+ private const string SIGNATURE = "MEUCIQCtIHJeH93Y+qpYeWrySphQgpNGNr/U+UyUlBkU6n7RAwIgJTz2C+8a8xonZGi6BpSzoQsbVRamr2nlxFDWYNH2j/0=";
+ private const string TIMESTAMP = "1588788367";
+
+ [Fact]
+ public void TestVerifySignature()
+ {
+ var isValidSignature = Verify(
+ PUBLIC_KEY,
+ PAYLOAD,
+ SIGNATURE,
+ TIMESTAMP
+ );
+
+ Assert.True(isValidSignature);
+ }
+
+ [Fact]
+ public void TestBadKey()
+ {
+ var isValidSignature = Verify(
+ "MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEqTxd43gyp8IOEto2LdIfjRQrIbsd4SXZkLW6jDutdhXSJCWHw8REntlo7aNDthvj+y7GjUuFDb/R1NGe1OPzpA==",
+ PAYLOAD,
+ SIGNATURE,
+ TIMESTAMP
+ );
+
+ Assert.False(isValidSignature);
+ }
+
+ [Fact]
+ public void TestBadPayload()
+ {
+ var isValidSignature = Verify(
+ PUBLIC_KEY,
+ "payload",
+ SIGNATURE,
+ TIMESTAMP
+ );
+
+ Assert.False(isValidSignature);
+ }
+
+ [Fact]
+ public void TestBadSignature()
+ {
+ var isValidSignature = Verify(
+ PUBLIC_KEY,
+ PAYLOAD,
+ "MEQCIB3bJQOarffIdM7+MEee+kYAdoViz6RUoScOASwMcXQxAiAcrus/j853JUlVm5qIRfbKBJwJq89znqOTedy3RetXLQ==",
+ TIMESTAMP
+ );
+
+ Assert.False(isValidSignature);
+ }
+
+ [Fact]
+ public void TestBadTimestamp()
+ {
+ var isValidSignature = Verify(
+ PUBLIC_KEY,
+ PAYLOAD,
+ SIGNATURE,
+ "timestamp"
+ );
+
+ Assert.False(isValidSignature);
+ }
+
+ private bool Verify(string publicKey, string payload, string signature, string timestamp)
+ {
+ var validator = new RequestValidator();
+ var ecPublicKey = validator.ConvertPublicKeyToECDSA(publicKey);
+ return validator.VerifySignature(ecPublicKey, payload, signature, timestamp);
+ }
+ }
+}