http://www.developer.com/

Back to article

Unlocking the Secrets of Java Cryptography Extensions: The Basics


October 15, 2008

In today's wired world, securing data from loss or theft is a top priority for companies that want to stay out of the headlines. Data encryption is a valuable tool for protecting a company's most valuable asset: customer data. Here, you will learn about cryptography and using the Java Cryptography Extension (JCE) to provide the security needed to protect that data.

What Is Cryptography?

Before getting started with Java cryptography, it is helpful to have a basic understanding of what cryptography is. At its most basic level, cryptography is a means of taking human-readable information, called plaintext, performing an encryption algorithm called a cipher on the plaintext, and producing encoded output, called ciphertext, that cannot easily be interpreted. Decryption is the reverse process of taking the ciphertext and converting it back into plaintext.

To do this, both participants in this process need to know what cipher algorithm is being used and must have access to a key, which is a unique value that is applied to the cipher to produce ciphertext or plaintext, depending on whether one is encrypting or decrypting. The key can be private, meaning that both parties agree on the key value ahead of time, or can derive it somehow by computation. This is known as key agreement.

A key also can be public, meaning that the sending party publishes the key along with the data. When a public key is used, the recipient of the data receives a private key that was also generated at encryption time separately and uses this value to decrypt the data.

A password is an example of a private key. The parties exchanging data agree on the password ahead of time, and the sender encrypts the data based on the password. When the receiver decrypts the data, he enters the password, which is then applied to the cipher to obtain the plaintext version. This is not particularly secure, as it is a fairly trivial exercise to crack a password cipher with enough computing power. However, to make password-based encryption more secure, a random number, known as a salt, is often introduced into the encryption scheme.

Other, more secure private key encryption schemes, such as the Advanced Encryption Standard (AES), Data Encryption Standard (DES), and Triple-DES are widely used. These are examples of block ciphers, and apply the cipher algorithm to a set number of bits of data at one time based on the private key. Other ciphers, such as RC4, encrypt and decrypt data in varying lengths at one time, and may change how they behave depending on key characteristics or characteristics of the plaintext. Because most data is larger than a single block, the encryption algorithm is repeated over and over again on subsequent blocks of data until all of the data is encrypted. The encryption algorithm may compute variations on the key value used to do the encryption depending on which block of data is being encrypted. These approaches are much more secure than the simple transformation mechanism used by a password encryption scheme.

Public key encryption algorithms, such as Diffie-Hellman and RSA, rely on the fact that the private key that is used to decrypt the data cannot be derived from the public key used to encrypt it. The sender encrypts the data using the public key and generates a mathematically related private key that is then sent to the recipient. The recipient uses the private key and the published public key to decrypt the data.

The most secure protocol for doing any sort of encryption that involves sending a private key to the data recipient is to encrypt the data using one encryption scheme and to encrypt the key using another. This adds another layer of security to the exchange of information, because one cannot decrypt the data without correctly decrypting the key first.

Besides simply encrypting and decrypting data, cryptography in the information age now includes message integrity checking, authenticating the identity of the sender/receiver of the message, and the use of digital signatures.

Installing the JCE

The JCE is referred to as an "extension" because it was only available as an extension to the normally supplied Java class libraries in JDK1.2 and 1.3. As of JDK1.4 and higher, however, the JCE is included with every JDK release. This includes a service-provider API, which allows outside vendors to provide new, pluggable encryption algorithms conforming to the Java Cryptography Architecture specification (JCA), as well as Sun's implementation of the JCA, referred to as the "Sun JCE." In the following code examples, you will examine the use of the Sun JCE, and also learn how to set up a third-party JCE provider's class library for use in your Java programs. The examples use the BouncyCastle JCE implementation, which is available at http://www.bouncycastle.org/.

Before following any of the installation instructions, be absolutely certain you know where the JDK/JRE you are currently running is located. You may have multiple instances of the JDK/JRE on your machine, or possibly both the JDK and JRE. If you update the wrong version, you will likely see this exception when running the examples:

java.lang.SecurityException:
   Unsupported keysize or algorithm parameters

