Verifying notification signatures

We use HMAC-SHA1 codes to authenticate notifications from our servers. To do so, the user needs to generate HMAC key first. It can be done via client panel. Please store the key securely as it’s displayed only once. In case it’s needed (for example key is lost or compromised), new key can be generated the same way, keep in mind that doing so will invalidate previously generated key. Once the key is generated, all notifications sent from BPG will include X-BPG-Signature header containing hash signature based on JSON payload and the key.

To verify a message, you need to generate signature yourself and compare it with signature provided in X-BPG-Signature header. Here's pseudocode to do that:

HMAC-SHA1(request.body, SECRET_KEY) == request.headers['X-BPG-Signature']

And here's example code in Java(with Spark framework) for verifying the signature:

import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
import javax.xml.bind.DatatypeConverter;
import java.util.Objects;

public class HmacVerifyingServer {

    private final static String HMAC_SHA1 = "HmacSHA1";

    private static String generateSignature(String message, String key) throws Exception {
        SecretKeySpec signingKey = new SecretKeySpec(key.getBytes(), HMAC_SHA1);
        Mac mac = Mac.getInstance(HMAC_SHA1);
        mac.init(signingKey);
        byte[] signatureBytes = mac.doFinal(message.getBytes());
        return toHexStringLowercased(signatureBytes);
    }

    private static boolean verify(String message, String key, String signature) throws Exception {
        return Objects.equals(generateSignature(message, key), signature);
    }

    private static String toHexStringLowercased(byte[] bytes) {
        return DatatypeConverter.printHexBinary(bytes).toLowerCase();
    }

    public static void main(String[] args) {
        final String hmacKey = System.getenv("HMAC_KEY");
        spark.Spark.post("/", (req, res) -> {
            String signature = req.headers("X-BPG-Signature");
            String body = req.body();
            System.out.println("Signatures match: " + verify(body, hmacKey, signature));
            return "";
        });
    }
}