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.
- Separate the initialization vector (iv) from the data.
- 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`);
});