The High-Level Overview:
1. We generate an initialization vector (IV), which is in turn used to generate the salt.
2. We use the passphrase (client secret) and the salt to derive an encryption key.
3. We use the encryption key and the IV to generate an AES-256-CBC encryption of the request.
Now the nerdy deep dive:
Initialization Vector and Salt
The job of the IV and Salt is to add randomness, so that encrypting identical data does not produce identical results. The salt is used to add randomness to the password; the IV is used to add randomness to the encryption.
With that concept in hand… for PaymentsJS specifically, the IV is a byte array that contains sixteen random bytes. Most languages will have two ways of getting random data: a fast version and a secure version. The fast versions are in principle predictable, so it is important to use a cryptographically-secure random number generator and not just “Math.Random()”.
The salt is just the IV converted to a base64 string. In C#, and in Java, there may be more conversion involved: you take the byte array, convert it to a hex string, convert that to a UTF8 byte array, and then convert that to a base64 string.
Here it is in C#:
public static Nonces GetNonces()
byte nonceBytes = new byte;
using (RNGCryptoServiceProvider rng = new RNGCryptoServiceProvider())
var result = new Nonces();
result.IV = nonceBytes;
result.Salt = Convert.ToBase64String(UTF8Encoding.UTF8.GetBytes(BytesToHex(nonceBytes)));
private static string BytesToHex(byte ba)
StringBuilder hex = new StringBuilder(ba.Length * 2);
foreach (byte b in ba)
Here it is in PHP:
$iv = openssl_random_pseudo_bytes(16);
$salt = base64_encode(bin2hex($iv));
"iv" => $iv,
"salt" => $salt
The salt is passed to us as the nonce, and we use that to reverse-engineer the IV. It doesn’t matter that it’s publicly exposed, it’s not its job to be a secret – that’s the password’s job. Which is a good segue into:
The Encryption Key
When you’re encrypting something, you don’t just use whatever the passphrase is; instead, you take the passphrase, add the salt, and then hash that thousands of times.
This hash uses an algorithm called either “PBKDF2” or “RFC2898” (same thing); in pseudocode, it’s something like:
encryptionKey = PBKDF2(algorithm, passphrase, salt, iterations, keySize)
“Algorithm” is the hashing algorithm to use; for PaymentsJS, use SHA1.
“Passphrase” is the original, “human” password; for PaymentsJS, it is the client secret.
“Salt” we discussed above. It adds randomness.
“Iterations” is how many times to repeat this hashing process; for PaymentsJS, use 1500.
“Key Size” is how long, in bytes, you need the encryption key to be; for PaymentsJS, use 32.
Here it is in C#:
using (Rfc2898DeriveBytes pbkdf2 = new Rfc2898DeriveBytes(passphrase, UTF8Encoding.UTF8.GetBytes(salt), 1500))
aesAlg.Key = pbkdf2.GetBytes(32);
Here it is in PHP:
$encryptHash = hash_pbkdf2("sha1", $password, $salt, 1500, 32, true);
The Encryption Itself
Okay, now we have enough to do the encryption itself. We take all the payload data (amount, MID, environment, etc.) and convert it to a JSON string. Then we encrypt it using all the stuff we computed above, using the AES-256-CBC algorithm.
In pseudo-code, something like:
AES256CBC(stringToEncrypt, password, padding, IV)
The string to encrypt is our JSON payload.
The password is the hash that we derived using PBKDF2, our client secret, and the salt for randomness.
The “padding” setting for PaymentsJS, use “PKCS7”.
The IV is our random byte array, to add randomness to the encryption.
Back to the High-Level Overview
It’s worth revisiting the high level overview to repackage the wall of text above into a concise summary:
1. We generate a cryptographically-random 16 bytes as our Initialization Vector (IV).
2. We convert the IV to a base64 string to get our salt.
3. The PBKDF2 algorithm produces our encryption key.
a. The client secret is our passphrase.
b. The salt adds randomness.
c. 1500 iterations.
d. 32 bytes long.
4. The AES-256-CBC algorithm produces our encrypted payload.
a. The payload, as JSON, is what we’re encrypting.
b. The encryption key is our secret.
c. PKCS7 is our padding setting.
d. The IV adds randomness.