Why Password Based Encryption (PBE) is Needed
Cryptography protects data from being viewed or modified and provides a secure means of communication over otherwise insecure channels. In cryptography, Encryption is a process of converting data from plain text into a form called cipher text which makes the data cannot be easily understood by unauthorized parties. Decryption is other way process where the ciphertext text is converted back to the original plain text. Encryption is usually carried out using an encryption algorithm with the use of an encryption key which specifies how the ciphertext should be generated from the plain text. Decryption is carried out using the same algorithm with the use of the decryption key to transform the ciphertext back to its original plain text. In symmetric encryption both the encryption and decryption key are same whereas in case of asymmetric encryption both the encryption and decryption keys are different. This article mainly concerns about symmetric cryptography where both the keys are same.
It is more difficult to decrypt the ciphertext without having access to these encryption/decryption keys. The secrecy of communication also depends how well these keys are secured and managed. Successful key management is critical to the security of a cryptosystem. Generally the encryption/decryption keys are generated randomly using key-generation algorithms. Keys are usually a long random string bits and it can not be expected that someone will actually remember them, let alone enter them using an onscreen keyboard. Because of this keys must be managed in a safe and secure storage location. But on the other hand users are quite familiar with passwords. There by a way to generate strong cryptographic keys based on a humanly manageable passwords is required. Also, the key sizes varies for different encryption algorithms so we also need a way to generate different cryptographic random keys of desired sizes from the given password.
Not only with the encryption algorithms password based encryption can also be used along with message authentication (MAC) algorithms where the MAC generation operation produces a message authentication code from a message using a key, and the MAC verification operation verifies the message authentication code using the same key.
What is PBKDF ?
But with the passwords there is another problem. If a key is directly constructed from the passwords one can easily use pre-generated keys formed using an exhaustive list of passwords (called dictionary) for performing a brute force attack to crack the correct password. A standard way to derive an encryption key from a password is defined in PKCS#5 (Public Key Cryptography Standard) published by RSA (the company).
The standard strengthens the approach of generating cryptographic keys from passwords by using the following approaches.
1) Salt: Using a salt while generating the encryption keys protects them from getting cracked by dictionary attacks. By using random salt multiple encryption keys can be generated based on the same password which makes attacker to generate a new key table for each salt value, making pre-computed table attacks much harder. The salt is used along with the password to derive the key , unlike the password the salt need not to be kept secret. The purpose of salt is to make the dictionary attack much harder and it is often stored along with encrypted data. The standard recommends a salt length of at least 64 bits (8 characters). The salt needs to be generated using a pseudo random number generator (PRNG). It is also strongly recommended not to reuse the same salt value for multiple instances of encryption.
2) Iteration Count: Specified number of times the key derivation operation will be performed before returning the resulting encryption key. Iteration count makes the key derivation computation expensive when used larger iterative counts like 1000 or more. Increasing the iteration count deliberately slows down the process of getting from a password to an actual encryption/decryption key. In cryptography we usually call this technique as Key Stretching. The minimum recommended number of iterations is 1000.
PKCS#5 defines two key derivation functions named PBKDF1 and PBKDF2. PBKDF stands for password based key derivation function. PBKDF1 applies a hash function (MD5 or SHA-1) multiple times to the salt and password, feeding the output of each round to next one to produce the final output. The length of the final key is thus bound by the hash function output length (16 bytes for MD5, 20 bytes for SHA-1). PBKDF1 was originally designed for DES and its 16 or 20 byte output was enough to derive both a key (56 bits) and an initialization vector (64 bits) to encrypt in CBC mode. However, since this is not enough for algorithms with longer keys such as 3DES and AES, PBKDF1 shouldn't be used and is only left in the standard for backward compatibility reasons.
PBKDF2 doesn't suffer from the limitations of PBKDF1: it can produce keys of arbitrary length by generating as many blocks as needed to construct the key. To generate each block, a pseudo-random function is repeatedly applied to the concatenation of the password, salt and block index. The pseudo-random function is configurable, but in practice HMAC-SHA1/256/384/512 are used, with HMAC-SHA1 being the most common. Despite all these having a restrictive password policy further improves the security of this cryptosystem.
In general, PBKDF standard can be used in both “password secrecy” and “password integrity” modes. The password privacy mode generates a secret key for encryption and the password integrity mode generates a Message Authentication Code (MAC) key.
ColdFusion 11 & PBKDF2
ColdFusion 11 added a new function GeneratePBKDFKey to facilitate the functionality of deriving an encryption key from the given input string. Added function returns the encryption key of desired length by taking password,algorithm,salt and iterations as function arguments. Each encryption algorithm will have its own key sizes generate the key of desired size from the password using this function and afterwards use this key in coldfusion's encrypt and decrypt functions. The syntax of the function as below.
GeneratePBKDFKey(String algorithm, String inputString, String salt, int iterations, int keysize)Function Arguments:
algorithm
The
encryption algorithm used to generate the encryption key. Supported
algorithms are PBKDF2WithHmacSHA1, PBKDF2WithSHA1, PBKDF2WithSHA224,
PBKDF2WithSHA256,PBKDF2WithSHA384, PBKDF2WithSHA512
inputString
Specify the input string (password/pass-phrase) which will be used for deriving the encryption key.
salt
Random cryptographic salt. Recommended length is 64 bits (8 characters) and must be randomly generated using a pseudo random number generator.
iterations
Desired Number of Iterations to perform the cryptographic operation. The minimum recommended number of iterations is 1000.
keySize
Desired arbitrary key length size in bits.
Example:
I am just trying to put up a simple use case where we can use PBKDF and leverage ColdFusion at the same time. Many websites while creating the user account gathers some private information of a user like email address, phone numbers, address etc and stores them in their respective data store. But In any case if the underlying data store got compromised all the information would be leaked. One way would be to encrypt all the user personal information using a single encryption key and storing it in location different from data store. But again stealing that encryption key also compromises the user personal data. In this case we can use the user's login password to derive the encryption key and encrypt the user's personal data using the same.
In this example we will use AES 192 bit encryption for encrypting the user email address and the encryption key derived from the password will be fed to encryption process. While decrypting the data the same encryption key will be derived and fed to decryption to successfully get the data back. In real, it can be any piece of data or any file we want to encrypt, here i am just using it as email address. Before start encrypting/decrypting the data generate a salt for that user and store it in some data store. I have created a CFC component (PBECrypto.cfc) which does encryption and decryption of given data using the supplied password.
component { // Hardcoding the below settings create a constructor to accept these settings This.iterations = 2000; This.desiredKeyLength = 192; This.pbkdfAlgorithm = "PBKDF2WithHmacSHA1"; This.saltLength = 16;// 16 * 8 = 128 bit salt This.encryptionAlgorithm = "AES"; This.outputEncoding = "BASE64"; // Generate the encryption key from the given password // returns generated salt for storing and also returns the encryption key private string function generateEncryptionKey(required string password, required string salt) { if(Len(Trim(password)) != 0 && Len(Trim(salt)) != 0) { return generatePBKDFKey(This.pbkdfAlgorithm, Trim(password), Trim(salt), This.iterations, This.desiredKeyLength); } throw("Invalid Password or Salt"); } public string function generateRandomSalt() { var lowerAlphabets = "abcdefghijklmnopqrstuvwxyz"; var upperAlphabets = uCase(lowerAlphabets); var numbers = "0123456789"; var saltSpace = lowerAlphabets & upperAlphabets & numbers; var salt = ""; for(var i = 0; i < This.saltLength; i++) { salt = salt & saltSpace.charAt(RandRange(0, Len(saltSpace) - 1, "SHA1PRNG")); } return salt; } public string function encryptData(required string inputData, required string password, required string salt) { var encryptionKey = generateEncryptionKey(password, salt); return encrypt(inputData, encryptionKey, This.encryptionAlgorithm, This.outputEncoding); } public string function decryptData(required string encryptedData, required string password, required string salt) { // regenerate the encryption key to decrypt the data var decryptionKey = generateEncryptionKey(password, salt); return decrypt(encryptedData, decryptionKey, This.encryptionAlgorithm, This.outputEncoding); } }The following code snippet uses the PBECrypto.cfc to encrypt the given email address using the password received over a form. Before encrypting a salt must be generated for use with the PBKDF2.
<cfscript> crypto = new PBECrypto(); salt = crypto.generateRandomSalt(); // Add your own logic to store the salt specific to the user. // Also assuming password & email address are received over a form from the user encryptedEmailAddress = crypto.encryptData(form.emailAddress,form.password,salt); // Store the encrypted email address in the store. </cfscript>
Now any time we can decrypt the email address if user supplies the password. The below snippet does the same.
<cfscript> // get the encrypted mail address and salt from the data store crypto = new PBECrypto(); // Also assuming password & email address are received over a form from the user emailAddress = crypto.decryptData(encryptedEmailAddress, form.password,salt); </cfscript>
In this way PBKDF makes it possible to encrypt and decrypt without storing the encryption keys but by deriving them from a given input string (possibly we call password). Also use a sufficiently long randomly generated salt and high iteration count when deriving key from the password.
References:
http://csrc.nist.gov/publications/nistpubs/800-132/nist-sp800-132.pdf
http://tools.ietf.org/html/rfc2898
http://en.wikipedia.org/wiki/PBKDF2
References:
http://csrc.nist.gov/publications/nistpubs/800-132/nist-sp800-132.pdf
http://tools.ietf.org/html/rfc2898
http://en.wikipedia.org/wiki/PBKDF2