Skip to main content

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`);
});