Chapter 13. Cipher−Based Encryption
In this chapter, well look at how to encrypt data using ciphers. We usually think of encryption as a means of protecting data sent over an insecure network, although it may also be used to protect data stored in a file, on a
Java smart card, or in a number of other applications. With ciphers, the encryption of data is separate from its transmission. This is in sharp contrast to SSL, which can encrypt only data that is sent over sockets.
Cipher−based encryption is part of the JCE, which contains an engine the cipher engine that performs encryption as well as several classes that support data encryption. All the classes in this chapter are available
only with the security provider that comes with JCE.
13.1 The Cipher Engine
First, well look at the engine that performs encryption within JCE. This engine is called the Cipher
class javax.crypto.Cipher
; it provides an interface to encrypt and decrypt data either in arrays within the program or as that data is read or written through Javas stream interfaces:
public class Cipher implements Cloneable Perform encryption and decryption of arbitrary data, using potentially a wide array of encryption
algorithms. Like all security engines, the cipher engine implements named algorithms. However, the naming convention
for the cipher engine is different in that cipher algorithms are compound names that can include the name of the algorithm along with the name of a padding scheme and the name of a mode. Padding schemes and modes
are specified by names −− just like algorithms. In theory, just as you may pick a new name for an algorithm, you may specify new names for a padding scheme or a mode, although the
SunJCE security provider
specifies several standard ones. Modes and padding schemes are present in the
Cipher class because that class implements what is known as
a block cipher; that is, it expects to operate on data one block e.g., 8 bytes at a time. Padding schemes are required in order to ensure that the length of the data is an integral number of blocks.
Modes are provided to further alter the encrypted data in an attempt to make it harder to break the encryption. For example, if the data to be encrypted contains a number of similar patterns −− repeated names or
headerfooter information, for example −− any patterns in the resulting data may aid in breaking the encryption. Different modes of encrypting data help prevent these sorts of attacks. Depending upon the mode
used by a cipher, it may need to be initialized in a special manner when the cipher is used for decryption. Some modes require initialization via an initialization vector.
Modes also enable a block cipher to behave as a stream cipher; that is, instead of requiring a large, 8−byte chunk of data to operate upon, a mode may allow data to be processed in smaller quantities. So modes are
very important in stream−based operations where data may need to be transmitted one or two characters at a time.
The algorithms specified by the SunJCE
security provider are: DES
DES is the Data Encryption Standard algorithm, a standard that has been adopted by various organizations, including the U.S. government. There are known ways to attack this encryption, though
they require a lot of computing power to do so; despite widespread predictions about the demise of
DES, it continues to be used in many applications and is generally considered secure. The examples in this chapter are mostly based on DES encryption.
DESede This is also known as triple−DES or multiple−DES. This algorithm uses multiple DES keys to
perform three rounds of DES encryption or decryption; the added complexity greatly increases the amount of time required to break the encryption. It also greatly increases the amount of time required
to encrypt and to decrypt the data.
From a developers perspective, DESede is equivalent to DES; only the algorithm name passed to the key generator and cipher engines is different. Although DESede requires multiple keys, these keys are
encoded into a single secret key. Hence, the programming steps required to use DESede are identical to the steps required to use DES.
PBEWithMD5AndDES This is the password−based encryption defined in PKCS5. This algorithm entails using a password, a
byte array known as salt, and an iteration count along with an MD5 message digest to produce a DES secret key; this key is then used to perform DES encryption or decryption. PKCS5 was developed by
RSA Data Security, Inc., primarily to encrypt private keys, although it may be used to encrypt any arbitrary data.
From a developers perspective, this algorithm requires some special programming to obtain the key. Well show an example of that later.
Blowfish This algorithm was designed by Bruce Schneier; it is an attractive algorithm because it is not
patented, and Mr. Schneier has placed implementations of the algorithm in the public domain. It is best used in applications where the key does not change often, though it requires a lot of memory.
The modes specified by the SunJCE
security provider are: ECB
This is the electronic cookbook mode. ECB is the simplest of all modes; it takes a simple block of data 8 bytes in the
SunJCE implementation, which is standard and encrypts the entire block at once.
No attempt is made to hide patterns in the data, and the blocks may be rearranged without affecting decryption though the resulting plaintext will be out of order. Because of these limitations, ECB is
recommended only for binary data; text or other data with patterns in it is not well−suited for this mode.
ECB mode can only operate on full blocks of data, so it is generally used with a padding scheme. ECB mode does not require an initialization vector.
CBC This is the cipher block chaining mode. In this mode, input from one block of data is used to modify
the encryption of the next block of data; this helps to hide patterns although data that contains identical initial text −− such as email messages −− will still show an initial pattern. As a result, this
mode is suitable for text data. This is the only mode that will work for PBEWithMD5AndDES encryption.
CBC mode can only operate on full blocks of data, so it is generally used with a padding scheme. CBC mode requires an initialization vector for decryption.
CFB This is the cipher−feedback mode. This mode is very similar to CBC, but its internal implementation
is slightly different. CBC requires a full block 8 bytes of data to begin its encryption, while CFB can begin encryption with a smaller amount of data. So this mode is suitable for encrypting text,
especially when that text may need to be processed a character at a time. By default, CFB mode operates on 8−byte 64−bit blocks, but you may append a number of bits after CFB e.g., CFB8 to
specify a different number of bits on which the mode should operate. This number must be a multiple of 8.
CFB requires that the data be padded so that it fills a complete block. Since that size may vary, the padding scheme that is used with it must vary as well. For CFB8, no padding is required, since data is
always fed in an integral number of bytes.
CFB mode requires an initialization vector for decryption. OFB
This is the output−feedback mode. This mode is also suitable for text; it is used most often when there is a possibility that bits of the encrypted data may be altered in transit e.g., over a noisy modem.
While a 1−bit error would cause an entire block of data to be lost in the other modes, it only causes a loss of 1 bit in this mode. By default, OFB mode operates on 8−byte 64−bit blocks, but you may
append a number of bits after OFB e.g., OFB8 to specify a different number of bits on which the mode should operate. This number must be a multiple of 8.
OFB requires that the data be padded so that it fills a complete block. Since that size may vary, the padding scheme that is used with it must vary as well. For OFB8, no padding is required since data is
always fed in an integral number of bytes.
OFB mode requires an initialization vector for decryption. PCBC
This is the propagating cipher block chaining mode. It is popular in a particular system known as Kerberos; if you need to speak to a Kerberos version 4 system, this is the mode to use. However, this
mode has some known methods of attack, and Kerberos version 5 has switched to using CBC mode. Hence, PCBC mode is no longer recommended.
PCBC mode requires that the input be padded to a multiple of 8 bytes. PCBC mode requires an initialization vector for decryption.
The padding schemes specified by the SunJCE
security provider are: Chapter 13. Cipher−Based Encryption
PKCS5Padding This padding scheme ensures that the input data is padded to a multiple of 8 bytes.
NoPadding When this scheme is specified, no padding of input is done. In this case, the number of input bytes
presented to the encryption cipher must be a multiple of the block size of the cipher; otherwise, when the cipher attempts to encrypt or decrypt the data, it generates an error.
Remember these uses of modes and padding are specific to the SunJCE
security provider. The modes and padding schemes are based upon accepted standards and are thus likely to be implemented in this manner by
third−party security providers as well, but check your third−party provider documentation to be sure. The mode and padding scheme specified for decryption must match the mode and padding scheme specified
for encryption, or the decryption will fail. The
Cipher class is normally used to encrypt or decrypt data. However, it may also be used to wrap and
unwrap keys. Well discuss encryption and decryption first; information about wrapping and unwrapping keys will follow.
13.1.1 Using the Cipher Class for EncryptionDecryption
In order to obtain an instance of the Cipher
class, we call one of these methods: public static Cipher getInstanceString algorithmName
public static Cipher getInstanceString algorithmName, String provider Obtain a cipher engine that can perform encryption and decryption by implementing the named
algorithm. The engine is provided by the given security provider, or the list of installed security providers is searched for an appropriate engine.
If an implementation of the given algorithm cannot be found, a NoSuchAlgorithmException
is thrown. If the named provider cannot be found, a
NoSuchProviderException is thrown.
The algorithm name passed to the getInstance
method may either be a simple algorithm name e.g., DES, or it may be an algorithm name that specifies a mode and padding in this format:
algorithmmodepadding e.g., DESECBPKCS5Padding. If the mode and padding are not specified, they default to an implementation−specific value; in the
SunJCE security provider, the mode defaults
to ECB CBC if the algorithm is PBEWithMD5andDES and padding defaults to PKCS5. Once youve obtained a cipher object, you must initialize it. An object can be initialized for encryption,
decryption, or for key wrapping, but in any case, you must provide a key. If the algorithm is a symmetric cipher, you should provide a secret key; otherwise, you should provide a public key to encrypt data and a
private key to decrypt data in fact, the key must match the algorithm type: a DES cipher must use a DES key, and so on. Initialization is achieved with one of these methods:
public final void initint op, Key k public final void initint op, Certificate c
public final void initint op, Key k, AlgorithmParameterSpec aps public final void initint op, Key k, AlgorithmParameterSpec aps, SecureRandom sr
public final void initint op, Key k, SecureRandom sr 246
public final void initint op, Certificate c, SecureRandom sr public final void initint op, Key k, AlgorithmParameters ap
public final void initint op, Key k, AlgorithmParameters ap, SecureRandom sr Initialize the cipher to encrypt or decrypt data. If
op is
Cipher.ENCRYPT_MODE , the cipher is
initialized to encrypt data; if op
is Cipher.DECRYPT_MODE
, the cipher is initialized to decrypt data. The cipher is initialized with the given key or the public key contained within the given
certificate. These calls reset the engine to an initial state, discarding any previous data that may have been fed to
the engine. Hence, a single cipher object can be used to encrypt data and then later to decrypt data. Many algorithm modes we discussed earlier require an initialization vector to be specified when the
cipher is initialized for decrypting. In these cases, the initialization vector must be passed to the init
method within the algorithm parameter specification or algorithm parameters; typically, the
IvParameterSpec class is used to do this for DES encryption.
In the SunJCE
security provider, specifying an initialization vector for a mode that does not support it will eventually lead to a
NullPointerException . Failure to specify an initialization vector for
a mode that requires one will generate incorrect decrypted data. After an engine has been initialized, it must be fed data. There are two sets of methods to accomplish this. The
first set can be used any number of times:
public final byte[] updatebyte[] input public final byte[] updatebyte[] input, int offset, int length
public final int updatebyte[] input, int offset, int length, byte[] output public final int updatebyte[] input, int offset, int length, byte[] output, int outOffset
Encrypt or decrypt the data in the input array starting at the given offset for the given length, if applicable. The resulting data is either placed in the given output array in which case the size of the
output data is returned or returned in a new array. If the cipher has not been initialized, an IllegalStateException
is thrown. If the length of the data passed to this method is not an integral number of blocks, any extra data is
buffered internally within the cipher engine; the next call to an update
or doFinal
method processes that buffered data as well as any new data that is just being provided. If the given output buffer is too small to hold the data, a
ShortBufferException is thrown. The
required size of the output buffer can be obtained from the getOutputSize
method. A ShortBufferException
does not clear the state of the cipher: any buffered data is still held, and the call can be repeated with a correctly sized buffer with no ill effects.
This second set of methods should only be called once: public final byte[] doFinal
public final int doFinalbyte[] output, int offset public final byte[] doFinalbyte[] input
public final byte[] doFinalbyte[] input, int offset, int length public final int doFinalbyte[] input, int offset, int length, byte[] output
public final int doFinalbyte[] input, int offset, int length, byte[] output, int outOffset
Encrypt or decrypt the data in the input array as well as any data that has been previously buffered in Chapter 13. Cipher−Based Encryption