Implementing a Signature Class

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