Professional Java Security : Symmetric Encryption Part 2
Password-Based Encryption (PBE)
The examples that we’ve given so far have used binary keys. These keys need to be stored in some fashion – on a hard drive or floppy disk for instance. Password-based encryption (PBE) uses a password as the key. This is convenient because the security then rests with the user rather than in a physical medium. Unfortunately, password-based encryption in general isn’t as secure as algorithms with binary keys like TripleDES or Blowfish. To demonstrate this, let’s say we use Blowfish with a key size of 128 bits. That gives us a keyspace of 2128. Password-based encryption typically uses ASCII characters. An average user’s password might be 6 characters in length. If it’s entirely lowercase, there are only 266 possible keys, or roughly 228. Adding capital characters and symbols helps quite a bit, but it cannot even approach the keyspace of a good symmetric algorithm.
To add to their insecurity, most passwords are simple everyday words. If an attacker were trying to crack a password, they would likely try every English word, which could be done rather quickly on a fast computer. This is known as a dictionary attack and is surprisingly successful. There are a number of tools out there that will automate a dictionary attack – some even test combinations of words, differing capitalization, and the use of numbers and some symbols. It’s quite interesting to try one of these tools out and see how secure a password is. A good password should be at least 8 characters long and use capitalization, numbers, and symbols, like “m1Nnes0+a”. It’s important that the password be simple enough to memorize, however, as you should never write a password down.
Password encryption uses a combination of hashing and normal symmetric encryption. The password is hashed using a message digest algorithm like SHA-1 (we’ll discuss this topic more in Chapter 6), and then the resulting hash is used to construct a binary key for an algorithm like Blowfish as shown in the following diagram:

As we mentioned, one of the problems with password-based encryption is that it is possible to create a precompiled list of passwords, hash them, and have a set of keys ready to try on encrypted data. This allows an attacker to try all normally used keys very quickly, and so determine if any of them decrypt the data. There are two techniques used to combat this attack: salting and iteration counts. A salt is a random value appended to the password before it is hashed to create a key, and the iteration count is the number of times the salt and password are hashed. Let’s examine each of these in some detail and see how they improve security.
Salt
By adding a random piece of data to the password before hashing it, we greatly increase the number of possible keys created from a single password. Without a salt, the word “sasquatch” hashes to a specific value. If we add 8 bytes of random data before hashing it, then “sasquatch” can hash to 264 possible values. That means that it is effectively impossible to create a precompiled-key list dictionary, as it would be far too large. Instead, the attacker is forced to perform a hashing computation rather than a simple lookup on the data, which will take quite some time.
The salt is stored with the data that is encrypted. Each time a new piece of data is encrypted, a new salt is generated. This means that the same phrase will encrypt to a different value each time it is encrypted, even if the same password is used every time. To decrypt, the salt must be extracted from the encrypted data, and then combined with the password to create the decryption key.
An example of the use of a salt is the password checking system in Linux. Typically passwords are MD5- hashed with a random salt. The salt is then prepended to the resulting hash. That salt and hash are then written to the password file. The first few characters are the salt and the remaining characters are the password hashed with the salt. When someone tries to log in, the operating system reads the password they type in and their entry in the password file. It then hashes the user’s typing along with the first characters of their password entry. If the resulting hash is equal to the remaining characters in their entry, their password is correct.
In just a moment we’ll show an example of using salt and password-based encryption that should help make this process clearer.
Iteration Count
The iteration count is an attempt to increase the time that an attacker will have to spend to test possible passwords. If we have an iteration count of a thousand, we need to hash the password a thousand times, which is a thousand times more computationally expensive than doing it just once. So now our attacker will have to spend 1000 times more computational resources to crack our password-based encryption.
Password-Based Encryption Example
Let’s write a simple class that does password-based encryption and decryption. We’ll use an iteration count of 1000, and a random 8-byte or 64-bit salt. When we write the ciphertext out, the first 64 bits will be the salt that we need to use to create the key to decrypt it. When we decrypt, we’ll use those 64 bits as the salt and we’ll only decrypt the ciphertext that begins after the 64th bit. Note that the salt isn’t being kept secret – it’s just different for each batch of text that we’re going to encrypt.
We want the output of the example to be displayable on the screen. To accomplish that, we’re going to BASE 64 encode the output, which transforms binary data into ASCII characters.
BASE64 Encoding
Binary data is typically stored in bytes of 8-bits. Standard ASCII is only 7 bits though, so if we want to display binary as ASCII, we’re going to lose at least one bit per byte. BASE64 encoding is a way of overcoming this problem. 8-bit bytes are converted to 6-bit chunks and then into characters. Six bits are used so that some control characters can be used indicating when the data ends. The encoded characters can then be displayed on the screen and converted back into binary with no difficulty. Of course, since we’re moving from an 8-bit chunk to a 6-bit chunk, we’re going to have more chunks – 3 bytes becomes 4 characters and vice-versa.
There is a BASE64 encoder and decoder in the sun.misc package. Since this is not included in a java.* package, its location could change in a future release of Java.
For that reason, we have provided a BASE64 encoder and decoder in Appendix C of this book.
We can use it as a drop-in replacement for the sun.misc implementation, by changing the import statement in PBE.java from:
1 | import sun.misc.*; |
to
1 | import com.isnetworks.base64.*; |
and include the BASE64 classes in the classpath.
Our code example will have two options: encryption and decryption. Encryption will require a password and some plaintext, and decryption a password and some encrypted data. We’ll create a salt for the encryption and prepend it to the ciphertext after it’s been encrypted, like so:
When decrypting, we’ll take that block of encrypted data and separate it into the salt and the ciphertext. Then we can use the password and the salt to initialize a cipher that can decrypt the ciphertext into the original plaintext message like this:
Password-based encryption in Java requires that we use two new classes in the javax.crypto.spec
package, PBEKeySpec
and PBEParameterSpec
, as well as javax.crypto.SecretKeyFactory
.
PBEKeySpec
We use PBEKeySpec
to create a key based on a password, using an instance of SecretKeyFactory
. This is one of the few classes in the JCE that we can actually call the standard constructor on. It takes a char array as an argument due to the fact that passwords are typically not stored as strings, since strings are immutable in Java, and there are times when you would like to be sure that a password is cleared out when you’re done using it. For more information, see Chapter 2 on writing secure code.
1 2 | char[] password = "sasquatch".toCharArray(); PBEKeySpec keySpec = new PBEKeySpec(password); |
SecretKeyFactory
In order to actually use the PBEKeySpec
as a key, we need to run it through a SecretKeyFactory
, which will generate the key given a key specification, by calling generateSecret()
.
We create an instance of SecretKeyFactory by calling getInstance() with the name of the algorithm we need. Here’s an example of creating a SecretKey
from a PBEKeySpec
:
1 2 3 | SecretKeyFactory keyFactory = SecretKeyFactory.getInstance("PBEWithSHAAndTwofish-CBC"); SecretKey theKey = keyFactory.generateSecret(keySpec); |
PBEParameterSpec
To create a cipher that uses PBE, we need to pass the salt and the iteration count to the cipher on instantiation. PBEParameterSpec
is a wrapper for that salt and iteration count. It is given to the cipher on initialization, along with the key. Assuming the salt and iterations are already defined, here’s how we would get a cipher, using PBEParameterSpec
:
1 2 3 | PBEParameterSpec paramSpec = new PBEParameterSpec(salt, iterations); Cipher cipher = Cipher.getInstance("PBEWithSHAAndTwofish-CBC"); cipher.init(Cipher.ENCRYPT_MODE, theKey, paramSpec); |
PBE Algorithm Names and Providers
You may have noticed the strange name for the algorithm that we used to initialize our key factory and cipher above, PBEWithSHAAndTwofish-CBC
. This specifies password-based encryption using SHA as a message digest and Twofish in CBC mode as the symmetric encryption algorithm. There are a great number of possible password-based encryption algorithms, including but not limited to:
1 2 3 4 5 | PBEWithMD5AndDES PBEWithSHAAndBlowfish PBEWithSHAAnd128BitRC4 PBEWithSHAAndIDEA-CBC PBEWithSHAAnd3-KeyTripleDES-CBC |
Most of the above algorithms are equivalently secure. The only possible exception is PBEWithMD5AndDES
. Since DES uses a 56-bit key, it’s possible that a password would be more secure than the DES key it creates. We recommend using one of the other algorithms if you have a choice in your application.
Unfortunately, no crypto provider supports all PBE algorithms and here we’ll use the Bouncy Castle provider to get support for PBEWithSHAAndTwofish-CBC
.
Now let’s take a look at our complete example of PBE. It uses SHA-1 and Twofish to encrypt or decrypt some text using a password. The iteration count is set to 1000 and the salt is randomly chosen on encryption. The salt is then written to the first 8 bytes of the output text.
PBE Example Code
When decrypting, this program will read the first 8 bytes of the ciphertext and use that as the salt:
1 2 3 4 5 6 7 8 9 10 11 12 | import java.security.*; import javax.crypto.*; import javax.crypto.spec.*; import java.util.*; // This is for BASE64 encoding and decoding import sun.misc.*; /** * PBE.java */ |
If you’re not using a Sun-based VM, comment out the import sun.misc.* line shown above, and uncomment the following line. This will switch to our BASE64 encoders:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 | // import com.isnetworks.base64.*; public class PBE { private static int ITERATIONS = 1000; private static void usage() { System.err.println("Usage: java PBE -e|-d password text"); System.exit(1); } public static void main (String[] args) throws Exception { if (args.length != 3) usage(); char[] password = args[1].toCharArray(); String text = args[2]; String output = null; // Check the first argument: are we encrypting or decrypting? if ("-e".equals(args[0])) output = encrypt(password, text); else if ("-d".equals(args[0])) output = decrypt(password, text); else usage(); System.out.println(output); } private static String encrypt(char[] password, String plaintext) throws Exception { // Begin by creating a random salt of 64 bits (8 bytes) byte[] salt = new byte[8]; Random random = new Random(); random.nextBytes(salt); // Create the PBEKeySpec with the given password PBEKeySpec keySpec = new PBEKeySpec(password); // Get a SecretKeyFactory for PBEWithSHAAndTwofish SecretKeyFactory keyFactory = SecretKeyFactory.getInstance("PBEWithSHAAndTwofish-CBC"); // Create our key SecretKey key = keyFactory.generateSecret(keySpec); // Now create a parameter spec for our salt and iterations PBEParameterSpec paramSpec = new PBEParameterSpec(salt, ITERATIONS); // Create a cipher and initialize it for encrypting Cipher cipher = Cipher.getInstance("PBEWithSHAAndTwofish-CBC"); cipher.init(Cipher.ENCRYPT_MODE, key, paramSpec); byte[] ciphertext = cipher.doFinal(plaintext.getBytes()); BASE64Encoder encoder = new BASE64Encoder(); String saltString = encoder.encode(salt); String ciphertextString = encoder.encode(ciphertext); return saltString+ciphertextString; } private static String decrypt(char[] password, String text) throws Exception { // Below we split the text into salt and text strings. String salt = text.substring(0,12); String ciphertext = text.substring(12,text.length()); // BASE64Decode the bytes for the salt and the ciphertext BASE64Decoder decoder = new BASE64Decoder(); byte[] saltArray = decoder.decodeBuffer(salt); byte[] ciphertextArray = decoder.decodeBuffer(ciphertext); // Create the PBEKeySpec with the given password PBEKeySpec keySpec = new PBEKeySpec(password); // Get a SecretKeyFactory for PBEWithSHAAndTwofish SecretKeyFactory keyFactory = SecretKeyFactory.getInstance("PBEWithSHAAndTwofish-CBC"); // Create our key SecretKey key = keyFactory.generateSecret(keySpec); // Now create a parameter spec for our salt and iterations PBEParameterSpec paramSpec = new PBEParameterSpec(saltArray, ITERATIONS); // Create a cipher and initialize it for encrypting Cipher cipher = Cipher.getInstance("PBEWithSHAAndTwofish-CBC"); cipher.init(Cipher.DECRYPT_MODE, key, paramSpec); // Perform the actual decryption byte[] plaintextArray = cipher.doFinal(ciphertextArray); return new String(plaintextArray); } } |
Running the Example
After compiling, we can run the example. For example, to encrypt we could type:
1 | C:\> java PBE –e sasquatch "Hello World!" |
This will encrypt the string “Hello World!” using PBE with the password of “sasquatch”. We should see output similar to the following:
1 | +xeivHEOb1M=AT/VYJlYIlJoQSMfKryaKw== |
We can decrypt the output with:
1 | C:\> java PBE –d sasquatch "+xeivHEOb1M=AT/VYJlYIlJoQSMfKryaKw==" |
and of course, we get our original message returned to us:
1 | Hello World! |
Now let’s look at a different method of encryption.
Professional Java Security : Symmetric Encryption Part 3