Professional Java Security : Symmetric Encryption Part 1

Symmetric Encryption

This chapter we’re going to elaborate on the subject by describing how to use the JCE to perform symmetric encryption. To begin with, we will look at how we encrypt and decrypt using symmetric encryption. Then we discuss applications for symmetric encryption, followed by some examples, which deal with the following aspects of encryption:

Basic encryption
Encryption using Blowfish
Password-based encryption
The main topics discussed in this chapter are:

Key storage, including wrapping and unwrapping
Modes
Cipher streams and how to initialize ciphers with an initialization vector
Sealed objects
Remember, before trying to run any of the code in the chapter you’ll need an encryption engine installed. Here we’ll be using the Bouncy Castle provider because it has support for many algorithms, and won’t restrict us from using any other providers in the future (instructions for its installation are provided in the previous chapter).
Encryption and Decryption
Symmetric encryption, also known as secret-key encryption, is the simplest type of encryption. A single key is used, which must be kept secret, hence the name. In order to encrypt data, we take the key and use it to initialize a cipher. The cipher can then be used to encrypt data passed to it. Decryption is similar – a cipher gets initialized with the same key, and the data passed in is decrypted.

The strength of encryption is based on the length of the key. For symmetric encryption, that key length is typically between 40 and 128 bits, but some algorithms can have even longer keys. DES, which is probably the most commonly used symmetric algorithm, has a key length of 56 bits, which is really not enough for a secure system, as we discussed in Chapter 3. We should use at least 128 bit keys if we’re going to use symmetric encryption.

Applications

Symmetric encryption can be applied in quite a number of areas. It is much faster than asymmetric, or public-key encryption (sometimes by a factor of 1000) and for that reason it is recommended in situations where lots of data must be transformed. File encryption, network encryption, and database encryption are all good places to employ symmetric encryption. Its main weakness is the symmetry of the key. That same key must be used to encrypt and decrypt, so anyone with the ability to encrypt also has the ability to decrypt. If you are sending a symmetrically encrypted message, both sender and receiver must agree on a key in advance. For that reason, it is often best to use symmetric encryption to encrypt a large amount of data, and then encrypt the symmetric key with asymmetric encryption. We will investigate that further in the next chapter. For now, let’s begin with a simple example – encrypting and decrypting a string.

The only JCE classes our code will use are the (javax.crypto.*) classes, which are used to handle cryptography. This makes the programs portable to any provider that supports the algorithms we use. We’ve discussed some of these classes in the previous chapter, but now let’s investigate them in more depth along with the java.security.Key class.

javax.crypto.Cipher

The cipher is essentially the engine that performs the encryption and decryption of data. The Cipher class has four methods that we’re interested in right now: getInstance()init()update(), and doFinal(). Let’s go over them one by one:

getInstance()

As we’ve mentioned before, most of the classes in the JCE use factory methods instead of normal constructors. Rather than create an instance with the new keyword, we make a call to the class’s getInstance() method, with the name of the algorithm and some additional parameters like so:

The first parameter is the name of the algorithm, in this case, “DESede”. The second is the mode the cipher should use, “ECB”, which stands for Electronic Code Book. The third parameter is the padding, specified with “PKCS5Padding”. We’ll go into more detail about the mode and padding later. For now, just be aware that even though it is possible to skip the declaration of the mode and padding they should be declared anyway. If the mode and padding are skipped, the provider picks a mode and padding to use, which is rarely what you want, and prevents the code from being ported to other providers.

init()

Once an instance of Cipher is obtained, it must be initialized with the init() method. This declares the operating mode, which should be either ENCRYPT_MODE or DECRYPT_MODE, and also passes the cipher a key (java.security.Key, described later). Assuming we had a key declared, initialized, and stored in the variable myKey, we could initialize a cipher for encryption with the following line of code:

The JCE 1.2.1 specification adds two more possible operating modes, WRAP_MODE and

UNWRAP_MODE

. These modes set up a cipher to encrypt or decrypt keys. We’ll use these modes later, when we use asymmetric encryption.

update()

In order to actually encrypt or decrypt anything, we need to pass it to the cipher in the form of a byte array. If the data is in the form of anything other than a byte array, it needs to be converted. If we have a string called myString and we want to encrypt it with the cipher we’ve initialized above, we can do so with the following two lines of code:

Ciphers typically buffer their output. If the input is large enough that it produces some ciphertext, it will be returned as a byte array. If the buffer has not been filled, then null will be returned.

Note that in order to get bytes from a string, we should specify the encoding method. In most cases, it will be UTF8.

If you don’t specify the encoding type, you will get the underlying platform’s default encoding. This is almost never what you want, and can cause bizarre errors when encryption takes place on one platform, and decryption on another. Always specify the encoding.

doFinal()

Now we can actually get the encrypted data from the cipher. doFinal() will produce a byte array, which is the encrypted data.

A number of the methods we’ve talked about can be overloaded with different arguments, like start and end indices for the byte arrays passed in. We’re not going to go into detail covering them, as they are presented clearly in the JCE’s JavaDoc.

java.security.Key

Key is an interface that defines a key to be used for cryptographic functions. A key object cannot be instantiated directly with new, but rather is created by a factory method in a class like javax.crypto.KeyGenerator or java.security.KeyFactory. Key defines three methods: getAlgorithm()getEncoded(), and getFormat(). They are all concerned with transporting keys, which we’ll describe later in this chapter.

javax.crypto.KeyGenerator

KeyGenerator allows us to create new keys for use in symmetric encryption. There are three methods that we’re interested in at this point: getInstance()init(), and generateKey().

getInstance()

As in Cipher, we cannot construct a KeyGenerator with new. Instead we need to call getInstance() with the name of the algorithm. An optional second parameter can be used to define the provider we wish to use. The following line of code will create a key generator that will generate DESede keys:

init()

Instances of KeyGenerator need to be initialized with the size of the key to be generated. With some algorithms, like TripleDES, this is always the same (168 bits). Other algorithms, like Blowfish, have variable key lengths as shown in Chapter 3.

The following line will initialize the key generator created above:

generateKey()

This method actually generates the Key object that can be used by an instance of Cipher. There are no arguments, so the call is quite simple:

Simple Encryption Example

Now that we’ve gone over some of the classes and methods we’ll need for symmetric encryption, we’re going to create a simple program that utilizes them. Our program will create a DESede key, and then encrypt a string with it. We’ll then decrypt the encrypted string and display it along with the plaintext and ciphertext bytes as and when we use them.

Here we’re using the name DESede rather than TripleDES since that’s the term used in the Bouncy Castle provider. You may have to change this if you using a different provider.

To run the example, enter the following:

The string "HelloWorld!" will then be converted to a byte array, encrypted, and then decrypted and converted back to a String. Here’s what the output should look like bearing in mind that the actual ciphertext will be different each time you run the example, because the key is generated anew on every execution:

Blowfish Example

Now we’re going to modify the previous example to use Blowfish instead of DESede. Thanks to the architecture of the JCE, this is a very simple task. We just change the arguments passed to the KeyGenerator and Cipher initializations, and a few comments. Blowfish keys can be any bit size from 8 to 448, as long as the number is divisible by 8. We’ll use 128 for our example.

The changes from the DESede example are highlighted below:


Of course, if you run this program, you’ll see the following output:

Now let’s look at a different method of encryption.

Professional Java Security : Symmetric Encryption Part 2