By Jim Birchfield
The Java Cryptography Extension (JCE) is now a core part of Java SDK 1.4. Basically, it's a set of packages that provide a framework and implementations for encryption, key generation and agreement, and Message Authentication Code (MAC) algorithms. This article will explore the installation and utilization of JCE.
Note that although the JCE is now a core package in Java SDK 1.4, we'll demonstrate how to configure it using Java SDK 1.2 or higher (static installation). Also, I'll cover how to use security providers with no setup at all (dynamic installation). Finally, I'll demonstrate how to create a key and a cipher and how to perform basic data encryption and decryption.
What is a provider?
A provider is the underlying implementation of a particular security mechanism. There are several providers, some of which are freely available and others that are quite costly. Companies that offer providers include IBM, Bouncy Castle, and RSA. Later in this series, we'll examine the RSA implementation from Bouncy Castle. Sun provides details on how to implement your own provider.
Before using or installing JCE, you must first obtain the library from the Sun Web site. JCE contains Sun’s own security provider, SunJCE. To statically add SunJCE to your list of default providers, you need to edit the security properties file:
- <java-home>\jre\lib\security\java.security (Win32)
- <java-home>/jre/lib/security/java.security (UNIX)
For instance, if you installed the JDK on a Windows machine in the folder C:\jdk1.3, you'd need to edit this file:
To install the SunJCE, you'd add the following line to the above file:
Substitute n with the priority for this provider.
Listing A demonstrates how you can view information about installed providers. The output displayed in Listing B shows information the provider supports, such as the available algorithms.
Listing C shows how to dynamically load a provider at runtime. It should be noted that when you invoke Security.addProvider(…), the provider is available to the entire JVM.
As stated previously, when you install a provider, you provide a number to indicate its priority. When an implementation is called for, the JVM will search through all installed security providers according to their priority and use the first provider it finds that implements the requested algorithm. You can also explicitly call for a given provider by including additional arguments to certain method calls, as we will see later.
The JCE API consists of numerous classes and interfaces for working with several kinds of algorithms and security features. In this first installment, we'll deal with using a popular symmetric algorithm, Data Encryption Standard (DES).
Listing D shows how to initialize a KeyGenerator and generate a key.
To generate a key, we must first get an instance of a KeyGenerator. This is accomplished by invoking the static method getInstance of the KeyGenerator class. We use a plain vanilla DES algorithm with no mode or padding scheme. Optionally, you could pass in something like this:
This provides a DES algorithm, with the Electronic Codebook (ECB) mode and PKCS#5 style padding. A second String parameter can be passed to specify provider implementation to use, but it is not necessary:
KeyGenerator kg = KeyGenerator.getInstance("DES");
Once we have our KeyGenerator, we invoke the generateKey method to get our key:
Key key = kg.generateKey();
Generating a cipher
We generate a cipher in much the same way that we generate a key. We must invoke the static method getInstance in the Cipher class. The parameters for this method work exactly as with KeyGenerator:
Cipher cipher = Cipher.getInstance(“DES”);
Listing E shows how to do this.
Encrypting and decrypting data
Encryption works at the byte level, so almost anything can be encrypted. Once you have a key and a cipher, you're ready to go. It should be noted that the same algorithm must be used for both the key and cipher. You cannot have a key initialized with DESede and a cipher initialized with DES. The Cipher object uses the same methods to encrypt and decrypt data, so you must initialize it first to let it know what you want done with the data:
This call initializes the Cipher object and gets it ready to encrypt data. The simplest way to encrypt data is invoking the doFinal method on the Cipher object passing in a byte array:
byte data = “Hello World!”.getBytes();
byte result = cipher.doFinal(data);
The result will now contain the encrypted representation of the passed-in data. It's just as easy to decrypt the same data. But before we can do that, we must reinitialize the Cipher object and get it ready for decryption:
Once we do this, we are ready to decrypt:
byte original = cipher.doFinal(result);
Now, original should now be identical to data. See Listing F for the full source code.
JCE is a powerful API, allowing numerous types of encryption, as well as several other security-related tasks. We've seen how to install the JCE both statically and dynamically, as well as use a symmetric encryption algorithm to encrypt and decrypt a simple message. In part two of this series, we'll take what we have learned in this article and apply it to a real-world scenario. I'll show you how to write a simple wrapper that can be used with sockets to encrypt all network traffic for your applications.