Password Based Encryption (PBE) is a mechanism for protecting sensitive data using a symmetric cryptographic key derived from a password or passphrase. The use of a passphrase allows the data owner to use a self-selected, easy to remember secret expression instead of 32 random bytes (in the case of a 256 bit key). If coded improperly, even with the use of strong passwords, password encrypted data is easily cracked. Therefore, the use of a proven cryptographic library is essential.
Here, we’ll look at a simple example of PBE to encode plaintext data using a password of arbitrary length. The example is in C# and uses the .Net version of the BouncyCastle cryptographic library. However, there is also a Java version of BouncyCastle which allows for a similar solution.
Select Symmetric Encryption Algorithm
The first task is to select the symmetric encryption algorithm and parameters that the password-generated key will be applied to. Below, we select the Advanced Encryption Standard (AES), used by the US Government, with a key size of 256 bits.
static public readonly string ENCRYPTION_ALGORITHM = “AES”;
const int KEY_SIZE = 256;
static public readonly string DECRYPTION_ALGORITHM
When we ultimately build the ciphers that will be used for encryption and decryption, we’ll use a SHA256 hash and cipher-block chaining (CBC). This is important to specify because it will determine how the data will later be decrypted. Above,
DECRYPTION_ALGORITMH indicates 1) Password based encryption, 2) SHA256 hash, 3) AES with 256 bit key, 4) cipher-block chaining, and 5) the BouncyCastle implementation.
Build the Encryption Cipher
Now that a symmetric encryption algorithm has been selected, we need to build a BouncyCastle
IBufferedCipher instance based on the encryption parameters. We’ll assume the hashing algorithm, key size, and block chaining parameters are fixed. This leaves three more parameters that must be defined:
A 256-bit key can represent 2256 unique combinations. However, if a password is selected using only ASCII numbers and letters, the encrypted message can become susceptible to brute force attacks. For example, an 8 character password selected from only lowercase letters will provide less than 238 unique passwords. This may seem like a large number, but it is small even by the standards of an early 21st century desktop computer. Therefore, a lengthy passphrase derived from mixed case letters, numbers, and special characters is far more secure.
This is the number of times the hashing algorithm will be applied. The larger the number of iterations, the longer it will take to decrypt the data. This helps slow down the progress of a dictionary attack.
The salt is pure random data used to protected the encrypted data against dictionary and rainbow table attacks. A new, random salt should be used every time. This causes the same plaintext and password combination to generate different cipher text on subsequent encryptions.
private static IBufferedCipher BuildEncryptionCipher(
// get the password bytes
char passwordChars = password.ToCharArray();
// select the digest algorithm.
// if you change the digest algorithm, you must change DECRYPTION_ALGORITHM
IDigest digest = new Sha256Digest();
PbeParametersGenerator pbeParamGen = new Pkcs12ParametersGenerator(digest);
pbeParamGen.Init(passwordBytes, salt, iterations);
// 128-bit initialization vector
ParametersWithIV parameters = (ParametersWithIV)pbeParamGen.GenerateDerivedParameters(ENCRYPTION_ALGORITHM, KEY_SIZE, 128);
KeyParameter encKey = (KeyParameter)parameters.Parameters;
// we’ll use CBC and PKCS7Padding
= CipherUtilities.GetCipher(ENCRYPTION_ALGORITHM + “/CBC/PKCS7Padding”);
In the code above, the
string password is first converted to
char array. The
char array is then converted to a
byte array according to the PKCS #12 encoding scheme (unicode, big endian, and two zero pad bytes at the end).
Next, a SHA256 digester is created and handed to a PBE parameter generator which is initialized with the password, salt, and iterations.
The derived parameters (including the initialization vector) are generated from the selected encryption algorithm.
Finally, the cipher is built from the derived PBE parameters and configured to use CBC and PKCS #7 padding (to fill incomplete blocks). The
true value passed to the cipher indicates this is an encryption, rather than decryption, cipher.
Performing the Encryption
With the cipher created, encrypting the plaintext is as simple as wrapping a target stream with a BouncyCastle
private static byte Encrypt(IBufferedCipher cipher, string plainText)
using (MemoryStream writeStr = new MemoryStream())
using (CipherStream cstr = new CipherStream(writeStr, null, cipher))
byte data = Encoding.UTF8.GetBytes(plainText);
cstr.Write(data, 0, data.Length);
encrypted = writeStr.ToArray();
Be aware that the encryption process does not store the salt. If you were storing your ciphertext to a stream, you’d want to write out the salt first so it’s available to the decryption process. There’s no security risk in your attacker having access to the salt.
Making the Call
With the utility methods in place, you can begin encrypting plaintext as demonstrated below.
static void Main(string args)
// create the message that we want to encrypt, and the password to use
string plainText = “Hello, World!”;
string password = “abc123”;
// select hash iterations and build random salt to increase security
int iterations = 100;
byte salt = new byte[KEY_SIZE >> 3];
SecureRandom random = new SecureRandom();
// build the encryption cipher, then use it to encrypt our message
= BuildEncryptionCipher(password, iterations, salt);
byte encryptedText = Encrypt(encCipher, plainText);
for (int i = 0; i < encryptedText.Length; i++)
The code begins by selecting the plaintext and password. A reasonably safe (based on current computing power) iteration count of 100 is specified. Lastly, a random salt array is created with a length of one byte per bit in the key.
The cipher is built, the plaintext is encrypted, and the ciphertext is written out as a string of hex characters. Notice how, when this is executed multiple times, the output is very different due to the random salt.
Again, note that you’ll require the salt to decrypt the message. There’s no security risk if an attacker knows the salt that was used. It’s simplest to just write out the salt bytes to a stream just before the ciphertext.
How to Decipher
The code to decipher the ciphertext closely mirrors the encryption process: create a cipher, instantiate a
CipherStream, and write out the ciphertext. Rather than present the code here, you may download the source code and check it out for yourself.
The source project also contains a utility class called
PBEStream which allows for encryption and decryption using the standard .NET Stream API.