Webhook Security

Branch uses AES with CBC mode and PKCS5 padding to encrypt sensitive data.

All sensitive data is prepended with a random initialization vector(IV) to avoid dictionary attacks.

Decryption mechanism needs to be implemented on the client side in order to read sensitive data.

Configuration

Branch provides the AES key in BASE64 format. Clients need to be sure that they have the key provided by Branch before implementing the solution on their end.

Implementation

Implementation can be summed up in two steps.

  1. Separate the initialization vector (iv) from the data
  2. Decrypt the data with iv and key

Java Example

public class DecryptionService {

    /**
     * Decrypts a user provided encrypted value with a user provided key
     *
     * @param base64EncryptedValue is the user provided base64 encrypted value
     * @param base64Key            is the base64 encoded key string
     * @return UTF8 encoded value string
     */
    public String decrypt(String base64EncryptedValue, String base64Key) {
        byte[] encryptedByteValue = Base64.getDecoder().decode(base64EncryptedValue);
        byte[] key = Base64.getDecoder().decode(base64Key);
        byte[] decryptedValue = AESUtil.decrypt(encryptedByteValue, key);
        return new String(decryptedValue, UTF_8);
    }

}

public class AESUtil {

    private final String ALGORITHM = "AES";
    private final String CIPHER_TRANSFORMATION = "AES/CBC/PKCS5Padding";

    @SneakyThrows
    public byte[] decrypt(byte[] value, byte[] key) {
        Cipher cipher = Cipher.getInstance(CIPHER_TRANSFORMATION);

        // Get the random iv from value
        byte[] iv = ArrayUtils.subarray(value, 0, cipher.getBlockSize());
        byte[] encryptedValue = ArrayUtils.subarray(value, cipher.getBlockSize(), value.length);
        IvParameterSpec ivParam = new IvParameterSpec(iv);
        cipher.init(Cipher.DECRYPT_MODE, new SecretKeySpec(key, ALGORITHM), ivParam);

        return cipher.doFinal(encryptedValue);
    }

}

NodeJS Example

import express from "express";
import crypto from "crypto";

const app = express();


app.post("/webhook", function(req, res) {

    const decryptCipher = (value, key) => {
        const iv = value.slice(0, 16);
        const data = value.slice(16, value.length);
        const decipher = crypto.createDecipheriv("aes-256-cbc", key, iv);
        return decipher.update(data, undefined, "utf8") + decipher.final("utf8");
    };

    const decrypt = (base64EncryptedValue, base64Key) => {
        const encryptedByteValue = Buffer.from(base64EncryptedValue, "base64");
        const key = Buffer.from(base64Key, "base64");
        return decryptCipher(encryptedByteValue, key);
    };

    const data = decrypt(req.body.data, `YOUR_BASE64AESKEY`);

});