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
e
ncryption-
d
ecryption-
e
ncryption; 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.
|
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:
-
Create the key for the cipher.
-
Retrieve the transformation you want to use with
the
Cipher.getInstance( )
factory method.
-
Initialize the
cipher by passing
Cipher.ENCRYPT_MODE
and the key to the
init( )
method
.
-
Feed data to the
update( )
method
.
-
While there's more
data, repeat step 4
.
-
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.
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);}
}
}
|
{% if main.adsdop %}{% include 'adsenceinline.tpl' %}{% endif %}
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.
|