Signature Verification can be used to authenticate requests sent to your endpoint. At a high level, each request contains headers that you can use to validate that the request came from our system.
The following headers can be used to authenticate the request:
Our implementation uses digital signatures with the RSA with SHA algorithm; specifically SHA256 which for our purposes reduces the size of the signature without compromising its security.
The process for authenticating a request involves two parties - We (flexEngage) and You (Merchants with configured webhook endpoints) - and is explained in simple terms as follows:
You can find sample code to guide you on how to verify the request signature below. Additionally, we have a public git repository with full examples on how to do the signature verification, including tests and dependencies.
import org.bouncycastle.util.io.pem.PemObject;
import org.bouncycastle.util.io.pem.PemReader;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.StringReader;
import java.net.URL;
import java.nio.charset.StandardCharsets;
import java.security.InvalidKeyException;
import java.security.KeyFactory;
import java.security.NoSuchAlgorithmException;
import java.security.PublicKey;
import java.security.Signature;
import java.security.SignatureException;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.X509EncodedKeySpec;
import java.util.Base64;
public class SignatureVerifier {
public static boolean verify(
String payload,
String signature,
String pubKey,
String algorithm)
throws IOException,
NoSuchAlgorithmException,
InvalidKeySpecException,
InvalidKeyException,
SignatureException {
PublicKey publicKey = getPublicKeyFromString(pubKey);
Signature publicSignature = Signature.getInstance(algorithm);
publicSignature.initVerify(publicKey);
publicSignature.update(payload.getBytes(StandardCharsets.UTF_8));
byte[] signatureBytes = Base64.getDecoder().decode(signature);
return publicSignature.verify(signatureBytes);
}
private static PublicKey getPublicKeyFromString(String keyFileContents)
throws IOException, NoSuchAlgorithmException, InvalidKeySpecException {
StringReader reader = new StringReader(keyFileContents);
PemReader pemReader = new PemReader(reader);
PemObject pemObject = pemReader.readPemObject();
byte[] keyContentAsBytes = pemObject.getContent();
X509EncodedKeySpec spec = new X509EncodedKeySpec(keyContentAsBytes);
KeyFactory kf = KeyFactory.getInstance("RSA");
return kf.generatePublic(spec);
}
}
This requires installation of the PyCryptodome package.
PyCryptodome installation instructions
import base64
from Crypto.Hash import SHA256
from Crypto.PublicKey import RSA
from Crypto.Signature import pkcs1_15
def verify(payload, signature, pub_key):
msg = payload.encode()
hashed_payload = SHA256.new(msg)
public_key = RSA.import_key(pub_key)
signer = pkcs1_15.new(public_key)
try:
signer.verify(hashed_payload, base64.b64decode(signature))
return True
except (ValueError, TypeError):
return False