By default, the JRE is restricted to particular encryption algorithms and limited encryption strength, or so-called "strong encryption." This restriction is put in place so that the JCE and applications that use it can be freely imported by other countries whose governments restrict the use of cryptography. These restrictions are not applicable in most countries, so the first step is to install the unlimited strength policy files. First, download the unrestricted policy files by going to http://java.sun.com/javase/downloads/index_jdk5.jsp and clicking the link next to "Cryptography Extension (JCE) Unlimited Strength Jurisdiction Policy Files." The download file is currently called jce_policy-1_5_0.zip.

After downloading the file, unzip it using the appropriate zip tool for your platform. The file contains a directory called /jce, which contains the files local_policy.jar and us_export_policy.jar. To install the files, go to the $java_home/jre/lib/security folder, back up the existing strong-encryption files, and copy the unrestricted policy files to this directory.

Next, to set up the third-party JCE provider, you must set up the java.security file and include the provider's class libraries in the system's classpath. To update the java.security file, go to $java_home/jre/lib/security, edit the java.security file, and add the provider. The third-party provider must be placed after all of the Sun providers to avoid problems. A sample is shown below:

security.provider.1=sun.security.provider.Sun
security.provider.2=com.sun.net.ssl.internal.ssl.Provider
security.provider.3=com.sun.rsajca.Provider
security.provider.4=com.sun.crypto.provider.SunJCE
security.provider.5=sun.security.jgss.SunProvider
security.provider.6=org.bouncycastle.jce.provider.BouncyCastleProvider

As you can see, entries 1-5 refer to the Sun JCE. The entry for the third-party software is entry 6, which refers to the provider's service provider class.

Now, to install the class libraries from BouncyCastle, download the complete class library suite, which is packaged in crypto-139.zip. After downloading and unzipping the file, you should see a directory called crypto-139, which contains all of the source code, signed .jar files, test classes, and other files provided by BouncyCastle for a complete build. Although you can build the appropriate .jar files yourself, it is more convenient to simply copy the ones provided from the /crypto-139/jars directory. Copy all of the .jar files for your particular version of the JDK—for example, files with "jdk15" in the name for JDK1.5—to your $java_home/jre/lib/ext directory, and you're done installing. This will install all of the features offered by BouncyCastle, including the JCE classes, certificate signing, Open PGP, and so forth.

A Simple Example

Now, you can begin looking at the basics with a very simple example program. This program will take plaintext, convert it to unintelligible ciphertext, and then convert it back again. The complete listing is shown in Listing 1.1:

package com.dlt.developer.crypto;

import javax.crypto.*;

/**
 * @author David Thurmond
 * A simple example of encrypting and decrypting
 * a text string using Data Encryption Standard encryption.
 */

