Webhook signature verification

Webhooks are used to communicate events in the system as status changes or important notifications. The data contained in a Webhook call might sometimes hold sensitive data. To ensure that the Webhook originated from Magnius, all our Webhooks are digitally signed.

Below you find a detailed description of the process to verify if a Webhook call originated from Magnius:

Public key

To be able to verify Webhooks signature, you first need to download our public key:

Payground Payground public key
Live Live public key

Finding the signature

The signature can be found on the X-signature HTTP header, encoded to Base64.

Verifying the signature

To verify the signature, you need from the Webhook Request: the body, the X-signature header and the public key available above.

Code examples

C#

using System;
using System.Security.Cryptography;
using System.Security.Cryptography.X509Certificates;
using System.Text;

namespace ConsoleApp
{
    class Program
    {
        static void Main(string[] args)
        {
            var Body = @"TheRequestBodyGoesHere"; // the body from the Webhook request
            var Signature = "TheRequestXSignatureGoesHere"; // the X-Signature request header
            var PublicKeyPath = @"path/to/the/downloaded/key/webhookPublicKey.pem"; // the path to the key

            byte[] body = Encoding.UTF8.GetBytes(Body);
            byte[] signature = Convert.FromBase64String(Signature); // X-Signature is base64 encoded

            var cert = new X509Certificate2(PublicKeyPath);
            var rsaCryptoIPT = (RSA)cert.PublicKey.Key;
            if (rsaCryptoIPT.VerifyData(body, signature, HashAlgorithmName.SHA1, RSASignaturePadding.Pkcs1))
            {
                Console.WriteLine("Signature verified");
            }
            else
            {
                Console.WriteLine("ERROR: Signature not valid!");
            }
            Console.ReadLine();
        }
    }
}

NodeJS

const fs = require('fs'), path = require('path'), crypto = require('crypto'), verify = crypto.createVerify('SHA1');

let body = 'TheRequestBodyGoesHere'; // the body from the Webhook request
let signature = 'TheRequestXSignatureGoesHere'; // the X-Signature request header
let publicKeyPath = 'path/to/the/downloaded/key/webhookPublicKey.pem'; // the path to the key

let publicKey = fs.readFileSync(publicKeyPath); // read

verify.update(body); // sets the message to be verified

console.log(verify.verify(publicKey, signature, 'base64')); // test it

PHP

$body = 'TheRequestBodyGoesHere'; // the body from the Webhook request
$xsignature =  'TheRequestXSignatureGoesHere'; // the X-Signature request header
$publicKeyPath = 'path/to/the/downloaded/key/webhookPublicKey.pem'; // the path to the key

$base64_decoded_xsignature = base64_decode($xsignature); // X-Signature is base64 encoded
$publicKey = openssl_pkey_get_public(file_get_contents($publicKeyPath));

echo(openssl_verify($body, $base64_decoded_xsignature, $publicKey)); // test it

Python 3

import base64
from OpenSSL import crypto

public_key_path = 'path/to/the/downloaded/key/webhookPublicKey.pem' # the path to the key

def verify(message, signature, public_key_path):
	try:
		key = crypto.load_certificate(crypto.FILETYPE_PEM, open(key_path, 'rb').read())
		crypto.verify(key, base64.urlsafe_b64decode(xsignature), message, 'sha1') # when successful, this call returns None
		return True
	except: # when unsuccessful, python raises an Error
		return False

body = 'TheRequestBodyGoesHere' # the body from the Webhook request
xsignature = 'TheRequestXSignatureGoesHere' # the X-Signature from the Webhook request

print(verify(body, xsignature, public_key_path)) # test it

Ruby

require 'openssl'
require 'base64'

public_key_path = 'path/to/the/downloaded/key/webhookPublicKey.pem' # the path to the key
body = 'TheRequestBodyGoesHere' # the body from the Webhook request
signature = 'TheRequestXSignatureGoesHere' # the X-Signature request header

key = OpenSSL::X509::Certificate.new File.read public_key_path # creates the certificate object from path

print(key.public_key.verify(OpenSSL::Digest::SHA1.new, Base64.decode64(signature), body)) # test it