Skip to content

Instantly share code, notes, and snippets.

@gaevoy
Last active April 26, 2018 10:18
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save gaevoy/ddf0478d17976cd4e492b0518dce69cd to your computer and use it in GitHub Desktop.
Save gaevoy/ddf0478d17976cd4e492b0518dce69cd to your computer and use it in GitHub Desktop.
How to verify webhook's signature
using System;
using System.IO;
using System.Text;
using Org.BouncyCastle.Crypto;
using Org.BouncyCastle.OpenSsl;
using Org.BouncyCastle.Security;
public static class WebhookSignature
{
public static bool Verify(string publicKey, string payload, string signature)
{
var key = (ICipherParameters)new PemReader(new StringReader(publicKey)).ReadObject();
var buffer = Encoding.UTF8.GetBytes(payload);
var signer = SignerUtilities.GetSigner("SHA256withRSA");
signer.Init(false, key);
signer.BlockUpdate(buffer, 0, buffer.Length);
return signer.VerifySignature(Convert.FromBase64String(signature));
}
}
using System;
using System.IO;
using System.Net;
using System.Threading.Tasks;
using Microsoft.Owin;
using Org.BouncyCastle.Crypto;
using Org.BouncyCastle.OpenSsl;
using Org.BouncyCastle.Security;
public class WebhookVerifierMiddleware : OwinMiddleware
{
private readonly string _url;
private readonly string _publicKey;
public WebhookVerifierMiddleware(OwinMiddleware next, string url, string publicKey) : base(next)
{
_url = url;
_publicKey = publicKey;
}
public override async Task Invoke(IOwinContext ctx)
{
var req = ctx.Request;
if (req.Path.Value.StartsWith(_url, StringComparison.OrdinalIgnoreCase))
{
var body = new MemoryStream();
await req.Body.CopyToAsync(body);
body.Position = 0;
req.Body = body;
if (!VerifySignature(_publicKey, body.ToArray(), req.Headers["X-Signature"]))
{
ctx.Response.StatusCode = (int)HttpStatusCode.BadRequest;
return;
}
}
await Next.Invoke(ctx);
}
private static bool VerifySignature(string publicKey, byte[] payload, string signature)
{
try
{
var key = (ICipherParameters)new PemReader(new StringReader(publicKey)).ReadObject();
var signer = SignerUtilities.GetSigner("SHA256withRSA");
signer.Init(false, key);
signer.BlockUpdate(payload, 0, payload.Length);
return signer.VerifySignature(Convert.FromBase64String(signature));
}
catch
{
return false;
}
}
}
@gaevoy
Copy link
Author

gaevoy commented Apr 24, 2018

There are dependencies to BouncyCastle and Microsoft.Owin for WebhookVerifierMiddleware so as prerequisite you must install:

Install-Package BouncyCastle -Version 1.8.2
Install-Package Microsoft.Owin -Version 3.0.1 

In order to enable WebhookVerifierMiddleware change your Owin startup code to look like:

using Owin;

public class Startup
{
    public void Configuration(IAppBuilder app)
    {
        // Following line must be at the very beginning
        app.Use<WebhookVerifierMiddleware>("/api/my-webhook", @"-----BEGIN PUBLIC KEY-----
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA07STSDsOtrS91xffsZ/r
hDg3VuF2QR4QK9UCFDmYrruANATBotkSX8EyYuGew9XrGB9lcG/ucysGZFCdK3Qs
..................This.is.your.public.key.......................
zxDV01aCHCkzHzZDoMG3xJgrwKxex9a5Smi8qirck9ZdWmGsYoTjCDl3lvo9TtPg
3wIDAQAB
-----END PUBLIC KEY-----
");

        // Put the rest of middleware here
    }
}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment