Tuesday, January 19, 2010

Section 12.5. The Cipher Class










12.5. The Cipher Class







The javax.crypto.Cipher class is a concrete class that encrypts arrays of bytes. The default implementation performs no encryption, but you'll never see this. You'll only receive subclasses that implement particular algorithms.



public class Cipher extends Object



The subclasses of Cipher that do real encryption are supplied by providers. Different providers can provide different sets of algorithms. For instance, an authoritarian government might only allow the installation of algorithms it knows how to crack, and create a provider that provided those algorithms and only those algorithms. A corporation might want to install algorithms that allow for key recovery in the event that an employee leaves the company or forgets their password.


JDK 1.3 and earlier only include the Sun provider that supplies no encryption schemes, though it does supply several digest algorithms. The JCE (which is bundled with Java 1.4 and later) adds one more provider, SunJCE,
which provides DES, triple DES (DESede), and password-based encryption (PBE). Other vendors may bundle additional providers. For instance, Apple's Java 5 VM includes an Apple-specific provider that implements DES, Triple DES, AES, Blowfish, PBE, Diffie-Hellman, MD5, and SHA1. RSA's payware BSafe Crypto-J product has a security provider that implements the RSA, DES, DESede, RC2, RC4, and RC5 cipher algorithms. The open source JCE provider from the Legion of the Bouncy Castle supports AES, Blowfish, CAST5, CAST6, DES, Triple DES, IDEA, RC2, RC5, RC6, Rijndael, Skipjack, Twofish, and Serpent, among others.


Most providers include some unique algorithms. However, providers usually also include some algorithms already supplied by other providers. At compile time, you do not know which providers will be installed at runtime. Indeed, different people running your program are likely to have different providers available, especially if you ship internationally. Therefore, rather than using constructors, the Cipher class relies on two static getInstance( ) factory methods that return Cipher objects initialized to support particular transformations:



public static final Cipher getInstance(String transformation)
throws NoSuchAlgorithmException, NoSuchPaddingException
public static final Cipher getInstance(String transformation, String provider)
throws NoSuchAlgorithmException, NoSuchProviderException,
NoSuchPaddingException



The first argument, TRansformation, is a string that names the algorithm, mode, and padding scheme to be used to encrypt or decrypt the data. Examples include "DES", "PBEWithMD5AndDES", and "DES/ECB/PKCS5Padding". The optional second argument to getInstance( ), provider, names the preferred provider for the requested transformation. If more than one installed provider supports the transformation, the one named in the second argument is used. Otherwise, an implementation is selected from any available provider that supports the transformation. If you request a transformation from getInstance( ) that the provider does not support, a NoSuchAlgorithmException or NoSuchPaddingException is thrown. If you request a provider that is not installed, a NoSuchProviderException is thrown.


The transformation string always includes the name of a cryptographic algorithm: for example, DES. The standard names for common algorithms are listed in Table 12-2. Not all of these algorithms are guaranteed to be available.


Sun's JDK 1.4 only bundles DES, DESede, AES, Blowfish, PBEWithMD5AndDES, and PBEWithMD5AndTripleDES. JDK 1.5 added RC2, ARCFOUR, PBEWithSHA1AndDESede, and PBEWithSHA1AndRC2_40.


Table 12-2. JCE standard algorithm names

Name

Algorithm

AES (a.k.a. Rijndael)

The U.S. Federal government's Advanced Encryption Standard as defined by NIST in FIPS 197 and invented by Joan Daemen and Vincent Rijmen; a symmetric 128-bit block cipher with keys of length 128, 192, or 256 bits; see http://csrc.nist.gov/publications/fips/fips197/fips-197.pdf. There are no known practical attacks on this algorithm, but a couple of theoretical attacks have been devised, and cryptographers are nervous that it may not be as strong as initially thought.

DES

The U.S. Federal government's Data Encryption Standard as defined by NIST in FIPS 46-1 and 46-2; a symmetric 64-bit block cipher that uses a 56-bit key; see http://www.itl.nist.gov/fipspubs/fip46-2.htm. Given the small key space, this algorithm can be broken by brute force.

DESede

DES encryption-decryption-encryption; triple DES; like DES, a 64-bit symmetric block cipher. DES encryption with one 56-bit key is followed by decryption with a different 56-bit key, which is followed by encryption with the first 56-bit key, effectively providing a 112-bit key space. (However, a known weakness in the algorithm reduces the effective strength of the key to roughly 80 bits.) This is one of the slower algorithms in use.

PBEWithMD5

AndDES

Password-Based Encryption as defined in RSA Laboratories, "PKCS #5: Password-Based Encryption Standard," Version 1.5, Nov. 1993; based on DES; also requires a salt; see http://www.rsasecurity.com/rsalabs/node.asp?id=2127.

PBEWithMD5

AndTripleDES

Password-Based Encryption as defined in RSA Laboratories, "PKCS #5: Password-Based Encryption Standard," version 1.5, Nov. 1993; based on Triple DES; also requires a salt and an initialization vector; see http://www.rsasecurity.com/rsalabs/node.asp?id=2127.

PBEWithSHA1AndDESede

Password-Based Encryption using triple DES encryption and SHA1 hashing; also requires a salt and an initialization vector.

PBEWithMD5AndTripleDES

Password-Based Encryption using triple DES encryption and MD5 hashing; also requires a salt and an initialization vector.

RSA

The Rivest, Shamir, and Adleman asymmetric cipher algorithm; RSA encryption as defined in the RSA Laboratories Technical Note PKCS#1, http://www.rsasecurity.com/rsalabs/node.asp?id=2125. It is possible that the NSA cannot penetrate this algorithm.

IDEA

The International Data Encryption Algorithm developed and patented by Dr. X. Lai and Professor J. Massey of the Federal Institute of Technology in Zurich, Switzerland; a symmetrical 64-bit block cipher with a 128-bit key. The patent expires in 2010 in the U.S., 2011 in Europe.

RC2

A variable key-size symmetric 64-bit block cipher designed by Ron Rivest as a drop-in replacement for DES. The NSA probably doesn't have much trouble breaking this one; see IETF RFC 2268, http://www.faqs.org/rfcs/rfc2268.html.

ARCFOUR (a.k.a. RC4)

A weak symmetric stream cipher algorithm designed by Ron Rivest used in Netscape's Secure Sockets Layer (SSL), among other products. The name "RC4" is trademarked, so this algorithm is also referred to by the untrademarked name ARCFOUR. Used (and broken) in the Wireless Encryption Protocol (WEP).

Blowfish

An unpatented fast, free, symmetric, variable key length (32 to 448 bits) 64-bit block cipher designed by Bruce Schneier as a drop-in replacement for DES; see http://www.schneier.com/blowfish.html.

Twofish

An unpatented free, symmetric, variable key length (128, 192 or 256 bits) 128-bit block cipher designed by Bruce Schneier; see http://www.schneier.com/twofish.html.

Skipjack

A symmetric key block encryption algorithm designed by the NSA with 80-bit keys. Several as yet impractical attacks on the algorithm have been found. Regardless of the algorithm's strength, the key length is too short to inspire confidence.

Serpent

A symmetric variable key length (128, 192 or 256 bits) 128-bit block cipher designed by Ross Anderson, Eli Biham, and Lars Knudsen. It is notable for its highly parallelizable design.

I have a hunch (not necessarily shared by experts in the field) that RSA and similar algorithms will be broken someday by means much less computationally intensive than brute force search. RSA's strength rests on the difficulty of factoring a large number into two large primes. However, it is not known whether such factorization is fundamentally hard or whether we just don't yet know the right factoring algorithms. It seems obvious to me that there's a lot of structure in the prime numbers that has yet to be exploited or understood by number theorists. For instance, Goldbach's conjecture and the number of twin primes are still unsolved questions. Therefore, I would not be surprised if far more efficient factorization algorithms are discovered. Any such algorithm would severely reduce the strength of encryption schemes like RSA. Furthermore, there's been an explosion of interest and research in quantum computing, following the discovery that RSA would be much more easily cracked by a quantum computer than by a traditional one. This does not seem to be the case for public key encryption schemes based on something other than prime factorization, for instance, discrete logarithms or elliptic curves.



When faced with input longer than its block size, a block cipher must divide and possibly reorder that input into blocks of the appropriate size. The algorithm for doing this is called a mode. A mode name may be included in the transformation string separated from the algorithm by a slash. If a mode is not selected, the provider supplies a default. Modes apply to block ciphers in general and DES in particular, though other block ciphers like Blowfish may use some of these modes as well. The named modes in the JCE are listed in Table 12-3. All of these modes are supported by the JCE, but modes are algorithm-specific. If you try to use an unsupported mode or a mode that doesn't match the algorithm, a NoSuchAlgorithmException is thrown.


Table 12-3. Block cipher modes

Name

Mode

ECB

Electronic CodeBook Mode; the 64-bit blocks are encrypted independently of each other and may also be decrypted independently of each other, so this mode is useful when you want random access to an encrypted file but in general is less secure than other modes. It does not require an initialization vector. See "DES Modes of Operation," National Institute of Standards and Technology Federal Information Processing Standards Publication 81, December 1980; http://www.itl.nist.gov/fipspubs/fip81.htm (NIST FIPS PUB 81).

CBC

Cipher Block Chaining Mode, as defined in NIST FIPS PUB 81; best choice for encrypting files; uses an initialization vector.

CFB

K-bit Cipher FeedBack Mode, as defined in NIST FIPS PUB 81; best choice for real-time encryption of streaming data such as network connections where each byte must be sent immediately rather than being buffered; uses an initialization vector.

OFB

K-bit Output FeedBack Mode, as defined in NIST FIPS PUB 81; designed so that a 1-bit error in the ciphertext only produces a 1-bit error in the plaintext; therefore, the best choice on noisy, error-prone channels; uses an initialization vector.

PCBC

Propagating Cipher Block Chaining, as used in pre-Version 5 Kerberos; similar to the more secure CBC mode used in Kerberos Version 5 and later; uses an initialization vector.



If the algorithm is a block cipher like DES, the transformation string may include a padding scheme that adds extra bytes to the input to fill out the last block. The named padding schemes

are shown in Table 12-4. Algorithms that use modes must generally also specify a padding scheme.


Table 12-4. Padding schemes

Name

Scheme

NoPadding

Do not add any padding bytes.

ZeroByte

Pad with zeros; insecure and not recommended.

PKCS5Padding

RSA Laboratories, "PKCS #5: Password-Based Encryption Standard," Version 1.5, Nov. 1993; see http://www.rsasecurity.com/rsalabs/node.asp?id=2127.

PKCS7Padding

RSA Laboratories, "PKCS #7: Cryptographic Message Syntax Standard," Version 1.5, Nov. 1993; see http://www.rsasecurity.com/rsalabs/node.asp?id=2129.

WithCTS

Ciphertext Stealing; really a variant mode of Cipher Block Chaining (CBC) that does not require any padding; requires at least one full block of data to operate.

SSL3Padding

A slight variation of PKCS5Padding used in Secure Sockets Layer (SSL); see "SSL Protocol Version 3.0, November 18, 1996, section 5.2.3.2 (CBC block cipher)" at http://wp.netscape.com/eng/ssl3/ssl-toc.html.



Encrypting data with a Cipher object takes six steps:


  1. Create the key for the cipher.

  2. Retrieve the transformation you want to use with the Cipher.getInstance( ) factory method.

  3. Initialize the cipher by passing Cipher.ENCRYPT_MODE and the key to the init( ) method.

  4. Feed data to the update( ) method.

  5. While there's more data, repeat step 4.

  6. Invoke doFinal( ).


Steps 1 and 2 can be reversed, as is done in the flowchart for this process shown in Figure 12-2. Decryption is almost an identical process except that you pass Cipher.DECRYPT_MODE to init( ) instead of Cipher.ENCRYPT_MODE. The same engine can both encrypt and decrypt data with a given transformation.



Figure 12-2. Encrypting data



Example 12-4 is a simple program that reads a filename and a password from the command line and encrypts the file with DES. The key is generated from the bytes of the password in a fairly predictable and insecure fashion. The cipher is initialized for encryption with the DES algorithm in CBC mode with PKCS5Padding and a random initialization vector. The initialization vector and its length are written at the start of the encrypted file so they'll be conveniently available for decryption.


Data is read from the file in 64-byte blocks. This happens to be an integral multiple of the 8-byte block size used by DES, but that's not necessary. The Cipher object buffers as necessary to handle nonintegral multiples of the block size. Each block of data is fed into the update( ) method to be encrypted. update( ) returns either encrypted data or null if it doesn't have enough data to fill out a block. If it returns the encrypted data, that's written into the output file. When no more input data remains, the cipher's doFinal( ) method is invoked to pad and flush any remaining data. Then both input and output files are closed.


Example 12-4. File Encryptor




import java.io.*;
import java.security.*;
import java.security.spec.*;
import javax.crypto.*;
import javax.crypto.spec.*;
public class FileEncryptor {
public static void main(String[] args) {
if (args.length != 2) {
System.err.println("Usage: java FileEncryptor filename password");
return;
}
String filename = args[0];
String password = args[1];
if (password.length( ) < 8 ) {
System.err.println("Password must be at least eight characters long");
}
try {
FileInputStream fin = new FileInputStream(args[0]);
FileOutputStream fout = new FileOutputStream(args[0] + ".des");
// Create a key.
byte[] desKeyData = password.getBytes( );
DESKeySpec desKeySpec = new DESKeySpec(desKeyData);
SecretKeyFactory keyFactory = SecretKeyFactory.getInstance("DES");
SecretKey desKey = keyFactory.generateSecret(desKeySpec);
// Use Data Encryption Standard.
Cipher des = Cipher.getInstance("DES/CBC/PKCS5Padding");
des.init(Cipher.ENCRYPT_MODE, desKey);
// Write the initialization vector onto the output.
byte[] iv = des.getIV( );
DataOutputStream dout = new DataOutputStream(fout);
dout.writeInt(iv.length);
dout.write(iv);
byte[] input = new byte[64];
while (true) {
int bytesRead = fin.read(input);
if (bytesRead == -1) break;
byte[] output = des.update(input, 0, bytesRead);
if (output != null) dout.write(output);
}
byte[] output = des.doFinal( );
if (output != null) dout.write(output);
fin.close( );
dout.flush( );
dout.close( );
}
catch (InvalidKeySpecException ex) {System.err.println(ex);}
catch (InvalidKeyException ex) {System.err.println(ex);}
catch (NoSuchAlgorithmException ex) {System.err.println(ex);}
catch (NoSuchPaddingException ex) {System.err.println(ex);}
catch (BadPaddingException ex) {System.err.println(ex);}
catch (IllegalBlockSizeException ex) {System.err.println(ex);}
catch (IOException ex) {System.err.println(ex);}
}
}



Many different exceptions must be caught. Except for the usual IOException, they are all subclasses of java.security.GeneralSecurityException. You could save some space simply by catching that. For example:



catch (GeneralSecurityException ex) {
System.err.println(ex);
ex.printStackTrace( );
}



One exception I'll note in particular (because it threw me more than once while writing this chapter): if you should see a NoSuchAlgorithmException, it probably means you haven't properly installed a provider that supports your algorithm.


Decrypting a file is similar, as Example 12-5 shows. The name of the input and output files and the password are read from the command line. A DES key factory converts the password to a DES secret key. Both input and output files are opened in file streams, and a data input stream is chained to the input file. The main reason for this is to read the initialization vector. First, the integer size is read, and then the actual bytes of the vector. The resulting array is used to construct an IvParameterSpec object that is used along with the key to initialize the cipher. Once the cipher is initialized, the data is copied from input to output much as before.


Example 12-5. File Decryptor




import java.io.*;
import java.security.*;
import java.security.spec.*;
import javax.crypto.*;
import javax.crypto.spec.*;
public class FileDecryptor {

public static void main(String[] args) {
if (args.length != 3) {
System.err.println("Usage: java FileDecryptor infile outfile password");
return;
}
String infile = args[0];
String outfile = args[1];
String password = args[2];
if (password.length( ) < 8 ) {
System.err.println("Password must be at least eight characters long");
}
try {
FileInputStream fin = new FileInputStream(infile);
FileOutputStream fout = new FileOutputStream(outfile);
// Create a key.
byte[] desKeyData = password.getBytes( );
DESKeySpec desKeySpec = new DESKeySpec(desKeyData);
SecretKeyFactory keyFactory = SecretKeyFactory.getInstance("DES");
SecretKey desKey = keyFactory.generateSecret(desKeySpec);
// Read the initialization vector.
DataInputStream din = new DataInputStream(fin);
int ivSize = din.readInt( );
byte[] iv = new byte[ivSize];
din.readFully(iv);
IvParameterSpec ivps = new IvParameterSpec(iv);
// Use Data Encryption Standard.
Cipher des = Cipher.getInstance("DES/CBC/PKCS5Padding");
des.init(Cipher.DECRYPT_MODE, desKey, ivps);
byte[] input = new byte[64];
while (true) {
int bytesRead = fin.read(input);
if (bytesRead == -1) break;
byte[] output = des.update(input, 0, bytesRead);
if (output != null) fout.write(output);
}
byte[] output = des.doFinal( );
if (output != null) fout.write(output);
fin.close( );
fout.flush( );
fout.close( );
}
catch (GeneralSecurityException ex) {
ex.printStackTrace( );
}
catch (IOException ex) {System.err.println(ex);}
}
}



Let's investigate some of the methods used in Example 12-4 and Example 12-5 in more detail.



12.5.1. init( )








Before a Cipher object can encrypt or decrypt data, it needs four things:


  • The mode to operate in (encryption or decryption; not a block cipher mode)

  • A key

  • Algorithm parameters, e.g., an initialization vector

  • A source of randomness


The init( ) method prepares the cipher by providing these four quantities or reasonable defaults. There are six overloaded variants:



public final void init(int opmode, Key key) throws InvalidKeyException
public final void init(int opmode, Key key, SecureRandom random)
throws InvalidKeyException
public final void init(int opmode, Key key, AlgorithmParameterSpec params)
throws InvalidKeyException, InvalidAlgorithmParameterException
public final void init(int opmode, Key key, AlgorithmParameterSpec params,
SecureRandom random) throws InvalidKeyException,
InvalidAlgorithmParameterException
public final void init(int opmode, Key key, AlgorithmParameters params)
throws InvalidKeyException, InvalidAlgorithmParameterException
public final void init(int opmode, Key key, AlgorithmParameters params,
SecureRandom random) throws InvalidKeyException,
InvalidAlgorithmParameterException



You can reuse a cipher object by invoking its init( ) method a second time. If you do, all previous information in the object is lost.



12.5.1.1. Mode

The mode determines whether this cipher is used for encryption or decryption. The mode argument has two possible values, which are both mnemonic constants defined by the Cipher class: Cipher.ENCRYPT_MODE and Cipher.DECRYPT_MODE.



public static final int ENCRYPT_MODE
public static final int DECRYPT_MODE





12.5.1.2. Key

The key is an instance of the java.security.Key interface. Symmetric ciphers like DES use the same key for both encryption and decryption. Asymmetric ciphers like RSA use different keys for encryption or decryption. Keys generally depend on the cipher. For instance, an RSA key cannot be used to encrypt a DES file or vice versa. If the key you provide doesn't match the cipher's algorithm, an InvalidKeyException is thrown.


To create a key, you first use the bytes of the key to construct a KeySpec for the algorithm you're using. Key specs are instances of the java.security.spec.KeySpec interface. Algorithm-specific implementations in the java.security.spec package include EncodedKeySpec, X509EncodedKeySpec, KCS8EncodedKeySpec, DSAPrivateKeySpec, and DSAPublicKeySpec, RSAPrivateKeySpec, RSAPrivateCrtKeySpec, RSAMultiPrimePrivateCrtKeySpec, RSAPublicKeySpec, and X509EncodedKeySpec. Java 5 added ECPrivateKeySpec and ECPublicKeySpec for public key cryptography based on elliptic curves rather than prime factorization. The javax.crypto spec package provides a few more including DESKeySpec, DESedeKeySpec, DHPrivateKeySpec, DHPublicKeySpec, PBEKeySpec. For example, this code fragment creates a DESKeySpec object that can be used to encrypt or decrypt from a password string using the DES algorithm:



byte[] desKeyData = password.getBytes( );
DESKeySpec desKeySpec = new DESKeySpec(desKeyData);



Once you've constructed a key specification from the raw bytes of the key, a key factory generates the actual key. A key factory is normally an instance of an algorithm-specific subclass of java.security.KeyFactory. It's retrieved by passing the name of the algorithm to the factory method javax.crypto.SecretKeyFactory.getInstance( ). For example:



SecretKeyFactory keyFactory = SecretKeyFactory.getInstance("DES");
SecretKey desKey = keyFactory.generateSecret(desKeySpec);



Providers should supply the necessary key factories and spec classes for any algorithms they implement.


A few algorithms, most notably Blowfish, use raw bytes as a key without any further manipulations. In these cases there may not be a key factory for the algorithm. Instead, you simply use the key spec as the secret key. For example:



byte[] blowfishKeyData = password.getBytes( );
SecretKeySpec blowfishKeySpec = new SecretKeySpec(blowfishKeyData,
"Blowfish");
Cipher blowfish = Cipher.getInstance("Blowfish/ECB/PKCS5Padding");
blowfish.init(Cipher.ENCRYPT_MODE, blowfishKeySpec);



Most of the examples in this book use very basic and not particularly secure passwords as keys. Stronger encryption requires more random keys. The javax.crypto.KeyGenerator class provides methods that generate random keys for any installed algorithm. For example:



KeyGenerator blowfishKeyGenerator = KeyGenerator.getInstance("Blowfish");
SecretKey blowfishKey = blowfishKeyGenerator.generateKey( );
Cipher blowfish = Cipher.getInstance("Blowfish/ECB/PKCS5Padding");
blowfish.init(Cipher.ENCRYPT_MODE, blowfishKey);



Generating random keys opens up the issue of how one stores and transmits the secret keys. To my way of thinking, random key generation makes more sense in public key cryptography, where all keys that need to be transmitted can be transmitted in the clear.




12.5.1.3. Algorithm parameters




The third possible argument to init( ) is a series of instructions for the cipher contained in an instance of the java.security.spec.AlgorithmParameterSpec interface or an instance of the java.security.AlgorithmParameters class. The AlgorithmParameterSpec interface declares no methods or constants. It's simply a marker for more specific subclasses that can provide additional, algorithm-dependent parameters for specific algorithms and modes (for instance, an initialization vector). If the algorithm parameters you provide don't fit the cipher's algorithm, an InvalidAlgorithmParameterException is thrown. The JCE provides several AlgorithmParameterSpec classes in the javax.crypto.spec package, including IVParameterSpec, which can set an initialization vector for modes that need it (CBC, CFB, and OFB), and PBEParameterSpec for password-based encryption.




12.5.1.4. Source of randomness

The final possible argument to init( ) is a SecureRandom object. This argument is only used when in encryption mode. It is an instance of the java.security.SecureRandom class, a subclass of java.util.Random that uses a pseudo-random number algorithm based on the SHA-1 hash algorithm instead of java.util.Random's linear congruential formula. java.util.Random's random numbers aren't random enough for strong cryptography. In this book, I will simply accept the default source of randomness.





12.5.2. update( )


Once the init( ) method has prepared the cipher for use, the update( ) method feeds data into it, encrypting or decrypting as it goes. This method has four overloaded variants. The first two return the encrypted or decrypted bytes:



public final byte[] update(byte[] input) throws IllegalStateException
public final byte[] update(byte[] input, int inputOffset, int inputLength)
throws IllegalStateException



They may return null if you're using a block cipher and not enough data has been provided to fill a block. The input data to be encrypted or decrypted is passed in as an array of bytes. Optional offsets and lengths may be used to select a particular subarray to be processed. update( ) tHRows an IllegalStateException if the cipher has not been initialized or it has already been finished with doFinal( ). In either case, it's not prepared to accept data until init( ) is called.


The second two variants of update( ) store the output in a buffer byte array passed in as the fourth argument and return the number of bytes stored in the buffer:



public final int update(byte[] input, int inputOffset, int inputLength,
byte[] output) throws IllegalStateException, ShortBufferException
public final int update(byte[] input, int inputOffset, int inputLength,
byte[] output, int outputOffset) throws IllegalStateException,
ShortBufferException



You can also provide an offset into the output array to specify where in the array data should be stored. An offset is useful when you want to repeatedly encrypt/decrypt data into the same array until the data is exhausted. You cannot, however, specify a length for the output data because it's up to the cipher to determine how many bytes of data it's willing to provide. The trick here is to make sure your output buffer is big enough to hold the processed output. Most of the time, the number of output bytes is close to the number of input bytes. However, block ciphers sometimes return fewer bytes on one call and more on the next. You can use the getOutputSize( ) method to determine an upper bound on the amount of data that will be returned if you were to pass in inputLength bytes of data:



public final int getOutputSize(int inputLength) throws IllegalStateException



If you don't do this and your output buffer is too small, update( ) throws a ShortBufferException. In this case, the cipher stores the data for the next call to update( ).


Java 5 added an update( ) method that reads from a ByteBuffer and writes into an output ByteBuffer:



public final int update(ByteBuffer input, ByteBuffer output)
throws ShortBufferException, IllegalStateException,
ReadOnlyBuffer Exception, IllegalArgumentException



Once you run out of data to feed to update( ), invoke doFinal( )
. This signals the cipher that it should pad the data with extra bytes if necessary and encrypt or decrypt all remaining bytes.




12.5.3. doFinal( )





The doFinal( ) method is responsible for reading one final array of data, wrapping that up with any data remaining in the cipher's internal buffer, adding any extra padding that might be necessary, and then returning the last chunk of encrypted or decrypted data. The simplest implementation of doFinal( ) takes no arguments and returns an array of bytes containing the encrypted or decrypted data. This is used to flush out any data that still remains in the cipher's buffer.



public final byte[] doFinal( )
throws IllegalStateException, IllegalBlockSizeException, BadPaddingException



An IllegalStateException means that the cipher is not ready to be finished; it has not been initialized; it has been initialized but no data has been fed into it; or it has already been finished and not yet reinitialized. An IllegalBlockSizeException is thrown by encrypting block ciphers if no padding has been requested, and the total number of bytes fed into the cipher is not a multiple of the block size. A BadPaddingException is thrown by a decrypting cipher that does not find the padding it expects to see.


There are five overloaded variants of doFinal( ) that allow you to provide additional input data or to place the result in an output buffer you supply. These variants are:



public final int doFinal(byte[] output, int outputOffset)
throws IllegalStateException, IllegalBlockSizeException,
ShortBufferException, BadPaddingException
public final byte[] doFinal(byte[] input)
throws IllegalStateException, IllegalBlockSizeException, BadPaddingException
public final byte[] doFinal(byte[] input, int inputOffset, int inputLength)
throws IllegalStateException, IllegalBlockSizeException, BadPaddingException
public final int doFinal(byte[] input, int inputOffset, int inputLength,
byte[] output) throws IllegalStateException, ShortBufferException,
IllegalBlockSizeException, BadPaddingException
public final int doFinal(byte[] input, int inputOffset, int inputLength,
byte[] output, int outputOffset) throws IllegalStateException,
ShortBufferException, IllegalBlockSizeException, BadPaddingException



All of the arguments are essentially the same as they are for update( ). output is a buffer where the cipher places the encrypted or decrypted data. outputOffset is the position in the output buffer where this data is placed. input is a byte array that contains the last chunk of data to be encrypted. inputOffset and inputLength select a subarray of input to be encrypted or decrypted.




12.5.4. Accessor Methods





As well as the methods that actually perform the encryption, the Cipher class has several getter methods that provide various information about the cipher. The getProvider( ) method returns a reference to the Provider that's implementing this algorithm. This is an instance of a subclass of java.security.Provider.



public final Provider getProvider( )



For block ciphers, getBlockSize( )returns the number of bytes in a block. For nonblock methods, it returns 0.



public final int getBlockSize( )



The getOutputSize( ) method tells you how many bytes of output this cipher produces for a given number of bytes of input. You generally use this before calling doFinal( ) or update( ) to make sure you provide a large enough byte array for the output, given inputLength additional bytes of data.



public final int getOutputSize(int inputLen) throws IllegalStateException



The length returned is the maximum number of bytes that may be needed. In some cases, fewer bytes may actually be returned when doFinal( ) is called. An IllegalStateException is thrown if the cipher is not ready to accept more data.


The getIV( ) method returns a new byte array containing this cipher's initialization vector. It's useful when the system picks a random initialization vector and you need to find out what that vector is so you can pass it to the decryption program, perhaps by storing it with the encrypted data.



public final byte[] getIV( )



getIV( ) returns null if the algorithm doesn't use initialization vectors or if the initialization vector isn't yet set.













No comments: