Webhook Security
To vverify the authenticity and origin of requests, you can register a client key to the webhook endpoint. Ezypay will use this client key to digitally sign the payloads that are sent, ensuring integrity by verifying the associated signature of the webhook.
How do you register a client key?
You can register a client key via the following options:
- add new
clientKey
parameter in request body.
- set
updateSecurity
to true in request body. - add new
clientKey
parameter in request body.
How do you securely generate a client key?
Your client key should be a secure and random string of characters. Such random strings can be generated in many programming languages via purpose-built libraries. Alternatively, you can generate them on the command line. In a Unix-like environment (including OS X), you could use:
pwgen -s 40 1
ruby -rsecurerandom -e 'puts SecureRandom.hex(20)'
What do we do with your client key?
Ezypay computes a hash using the HMAC SHA-1 algorithm, passing in your client key and request payload as arguments. The result is converted to a hexadecimal string and included in a request header named X-Ezypay-Signature
.
How do you verify the signature?
To verify Ezypay's signature:
- Extract the signature from the
X-Ezypay-Signature
HTTP header. - Extract the payload from the HTTP request.
- Compute the hexadecimal HMAC SHA-1 hash using your client key and the request payload.
- Compare the resulting string with the signature extracted in step 1.
Code examples on computing HMAC SHA-1 hash from some common languages as follow.
import org.apache.commons.codec.digest.HmacUtils;
public class Example {
public String computeSignature(String clientKey, String payload) {
return HmacUtils.hmacSha1Hex(clientKey, payload);
}
}
using System.Security.Cryptography;
namespace Example {
public class Signature {
public string computeSignature(string clientKey, string payload) {
byte[] clientKeyBytes = System.Text.Encoding.UTF8.GetBytes(clientKey);
byte[] payloadBytes = System.Text.Encoding.UTF8.GetBytes(payload);
HMACSHA1 hmac = new HMACSHA1(clientKeyBytes);
byte[] hashValue = hmac.ComputeHash(payloadBytes);
return BitConverter.ToString(hashValue).ToLower().Replace("-", "");
}
}
}
function computeSignature(clientKey, payload) {
var crypto = require('crypto');
var hmac = crypto.createHmac('sha1', clientKey).update(payload);
return hmac.digest('hex');
}
import hashlib
import hmac
def computeSignature(clientKey, payload):
utf8 = "utf-8"
clientKeyBytes = bytes(clientKey).encode(utf8)
payloadBytes = bytes(payload).encode(utf8)
computedHmac = hmac.new(clientKeyBytes, payloadBytes, digestmod=hashlib.sha1)
return computedHmac.digest().encode("hex")
<?php
function computeSignature($clientKey, $payload) {
return hash_hmac('sha1', $payload, $clientKey, false);
}
?>
require 'openssl'
def computeSignature(clientKey, payload)
digest = OpenSSL::Digest.new('sha1')
return OpenSSL::HMAC.hexdigest(digest, clientKey, payload)
end
use Digest::HMAC_SHA1;
sub compute_signature {
my ($clientKey, $payload) = @_;
my $hmac = Digest::HMAC_SHA1->new($clientKey);
$hmac->add($payload);
return $hmac->hexdigest;
}
What do we mean by "payload"?
The payload is the raw body of the HTTP request, meaning it is the JSON data in its raw form. Do not parse, re-encode, or manipulate it in any way; use it as is.
Can I test the signature verification process?
You can test the signature verification process using the following reference example:
- client key =
key
- payload =
{"requestId":"4cb74646-4d30-4d6a-ac71-7a26a4a623d3","merchantId":"aad25d2b-85f2-40cf-8b6d-44b5439a220f","eventType":"INVOICE_BATCH_CREATED","createdOn":"2024-07-11T06:42:28.296","data":{"id":"26055389-927b-41b9-bc13-77a50887db5c","batchReference":"tyj56","createdOn":"2024-07-11T06:42:28.135","status":"SUBMITTED"}}
The hexadecimal HMAC SHA-1 produces:
- output =
6354ecd501ca4c87da2b42872949c7fa02fefd89
What if you don't want to use a client key?
This security feature is optional. If you do not register a client key with a webhook, Ezypay will not compute the signature or include it as a header in the HTTP request.
Updated 5 months ago