[SOLVED] Webhook signature verification on Java

Hello! I tried to reimplement signature verification snippet on java and faced with an issue:

    private static final char[] LOOKUP_TABLE_LOWER = new char[]{0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66};
        public void checkSignature(String signature, byte[] data) throws NoSuchAlgorithmException, WrongSignatureException {
        MessageDigest md = MessageDigest.getInstance("SHA3-256");
        md.update(data);
        md.update(StandardCharsets.UTF_8.encode(properties.getApiKey()));
        byte[] digest = md.digest();
        StringBuilder stringBuilder = new StringBuilder(digest.length * 2 + 2);
        stringBuilder.append("0x");
        for (byte b : digest) {
            stringBuilder.append(LOOKUP_TABLE_LOWER[((b >> 4) & 0xf)]);
            stringBuilder.append(LOOKUP_TABLE_LOWER[((b & 0xf))]);
        }
//        String expectedSignature =  HexFormat.of().formatHex(md.digest());
        String expectedSignature = stringBuilder.toString();
        if (!expectedSignature.equals(signature)) {
            log.error("Wrong signature: expected {}, but got {}.", expectedSignature, signature);
            throw new WrongSignatureException();
        }
    }

As reference the JS snippet below:

const verifySignature = (req, secret) => {

    const providedSignature = req.headers["x-signature"]
    if(!providedSignature) throw new Error("Signature not provided")
    const generatedSignature= web3.utils.sha3(JSON.stringify(req.body)+secret)
    if(generatedSignature !== providedSignature) throw new Error("Invalid Signature")

}

So, my question is JSON.stringify returns pure JSON string like ‘{“confirmed”:true,"chainI…’? Also, secret is a API key which looks like ‘jUftoI3…’?

There are 2 things that you have to take care when implementing that validation in another language:
1: you have to use the raw data that you received in the request and not to try to compute the json stringify format of the object as different languages have different json implementations
2: you have the use the sha3 that is used in eth and not the sha3 that is the standard one, the sha3 from eth is different because it was used before it existed a standard for sha3

here you also have an example of how to do the webhook validation in python

Thank you for the help! You are absolutely right! I always do this mistake: web3.sha3 is keccak-256 but not sha3-256.
I’ve replaced algorithm with KECCAK-256, so the Digest creation now looks so: MessageDigest.getInstance("KECCAK-256"); and everything works correctly. The note for the community: keccak-256 does not exist in Java by default, you need to add Bouncy Castle dependency.

1 Like