public class SimpleEncryptExample {
   public static void main(String[] args) throws Exception {
      // First, create the encryption key...
      System.out.println("Generating key");
      KeyGenerator keygen =
         KeyGenerator.getInstance(""DES/ECB/PKCS5Padding");
      SecretKey desKey = keygen.generateKey();

      // Now, create the cipher object with appropriate parameters...
      System.out.println("Encrypting using DES/ECB/PKCS5Padding");
      Cipher desCipher = Cipher.getInstance("DES/ECB/PKCS5Padding");
      desCipher.init(Cipher.ENCRYPT_MODE, desKey);

      // Now, set up the plaintext to encrypt...
      byte[] plaintext = "The falcon flies at midnight".getBytes();
      System.out.println("The plaintext is \'" +
         new String(plaintext).toString() + "\'");

      // Now, do the encryption...
      byte[] ciphertext = desCipher.doFinal(plaintext);

      // Display the results...
      System.out.println("The ciphertext is \'" +
         new String(ciphertext).toString() + "\'");

      // Now, let's prove that the encryption worked by decrypting
      // using the same key...
      System.out.println("Decrypting the ciphertext");
      desCipher = Cipher.getInstance("DES/ECB/PKCS5Padding");

      // Note that we now initialize the cipher in decrypt mode...
      desCipher.init(Cipher.DECRYPT_MODE, desKey);

      // Now, decrypt the ciphertext and show the results...
      plaintext = desCipher.doFinal(ciphertext);
      System.out.println("The plaintext is \'" +
         new String(plaintext).toString() + "\'");
      }    // main


}    // SimpleEncryptExample

Listing 1.1: SimpleEncryptExample.java

The output generated from running this program is shown in Listing 1.2:

Generating key
Encrypting using DES/ECB/PKCS5Padding
The plaintext is 'The falcon flies at midnight'
The ciphertext is 'Im©¾ØLMŸm«L´ÞŒdÄf¾¥«ˆqJ"?'
Decrypting the ciphertext
The plaintext is 'The falcon flies at midnight'

Listing 1.2: Output of SimpleEncryptExample.java

Although the example is a simple one, it demonstrates a great deal of functionality. Now, examine each part of the program and understand what is going on.

First, a key is generated, which is necessary to encrypt the plaintext:

KeyGenerator keygen =
   KeyGenerator.getInstance("DES/ECB/PKCS5Padding");
SecretKey desKey = keygen.generateKey();

In the first line, a KeyGenerator object is specified by supplying the encryption algorithm that you will use later on when encrypting the data. This is necessary because different encryption algorithms require keys with differing characteristics, so, for example, a DES-compatible key is not suitable for encrypting data using the RSA encryption scheme.

Next, the cipher is created and initialized:

Cipher desCipher = Cipher.getInstance("DES/ECB/PKCS5Padding);
desCipher.init(Cipher.ENCRYPT_MODE, desKey);

In line 1 above, the cipher is initialized by specifying a string called a transform. The transform has the format <encryption algorithm>/<mode>/<padding scheme>. In this case, the encryption algorithm is DES, the mode is ECB (electronic codebook), and the padding scheme is PKCS5Padding. The cipher could also be created without supplying the mode and padding parameters, in which case appropriate default values will be assigned by the encryption provider, as shown below:

Cipher desCipher = Cipher.getInstance("DES");

A complete listing of the encryption algorithms, modes, and padding schemes available within the Sun JCE can be found in the Java Cryptography Extension Reference Guide, Appendix A, at http://java.sun.com/j2se/1.4.2/docs/guide/security/jce/JCERefGuide.html#AppA.

Next, the cipher is initialized by executing the following:

desCipher.init(Cipher.ENCRYPT_MODE, desKey);

Here, the cipher is initialized by providing a mode switch and the key generated earlier. The cipher mode refers to the operations of encrypting or decrypting plaintext. This will be explained more in a moment.

Next, the plaintext is created and encrypted:

byte[] plaintext = "The falcon flies at midnight".getBytes();
byte[] ciphertext = desCipher.doFinal(plaintext);

The encrypted data must be a byte array rather than a String. This is because encryption is usually performed on a much larger data set, such as a file, and is repeated over and over on smaller portions of the data, called blocks. In this example, there is only one single block of data, the plaintext's bytes.

Finally, the ciphertext is created by invoking the doFinal() method of the Cipher object on the plaintext, which returns a byte array of encrypted data.

Next, the decryption process is performed:

desCipher = Cipher.getInstance("DES/ECB/PKCS5Padding");
desCipher.init(Cipher.DECRYPT_MODE, desKey);
plaintext = desCipher.doFinal(ciphertext);

On the first line above, the Cipher object is re-created with the same encryption algorithm, mode, and padding scheme as before. Then, it is initialized in decryption mode by using the key created at the beginning of the program. Finally, the plaintext is regenerated by invoking doFinal() on the ciphertext. The doFinal() method returns the plaintext bytes because the Cipher object was initialized in decryption mode.

Using Third-Party Provider Cryptographic Libraries

The example above uses the Sun JCE. Using a third-party encryption library instead is almost as easy. Once you have completed the installation of the unrestricted policy files and the third-party libraries, invoking the encryption algorithms available to you requires only a few small code changes. Listing 2.1 shows SimpleEncryptProviderExample.java, which follows the same process as the earlier example, except that it uses the BouncyCastle libraries.

package com.dlt.developer.crypto;

import java.security.*;
import javax.crypto.*;
import org.bouncycastle.jce.provider.BouncyCastleProvider;

/**
 * @author David Thurmond:
 * A simple example of encrypting and decrypting a text
 * string using encryption from a provider library.
 */
public class SimpleEncryptProviderExample {
   public static void main(String[] args) throws Exception {
      System.out.println("Using BouncyCastle JCE API");
      // Add the BouncyCastleProvider to the system for later use...
      Security.addProvider(new BouncyCastleProvider());

      // First, create the encryption key...
      System.out.println("Generating key");
      KeyGenerator keygen = KeyGenerator.getInstance("DES", "BC");
      SecretKey desKey = keygen.generateKey();

      // Now, create the cipher object with appropriate parameters...
      System.out.println("Encrypting using Bouncy Castle DES");
      Cipher desCipher = Cipher.getInstance("DES", "BC");
      desCipher.init(Cipher.ENCRYPT_MODE, desKey);

      // Now, set up the plaintext to encrypt...
      byte[] plaintext = "The falcon flies at midnight".getBytes();
      System.out.println("The plaintext is \'" +
         new String(plaintext).toString() + "\'");

      // Now, do the encryption...
      byte[] ciphertext = desCipher.doFinal(plaintext);

      // Display the results...
      System.out.println("The ciphertext is \'" +
         new String(ciphertext).toString() + "\'");

      // Now, let's prove that the encryption worked by decrypting
      // using the same key...
      System.out.println("Decrypting the ciphertext");
      desCipher = Cipher.getInstance("DES/ECB/PKCS5Padding", "BC");

      // Note that we now initialize the cipher in decrypt mode...
      desCipher.init(Cipher.DECRYPT_MODE, desKey);

      // Now, decrypt the ciphertext and show the results...
      plaintext = desCipher.doFinal(ciphertext);
      System.out.println("The plaintext is \'" +
         new String(plaintext).toString() + "\'");

}    // main

}    // SimpleEncryptExample

Listing 2.1: SimpleEncryptProviderExample.java

The differences between the Sun JCE example and the provider example are shown below:

Security.addProvider(new BouncyCastleProvider());
...Generate encryption/decryption key...
KeyGenerator keygen = KeyGenerator.getInstance("DES", "BC");
...Encrypt text...
Cipher desCipher = Cipher.getInstance("DES", "BC");
desCipher.init(Cipher.ENCRYPT_MODE, desKey);
...Decrypt text...
desCipher = Cipher.getInstance("DES/ECB/PKCS5Padding", "BC");
desCipher.init(Cipher.DECRYPT_MODE, desKey);

Listing 2.2: Differences between SimpleEncryptExample.java and SimpleEncryptProviderExample.java

In line 1 above, the Bouncy Castle provider is initialized. Recall that this class corresponds to the entry configured in the java.security file set up earlier. This code tells Java how to resolve the provider name when asking for cryptography services later on.

The subsequent differences occur when requesting a cipher for encrypting and decrypting data. The call to Cipher.getInstance() takes the optional provider-name parameter "BC", which requests that the implementation of the cipher be created from the provider's libraries.

The remainder of the code is not provider-specific, so one can code using the JCE without being locked in to a particular provider's libraries. Key generation, encryption, and decryption are all done in the same way, regardless of whether the Sun JCE provider or a third-party provider is used.

A More Realistic Example

The examples above do a good job of getting across basic concepts, but a few important points are left out. First, the examples use the same key object to encrypt and decrypt the data. In real life, it is likely that the party who wishes to decrypt the data will receive the key separately, perhaps contained in a file, rather than being hard-coded within the program. Second, the simple examples only encrypt and decrypt a small amount of data, a single text string, rather than a more realistic example, such as a large data file containing sensitive information. Listings 3.1 and 3.2 show examples that demonstrate how to perform these tasks.

package com.dlt.developer.crypto;

import javax.crypto.*;
import java.io.*;

/**
 * @author David Thurmond
 * An example of encrypting a text file
 * using Data Encryption Standard encryption.
 */
public class EncryptFileExample {
   public static void main(String[] args) throws Exception {
      // First, create the encryption key...
      System.out.println("Generating key");

      KeyGenerator keygen = KeyGenerator.getInstance("DES");
      SecretKey desKey = keygen.generateKey();

      System.out.println("Writing key to file...");
      byte[] keyBytes = desKey.getEncoded();

      System.out.println("Writing bytes = " + keyBytes.length);
      BufferedOutputStream out =
         new BufferedOutputStream(new FileOutputStream
         ("encrypted_key.txt"));
      out.write(keyBytes);
      out.flush();
      out.close();


      // Now, create the cipher object with appropriate parameters...
      System.out.println("Encrypting file using DES/ECB/PKCS5Padding");
      Cipher desCipher = Cipher.getInstance("DES/ECB/PKCS5Padding");
      desCipher.init(Cipher.ENCRYPT_MODE, desKey);

      System.out.println("Reading cleartext file and encrypting...");
      BufferedOutputStream outData =
         new BufferedOutputStream(new FileOutputStream
         ("encrypted_data.txt"));
      BufferedInputStream in =
         new BufferedInputStream(new FileInputStream
         ("cleartext.txt"));
      while (in.available() > 0) {
         byte[] cleartextBytes = new byte[in.available()];
         in.read(cleartextBytes);
         // Now, encrypt them and write them to the encrypted file...
         byte[] encryptedBytes = desCipher.update(cleartextBytes);

         outData.write(encryptedBytes, 0, encryptedBytes.length);
      }    // while
      // Take care of any pending padding operations
      outData.write(desCipher.doFinal());
      in.close();
      outData.flush();
      outData.close();

      System.out.println("Done!");
   }    // main

}    // EncryptFileExample

Listing 3.1: EncryptFileExample

The preceding example code first creates an encryption key and saves it to a file called encrypted_key.txt for later retrieval by a decryption program. The line:

byte[] keyBytes = desKey.getEncoded();

grabs the raw data for the key so that it can be saved to the file. The party who wants to decrypt the data file must know what kind of key was saved to the file, in this case a DES key in ECB format with PKCS-5 padding, to properly read it and create the decryption key.

Next, the file cleartext.txt, which is the unencrypted data file, is read in chunks of data. As each chunk is read, a block of data is encrypted and written out to the encrypted file encrypted_data.txt. To encrypt the data, the line:

byte[] encryptedBytes = desCipher.update(cleartextBytes);

takes the cleartext bytes just read from the file and converts them to ciphertext. This is slightly different than the invocation of doFinal() in the earlier examples. The Cipher.update() method assumes that there will be more blocks of data to be encrypted, and does not take care of any final padding that must be done for the cipher's algorithm to be properly applied. This is addressed by the line:

outData.write(desCipher.doFinal());

This finishes doing any remaining encryption and pads the resulting ciphertext appropriately, so that the total number of bytes of ciphertext is correct for the DES algorithm with PKCS-5 padding. Without this final call, the deciphering program could not decrypt the last bit of data in the file.

Listing 3.2 shows the reverse process of grabbing the encrypted key from the file and decrypting the data file.

package com.dlt.developer.crypto;

import java.security.*;
import javax.crypto.*;
import javax.crypto.spec.*;

import javax.crypto.*;import java.security.Key;

import java.io.*;

/**
 * @author David Thurmond
 * An example of decrypting a text file using Data Encryption
 * Standard encryption.
 */
public class DecryptFileExample {
   public static void main(String[] args) throws Exception {

      // First, create the encryption key...
      System.out.println("Reading key from file...");
      BufferedInputStream in =
         new BufferedInputStream(new FileInputStream
         ("encrypted_key.txt"));
      byte[] keyBytes = new byte[in.available()];
      in.read(keyBytes);
      System.out.println("Bytes read=" + keyBytes.length);
      in.close();
      SecretKeySpec skeySpec = new SecretKeySpec(keyBytes, "DES");

      // Now, create the cipher object with appropriate parameters...
      System.out.println("Decrypting file using DES/ECB/PKCS5Padding");
      Cipher desCipher = Cipher.getInstance("DES/ECB/PKCS5Padding");
      desCipher.init(Cipher.DECRYPT_MODE, skeySpec);


      System.out.println("Reading encrypted file and decrypting...");
      BufferedOutputStream outData =
         new BufferedOutputStream(new FileOutputStream
         ("decrypted_data.txt"));
      BufferedInputStream inData =
         new BufferedInputStream(new FileInputStream
         ("encrypted_data.txt"));
      while (inData.available() > 0) {
         // Read the next chunk of bytes...
         byte[] encryptedBytes = new byte[inData.available()];
         inData.read(encryptedBytes);
         // Now, decrypt them and write them to the encrypted file...
         byte[] cleartextBytes = desCipher.update(encryptedBytes);
         outData.write(cleartextBytes, 0, cleartextBytes.length);
      }    // while
      outData.write(desCipher.doFinal());

      inData.close();
      outData.flush();
      outData.close();

      System.out.println("Done!");
   }    // main


}

Listing 3.2: DecryptFileExample.java

In Listing 3.2, the file encrypted_key.txt is read, and the key's raw bytes are retrieved and loaded into a key spec for use by the Cipher object:

FileInputStream("encrypted_key.txt"));
   byte[] keyBytes = new byte[in.available()];
...Create the cipher object with the right decription...
...parameters and key spec...
SecretKeySpec skeySpec = new SecretKeySpec(keyBytes, "DES");
   Cipher desCipher = Cipher.getInstance("DES/ECB/PKCS5Padding");
   desCipher.init(Cipher.DECRYPT_MODE, skeySpec);

The remainder of the process is just the reverse of the previous example. Data is read from encrypted_data.txt, is decrypted by using the Cipher.update() and Cipher.doFinal() methods just as before, and is written out to decrypted_data.txt. Note that without the final invocation of doFinal(), a chunk of data would be missing at the end of the decrypted file. By examining decrypted_data.txt and cleartext.txt, the input to the file encryption program, you will see that the files are identical.

Password-Based Encryption

Although password-based encryption is not considered to be as secure as the secret-key encryption method shown above, it is probably the most commonly used method of encrypting and decrypting data. Listing 4.1 shows how to encrypt a file using a predetermined password, "mybigsecret". The password algorithm in this example uses a predetermined source of randomness, called a salt, and an iteration counter to determine a block size for the encryption, to make cracking the password a bit more difficult. In a real-world example, the salt and iteration counter might be determined based on some pre-determined agreement between the encrypting and decrypting parties, rather than being a hard-coded value.

package com.dlt.developer.crypto;

import javax.crypto.*;import javax.crypto.spec.SecretKeySpec;

import javax.crypto.spec.*;
import java.io.*;

/**
 * @author David Thurmond
 * An example of encrypting a text file using password-based
 * encryption.
 */
public class EncryptFilePasswordExample {
   public static void main(String[] args) throws Exception {

      PBEKeySpec pbeKeySpec;
      PBEParameterSpec pbeParamSpec;
      SecretKeyFactory keyFac;

      // Salt
      byte[] salt = {(byte)0x9f, (byte)0x33, (byte)0x4e, (byte)0xfe,
         (byte)0xd4, (byte)0xee, (byte)0x12, (byte)0x54};

      // Iteration count
      int count = 17;

      // Create PBE parameter set
      pbeParamSpec = new PBEParameterSpec(salt, count);
      char[] password = {'m', 'y', 'b', 'i', 'g', 's', 'e', 'c',
                         'r', 'e', 't'};

      pbeKeySpec = new PBEKeySpec(password);
      keyFac = SecretKeyFactory.getInstance("PBEWithMD5AndDES");
      SecretKey pbeKey = keyFac.generateSecret(pbeKeySpec);

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

      // Initialize PBE Cipher with key and parameters
      pbeCipher.init(Cipher.ENCRYPT_MODE, pbeKey, pbeParamSpec);
      System.out.println("Encrypting file using
                          DES/ECB/PKCS5Padding");



      System.out.println("Reading cleartext file and encrypting...");
      BufferedOutputStream outData =
         new BufferedOutputStream(new FileOutputStream
         ("password_encrypted_data.txt"));
      BufferedInputStream in =
         new BufferedInputStream(new FileInputStream
         ("cleartext.txt"));
      while (in.available() > 0) {
         // Read the next chunk of bytes...
         byte[] cleartextBytes = new byte[in.available()];
            in.read(cleartextBytes);
            // Now, encrypt them and write them to the encrypted file...
            byte[] encryptedBytes = pbeCipher .update(cleartextBytes);
            outData.write(encryptedBytes, 0, encryptedBytes.length);
      }    // while
      // Take care of any pending padding operations
      outData.write(pbeCipher .doFinal());
      in.close();
      outData.flush();
      outData.close();

      System.out.println("Done!");
   }    // main



}

Listing 4.1: EncryptFilePasswordExample.java

The preceding example is very similar to the previous file encryption example, except for the creation of the key value. The password key is created by using the following snippet of code:

pbeParamSpec = new PBEParameterSpec(salt, count);
char[] password = {'m', 'y', 'b', 'i', 'g', 's', 'e', 'c', 'r',
                   'e', 't'};
pbeKeySpec = new PBEKeySpec(password);
keyFac = SecretKeyFactory.getInstance("PBEWithMD5AndDES");
SecretKey pbeKey = keyFac.generateSecret(pbeKeySpec);

In the first three lines, parameters for creating the cipher and password-based key, namely the salt and an iteration count for additional randomness, and of course the password, are specified. Next, a secret key is generated, just as before in the DES encryption examples. Finally, the creation of the password cipher to perform the encryption is done in the following lines:

Cipher pbeCipher = Cipher.getInstance("PBEWithMD5AndDES");
pbeCipher.init(Cipher.ENCRYPT_MODE, pbeKey, pbeParamSpec);

Note that the password cipher requires not only the key, but also the salt and iteration counter parameters for initialization.

The decryption program, shown in Listing 4.2, is very similar to the encryption example shown earlier. Note that the salt, iteration count, and password are hard-coded in the program, but could easily be entered by the user at the command line or in a dialog.

package com.dlt.developer.crypto;

import javax.crypto.*;
import java.security.spec.*;

import javax.crypto.spec.*;
import java.io.*;

/**
 * @author David Thurmond
 * An example of decrypting a text file using
 * password-based encryption.
 */
public class DecryptFilePasswordExample {
   public static void main(String[] args) throws Exception {

      PBEKeySpec pbeKeySpec;
      PBEParameterSpec pbeParamSpec;
      SecretKeyFactory keyFac;

      // Salt
      byte[] salt = {(byte) 0x9f, (byte) 0x33, (byte) 0x4e,
         (byte) 0xfe, (byte) 0xd4, (byte) 0xee, (byte) 0x12,
         (byte) 0x54};

      // Iteration count
      int count = 17;

      // Create PBE parameter set
      pbeParamSpec = new PBEParameterSpec(salt, count);
      char[] password = {'m', 'y', 'b', 'i', 'g', 's', 'e', 'c',
                         'r', 'e', 't'};

      pbeKeySpec = new PBEKeySpec(password);
      keyFac = SecretKeyFactory.getInstance("PBEWithMD5AndDES");
      SecretKey pbeKey = keyFac.generateSecret(pbeKeySpec);

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

      // Initialize PBE Cipher with key and parameters
      pbeCipher.init(Cipher.DECRYPT_MODE, pbeKey, pbeParamSpec);
      System.out.println("Encrypting file using DES/ECB/PKCS5Padding");

      System.out.println("Reading encrypted file and decrypting...");
      BufferedOutputStream outData =
         new BufferedOutputStream(new FileOutputStream
         ("password_decrypted_data.txt"));
      BufferedInputStream in =
         new BufferedInputStream(new FileInputStream
         ("password_encrypted_data.txt"));
      while (in.available() > 0) {
         // Read the next chunk of bytes...
         byte[] cleartextBytes = new byte[in.available()];
         in.read(cleartextBytes);
         // Now, encrypt them and write them to the encrypted file...
         byte[] encryptedBytes = pbeCipher.update(cleartextBytes);
         outData.write(encryptedBytes, 0, encryptedBytes.length);
      }    // while
      // Take care of any pending padding operations
      outData.write(pbeCipher.doFinal());
      in.close();
      outData.flush();
      outData.close();

      System.out.println("Done!");
   }    // main

}

Listing 4.2: DecriptFilePasswordExample.java

Above, the password cipher parameters and password key are initialized as in the encryption program, and the Cipher object is initialized in decryption mode with the key and parameters. The process takes the password_encrypted_data.txt file and decrypts it by using the Cipher object, writing the output to password_decrypted_data.txt. Note that, as before, the Cipher.update() and Cipher.doFinal() methods are used to encrypt and decrypt blocks of data from the input files.

Conclusion

Cryptography is the process of taking human-readable text and converting it to unreadable text and back again. Here, you have learned how to perform encryption using the Java Cryptography Extensions, and how to install and use third-party cryptography libraries. By using the techniques discussed here, you now have the tools to provide security for any enterprise's most important asset: its data.

Download the Code

You can download the code that accompanies this article here.

References

About the Author

David Thurmond is a Sun Certified Developer with over fifteen years of software development experience. He has worked in the agriculture, construction equipment, financial, home improvement, and logistics industries.

Sitemap | Contact Us

Thanks for your registration, follow us on our social networks to keep up-to-date