RSA-SHA512 Webhook signature verification#
I. Purpose#
To ensure webhooks are authentic and untampered, verify the Signature header with Finix’s RSA public key and SHA512.
II. When verification is required#
After receiving a Finix callback, verify Signature to confirm the payload is genuine and unchanged.
III. Structure and rules#
Callbacks include Signature. Build the verification string from BODY + Timestamp and verify with Finix’s public key.
1
Signing algorithms
Digest: SHA512 (512-bit Secure Hash Algorithm)
Full algorithm name: SHA512withRSA
2
Build string to verify
String to verify:stringToVerify = sha512Hex(rawBodyJson) + timestamp
Body: raw callback JSON string (field order must not change).
Timestamp: value from the Timestamp header, in seconds.
URL: not included in the verification input.
{"status":"SUCCESS","orderId":"ABC123","amount":100}
09ec4d91b7dd1fce70320b09f3fd7e98cd66efc8f918bb56e98a37c6b179f7d24fc4e43c0a614f8b9e5e95e410af0c0fd9d5f40ad8e38b30a9ad512b48c9c0e7
Then the string to verify is:09ec4d91b7dd1fce70320b09f3fd7e98cd66efc8f918bb56e98a37c6b179f7d24fc4e43c0a614f8b9e5e95e410af0c0fd9d5f40ad8e38b30a9ad512b48c9c0e71699447297
3
Verify with public key
Verify using Finix’s RSA public key and SHA512withRSA.
Base64-decode Signature before verification.
A successful verification means the payload is authentic.
IV. Security notes#
Finix signs certain responses with its private key; verify with Finix’s public key.
Successful verification proves the payload came from Finix and was not modified.
This design protects against tampering, man-in-the-middle attacks, and forged callbacks.
V. Important notes#
The sequence of fields in the string to verify must be exact.
Body must be the exact raw JSON (no pretty-printing, reordered keys, or extra whitespace).
Base64-decode Signature before calling the verifier.
Read Timestamp from headers; it must match the value Finix used when signing.
VI. Code samples#
import java.nio.charset.StandardCharsets;
import java.security.*;
import java.security.spec.X509EncodedKeySpec;
import java.util.Base64;
public class CallbackVerifier {
public static void main(String[] args) throws Exception {
String body = "{\"status\":\"SUCCESS\",\"orderId\":\"ABC123\",\"amount\":100}";
String timestamp = "1699447297";
String signatureBase64 = "REQUEST_HEADER_SIGNATURE_VALUE";
String bodyHash = sha512Hex(body);
String stringToVerify = bodyHash + timestamp;
String publicKeyPem = "-----BEGIN PUBLIC KEY-----\nYOUR_FINIX_PUBLIC_KEY_HERE\n-----END PUBLIC KEY-----";
PublicKey publicKey = loadPublicKey(publicKeyPem);
boolean isValid = verifySignature(publicKey, stringToVerify, signatureBase64);
System.out.println("Signature valid: " + isValid);
}
public static String sha512Hex(String data) throws Exception {
MessageDigest md = MessageDigest.getInstance("SHA-512");
byte[] digest = md.digest(data.getBytes(StandardCharsets.UTF_8));
StringBuilder sb = new StringBuilder();
for (byte b : digest) sb.append(String.format("%02x", b));
return sb.toString();
}
public static PublicKey loadPublicKey(String pem) throws Exception {
pem = pem.replace("-----BEGIN PUBLIC KEY-----", "")
.replace("-----END PUBLIC KEY-----", "")
.replaceAll("\\s+", "");
byte[] decoded = Base64.getDecoder().decode(pem);
X509EncodedKeySpec spec = new X509EncodedKeySpec(decoded);
return KeyFactory.getInstance("RSA").generatePublic(spec);
}
public static boolean verifySignature(PublicKey publicKey, String data, String signatureBase64) throws Exception {
byte[] signatureBytes = Base64.getDecoder().decode(signatureBase64);
Signature verifier = Signature.getInstance("SHA512withRSA");
verifier.initVerify(publicKey);
verifier.update(data.getBytes(StandardCharsets.UTF_8));
return verifier.verify(signatureBytes);
}
}
Modified at 2026-04-07 17:05:11