Using Password-Based Encryption

In this example, we prompt the user for a password from which we derive an encryption key.

It would seem logical to collect and store the password in an object of type java.lang.String. However, here's the caveat: Objects of type String are immutable, that is, there are no methods defined that allow you to change (overwrite) or zero out the contents of a String after usage. This feature makes String objects unsuitable for storing security sensitive information such as user passwords. You should always collect and store security sensitive information in a char array instead.

For that reason, the javax.crypto.spec.PBEKeySpec class takes (and returns) a password as a char array.

The following method is an example of how to collect a user password as a char array:

 /**
 * Reads user password from given input stream.
 */
 public char[] readPasswd(InputStream in) throws IOException {
 char[] lineBuffer;
 char[] buf;
 int i;

 buf = lineBuffer = new char[128];

 int room = buf.length;
 int offset = 0;
 int c;

loop: while (true) {
 switch (c = in.read()) {
 case -1: 
 case '\n':
 break loop;

 case '\r':
 int c2 = in.read();
 if ((c2 != '\n') && (c2 != -1)) {
 if (!(in instanceof PushbackInputStream)) {
 in = new PushbackInputStream(in);
 }
 ((PushbackInputStream)in).unread(c2);
 } else 
 break loop;

 default:
 if (--room < 0) {
 buf = new char[offset + 128];
 room = buf.length - offset - 1;
 System.arraycopy(lineBuffer, 0, buf, 0, offset);
 Arrays.fill(lineBuffer, ' ');
 lineBuffer = buf;
 }
 buf[offset++] = (char) c;
 break;
 }
 }

 if (offset == 0) {
 return null;
 }

 char[] ret = new char[offset];
 System.arraycopy(buf, 0, ret, 0, offset);
 Arrays.fill(buf, ' ');

 return ret;
 }

In order to use Password-Based Encryption (PBE) as defined in PKCS#5, we have to specify a salt and an iteration count. The same salt and iteration count that are used for encryption must be used for decryption:

 PBEKeySpec pbeKeySpec;
 PBEParameterSpec pbeParamSpec;
 SecretKeyFactory keyFac;

 // Salt
 byte[] salt = {
 (byte)0xc7, (byte)0x73, (byte)0x21, (byte)0x8c,
 (byte)0x7e, (byte)0xc8, (byte)0xee, (byte)0x99
 };

 // Iteration count
 int count = 20;

 // Create PBE parameter set
 pbeParamSpec = new PBEParameterSpec(salt, count);

 // Prompt user for encryption password.
 // Collect user password as char array (using the
 // "readPasswd" method from earlier code), and convert
 // it into a SecretKey object, using a PBE key
 // factory.
 System.out.print("Enter encryption password: ");
 System.out.flush();
 pbeKeySpec = new PBEKeySpec(readPasswd(System.in));
 keyFac = SecretKeyFactory.getInstance("PBEWithHmacSHA256AndAES_128
");
 SecretKey pbeKey = keyFac.generateSecret(pbeKeySpec);

 // Create PBE Cipher
 Cipher pbeCipher = Cipher.getInstance("PBEWithHmacSHA256AndAES_128
");

 // Initialize PBE Cipher with key and parameters
 pbeCipher.init(Cipher.ENCRYPT_MODE, pbeKey, pbeParamSpec);

 // Our cleartext
 byte[] cleartext = "This is another example".getBytes();

 // Encrypt the cleartext
 byte[] ciphertext = pbeCipher.doFinal(cleartext);