验证 SendGrid 的签名事件 Webhook?
Verifying SendGrid's Signed Event Webhook?
我正在尝试通过此 link 实施验证。
我不想要其中的 OAuth 部分,而是使用 Public 密钥和 headers 来验证数据。我看过很多与 SendGrid 的 Event Webhook 相关的帖子,但没有验证它。
这是我尝试过的方法,但显然有些地方是错误的。我真的不确定如何将代码从文档转换为 C#。我不确定请求中的每条数据应该去哪里。有人对此有任何见解吗?
public async Task<IHttpActionResult> EventsHook()
{
IEnumerable<string> signatureFromHeader = Request.Headers.GetValues("X-Twilio-Email-Event-Webhook-Signature");
byte[] timeStampBytes = Encoding.ASCII.GetBytes(Request.Headers.GetValues("X-Twilio-Email-Event-Webhook-Timestamp").First());
byte[] body = await Request.Content.ReadAsByteArrayAsync();
byte[] payloadHash = timeStampBytes.Concat(body).ToArray();
byte[] signatureByteArray = Convert.FromBase64String(signatureFromHeader.First());
var publicKey = "{myPublicKey}";
byte[] publicKeyBytes = Convert.FromBase64String(publicKey);
var ecdsaParams = new ECParameters
{
D = publicKeyBytes
};
using (var ecdsa = ECDsa.Create(ecdsaParams))
{
if (ecdsa.VerifyData(payloadHash, signatureByteArray, HashAlgorithmName.SHA256))
{
return Ok();
}
return StatusCode(System.Net.HttpStatusCode.Forbidden);
}
}
我刚刚实现了这个,我可以帮助你:
1- 我使用库 starkbank-ecdsa 来验证签名。
2- 我声明了这个接口
public interface IEmailWebHookRequestValidator
{
bool IsValidRequest(HttpRequest request);
}
3-这个接口的实现是这样的:
public class SendGridWebHookRequestValidator : IEmailWebHookRequestValidator
{
private const string SIGNATURE_HEADER = "X-Twilio-Email-Event-Webhook-Signature";
private const string TIMESTAMP_HEADER = "X-Twilio-Email-Event-Webhook-Timestamp";
private readonly string _sendGridWebHookPublicKey;
public SendGridWebHookRequestValidator(ISettingsProvider settingsProvider)
{
_sendGridWebHookPublicKey = settingsProvider.SendGridWebHookPublicKey //"public key that you can get from SendGrid website when you enable Event Webhook's signature";
}
public bool IsValidRequest(HttpRequest request)
{
request.Headers.TryGetValue(SIGNATURE_HEADER, out var signatureHeaderValues);
request.Headers.TryGetValue(TIMESTAMP_HEADER, out var timestampHeaderValues);
var signature = signatureHeaderValues.FirstOrDefault();
var timestamp = timestampHeaderValues.FirstOrDefault();
if (string.IsNullOrWhiteSpace(signature) || string.IsNullOrWhiteSpace(timestamp))
{
return false;
}
var body = ReadStream(request.Body);
return VerifySignature(_sendGridWebHookPublicKey, body, signature, timestamp);
}
public bool VerifySignature(string verificationKey, string payload, string signature, string timestamp)
{
var publicKey = PublicKey.fromPem(verificationKey);
var timestampedPayload = timestamp + payload;
var decodedSignature = Signature.fromBase64(signature);
return Ecdsa.verify(timestampedPayload, decodedSignature, publicKey);
}
public static string ReadStream(Stream stream)
{
using var ms = new MemoryStream();
stream.CopyTo(ms);
return Encoding.UTF8.GetString(ms.GetBuffer(), 0, (int)ms.Length);
}
}
ISettingsProvider 只是一个界面,可以帮助您从设置中检索 public 键。
当您的 webhook 接收 httprequest 时,只需将其传递给 IsValidRequest 方法即可轻松使用上述代码。
我正在尝试通过此 link 实施验证。
我不想要其中的 OAuth 部分,而是使用 Public 密钥和 headers 来验证数据。我看过很多与 SendGrid 的 Event Webhook 相关的帖子,但没有验证它。
这是我尝试过的方法,但显然有些地方是错误的。我真的不确定如何将代码从文档转换为 C#。我不确定请求中的每条数据应该去哪里。有人对此有任何见解吗?
public async Task<IHttpActionResult> EventsHook()
{
IEnumerable<string> signatureFromHeader = Request.Headers.GetValues("X-Twilio-Email-Event-Webhook-Signature");
byte[] timeStampBytes = Encoding.ASCII.GetBytes(Request.Headers.GetValues("X-Twilio-Email-Event-Webhook-Timestamp").First());
byte[] body = await Request.Content.ReadAsByteArrayAsync();
byte[] payloadHash = timeStampBytes.Concat(body).ToArray();
byte[] signatureByteArray = Convert.FromBase64String(signatureFromHeader.First());
var publicKey = "{myPublicKey}";
byte[] publicKeyBytes = Convert.FromBase64String(publicKey);
var ecdsaParams = new ECParameters
{
D = publicKeyBytes
};
using (var ecdsa = ECDsa.Create(ecdsaParams))
{
if (ecdsa.VerifyData(payloadHash, signatureByteArray, HashAlgorithmName.SHA256))
{
return Ok();
}
return StatusCode(System.Net.HttpStatusCode.Forbidden);
}
}
我刚刚实现了这个,我可以帮助你:
1- 我使用库 starkbank-ecdsa 来验证签名。 2- 我声明了这个接口
public interface IEmailWebHookRequestValidator
{
bool IsValidRequest(HttpRequest request);
}
3-这个接口的实现是这样的:
public class SendGridWebHookRequestValidator : IEmailWebHookRequestValidator
{
private const string SIGNATURE_HEADER = "X-Twilio-Email-Event-Webhook-Signature";
private const string TIMESTAMP_HEADER = "X-Twilio-Email-Event-Webhook-Timestamp";
private readonly string _sendGridWebHookPublicKey;
public SendGridWebHookRequestValidator(ISettingsProvider settingsProvider)
{
_sendGridWebHookPublicKey = settingsProvider.SendGridWebHookPublicKey //"public key that you can get from SendGrid website when you enable Event Webhook's signature";
}
public bool IsValidRequest(HttpRequest request)
{
request.Headers.TryGetValue(SIGNATURE_HEADER, out var signatureHeaderValues);
request.Headers.TryGetValue(TIMESTAMP_HEADER, out var timestampHeaderValues);
var signature = signatureHeaderValues.FirstOrDefault();
var timestamp = timestampHeaderValues.FirstOrDefault();
if (string.IsNullOrWhiteSpace(signature) || string.IsNullOrWhiteSpace(timestamp))
{
return false;
}
var body = ReadStream(request.Body);
return VerifySignature(_sendGridWebHookPublicKey, body, signature, timestamp);
}
public bool VerifySignature(string verificationKey, string payload, string signature, string timestamp)
{
var publicKey = PublicKey.fromPem(verificationKey);
var timestampedPayload = timestamp + payload;
var decodedSignature = Signature.fromBase64(signature);
return Ecdsa.verify(timestampedPayload, decodedSignature, publicKey);
}
public static string ReadStream(Stream stream)
{
using var ms = new MemoryStream();
stream.CopyTo(ms);
return Encoding.UTF8.GetString(ms.GetBuffer(), 0, (int)ms.Length);
}
}
ISettingsProvider 只是一个界面,可以帮助您从设置中检索 public 键。
当您的 webhook 接收 httprequest 时,只需将其传递给 IsValidRequest 方法即可轻松使用上述代码。