Chapter 26. The javax.crypto PackageThe javax.crypto package defines classes and interfaces for various cryptographic operations. Figure 26-1 shows the class hierarchy of this package. The central class is Cipher, which is used to encrypt and decrypt data. CipherInputStream and CipherOutputStream are utility classes that use a Cipher object to encrypt or decrypt streaming data. SealedObject is another important utility class that uses a Cipher object to encrypt an arbitrary serializable Java object. Figure 26-1. The javax.crypto packageThe KeyGenerator class creates the SecretKey objects used by Cipher for encryption and decryption. SecretKeyFactory encodes and decodes SecretKey objects. The KeyAgreement class enables two or more parties to agree on a SecretKey in such a way that an eavesdropper cannot determine the key. The Mac class computes a message authentication code (MAC) that can ensure the integrity of a transmission between two parties who share a SecretKey. A MAC is akin to a digital signature, except that it is based on a secret key instead of a public/private key pair. Like the java.security package, the javax.crypto package is provider-based, so that arbitrary cryptographic implementations may be plugged into any Java installation. Various classes in this package have names that end in "Spi". These classes define a service-provider interface and must be implemented by each cryptographic provider that wishes to provide an implementation of a particular cryptographic service or algorithm. This package is part of the Java Cryptography Extension ( JCE). Sun distributes the JCE within the United States and Canada, but, unfortunately, U.S. export regulations prohibit the export of cryptographic technology to other countries. If you are not a resident of the United States or Canada, you have to obtain and use a third-party implementation of the JCE developed outside of the United States. The JCE is distributed with a cryptographic provider named "SunJCE" that includes a robust set of implementations for Cipher, KeyAgreement, Mac, and other classes. Installing the JCE extension is not the same, however, as installing the SunJCE provider. To make the SunJCE provider permanently available on a Java installation, you must edit the ${java.home}/lib/security/java.security file in the Java installation to add a line such as the following: security.provider.2=com.sun.crypto.provider.SunJCE The digit 2 in the line above specifies the preference order of the provider; you can use a different number. If the SunJCE provider is not statically installed as above, you can dynamically install it in an application with code such as the following: import java.security.*; Provider sunjce = new com.sun.crypto.provider.SunJCE(); Security.addProvider(sunjce); A full tutorial on cryptography is beyond the scope of this chapter and of this book. In order to use this package, you need to have a basic understanding of cryptographic algorithms such as DES. In order to take full advantage of this package, you also need to have a detailed understanding of things like feedback modes, padding schemes, the Diffie-Hellman key-agreement protocol, and so on. For a good introduction to modern cryptography in Java, see Java Cryptography by Jonathan Knudsen (O'Reilly). For more in-depth coverage, not specific to Java, see Applied Cryptography by Bruce Schneier (Wiley).
Signals that input data to a Cipher is not padded correctly.
Hierarchy: Object-->Throwable(Serializable)-->Exception-->java.security.GeneralSecurityException-->BadPaddingException Thrown By: Cipher.doFinal(), CipherSpi.engineDoFinal(), SealedObject.getObject()
This class performs encryption and decryption of byte arrays. Cipher is provider-based, so to obtain a Cipher object, you must call the static getInstance() factory method. The arguments to this method are a string that describes the type of encryption desired and, optionally, the name of the provider whose implementation should be used. To specify the desired type of encryption, you can simply specify the name of an encryption algorithm, such as "DES". Or you can specify a three-part name that includes the encryption algorithm, the algorithm operating mode, and the padding scheme. These three parts are separated by slash characters, as in "DES/CBC/PKCS5Padding". Finally, if you are requesting a block cipher algorithm in a stream mode, you can specify the number of bits to be processed at a time by following the name of the feedback mode with a number of bits. For example: "DES/CFB8/NoPadding". The "SunJCE" provider supports the following cryptographic algorithms:
SunJCE supports the following operating modes:
Finally, the "SunJCE" provider also supports two padding schemes: "NoPadding" and "PKCS5Padding". The name "SSL3Padding" is reserved, but this padding scheme is not implemented in the current release of "SunJCE". Once you have obtained a Cipher object for the desired cryptographic algorithm, mode, and padding scheme, you must initialize it by calling one of the init() methods. The first argument to init() is one of the constants ENCRYPT_MODE or DECRYPT_MODE. The second argument is a java.security.Key object that performs the encryption or decryption. If you use one of the symmetric (i.e., non-public key) encryption algorithms supported by the "SunJCE" provider, this Key object is a SecretKey implementation. You can optionally pass a java.security.SecureRandom object to init() to provide a source of randomness. If you do not, the Cipher implementation provides its own pseudo-random number generator. Some cryptographic algorithms require additional initialization parameters; these can be passed to init() as a java.security.AlgorithmParameters object or as a java.security.spec.AlgorithmParameterSpec object. When encrypting, you can omit these parameters, and the Cipher implementation uses default values or generates appropriate random parameters for you. In this case, you should call getParameters() after performing encryption to obtain the AlgorithmParameters used to encrypt. These parameters are required in order to decrypt, and must therefore be saved or transferred along with the encrypted data. Of the algorithms supported by the "SunJCE" provider, the block ciphers "DES", "DESede", and "Blowfish" all require an initialization vector when they are used in "CBC", "CFB", "OFB", or "PCBC" mode. You can represent an initialization vector with a javax.crypto.spec.IvParameterSpec object and obtain the raw bytes of the initialization vector used by a Cipher with the getIV() method. The "PBEWithMD5AndDES" algorithm requires a salt and iteration count as parameters. These can be specified with a javax.crypto.spec.PBEParameterSpec object. Once you have obtained and initialized a Cipher object, you are ready to use it for encryption or decryption. If you have only a single array of bytes to encrypt or decrypt, pass that input array to one of the doFinal() methods. Some versions of this method return the encrypted or decrypted bytes as the return value of the function. Other versions store the encrypted or decrypted bytes to another byte array you specify. If you choose to use one of these latter methods, you should first call getOutputSize() to determine the required size of the output array. If you want to encrypt or decrypt data from a streaming source or have more than one array of data, pass the data to one of the update() methods, calling it as many times as necessary. Then pass the last array of data to one of the doFinal() methods. If you are working with streaming data, consider using the CipherInputStream and CipherOutputStream classes instead.
Subclasses: NullCipher Passed To: CipherInputStream.CipherInputStream(), CipherOutputStream.CipherOutputStream(), SealedObject.{getObject(), SealedObject()} Returned By: Cipher.getInstance()
This class is an input stream that uses a Cipher object to encrypt or decrypt the bytes it reads from another stream. You must initialize the Cipher object before passing it to the CipherInputStream() constructor.
Hierarchy: Object-->java.io.InputStream-->java.io.FilterInputStream-->CipherInputStream
This class is an output stream that uses a Cipher object to encrypt or decrypt bytes before passing them to another output stream. You must initialize the Cipher object before passing it to the CipherOutputStream() constructor. If you are using a Cipher with any kind of padding, you must not call flush() until you are done writing all data to the stream; otherwise decryption fails.
Hierarchy: Object-->java.io.OutputStream-->java.io.FilterOutputStream-->CipherOutputStream
This abstract class defines the service-provider interface for Cipher. A cryptographic provider must implement a concrete subclass of this class for each encryption algorithm it supports. A provider can implement a separate class for each combination of algorithm, mode, and padding scheme it supports or implement more general classes and leave the mode and/or padding scheme to be specified in calls to engineSetMode() and engineSetPadding(). Applications never need to use or subclass this class.
Passed To: Cipher.Cipher()
Signals that the length of data provided to a block cipher (as implemented, for example, by Cipher and SealedObject) does not match the block size for the cipher.
Hierarchy: Object-->Throwable(Serializable)-->Exception-->java.security.GeneralSecurityException-->IllegalBlockSizeException Thrown By: Cipher.doFinal(), CipherSpi.engineDoFinal(), SealedObject.{getObject(), SealedObject()}
This class provides an API to a key-agreement protocol that allows two or more parties to agree on a secret key without exchanging any secrets and in such a way that an eavesdropper listening in on the communication between those parties cannot determine the secret key. The KeyAgreement class is algorithm-independent and provider-based, so you must obtain a KeyAgreement object by calling one of the static getInstance() factory methods and specifying the name of the desired key agreement algorithm and, optionally, the name of the desired provider of that algorithm. The "SunJCE" provider implements a single key-agreement algorithm named "DiffieHellman". To use a KeyAgreement object, each party first calls the init() method and supplies a Key object of its own. Then, each party obtains a Key object from one of the other parties to the agreement and calls doPhase(). Each party obtains an intermediate Key object as the return value of doPhase(), and these keys are again exchanged and passed to doPhase(). This process typically repeats n-1 times, where n is the number of parties, but the actual number of repetitions is algorithm-dependent. When doPhase() is called the last time, the second argument must be true to indicate that it is the last phase of the agreement. After all calls to doPhase() have been made, each party calls generateSecret() to obtain an array of bytes or a SecretKey object for a named algorithm type. All parties obtain the same bytes or SecretKey from this method. The KeyAgreement class is not responsible for the transfer of Key objects between parties or for mutual authentication among the parties. These tasks must be accomplished through some external mechanism. The most common type of key agreement is "DiffieHellman" key agreement between two parties. It proceeds as follows. First, both parties obtain a java.security.KeyPairGenerator for the "DiffieHellman" algorithm and use it to generate a java.security.KeyPair of Diffie-Hellman public and private keys. Each party passes its private key to the init() method of its KeyAgreement object. (The init() method can be passed a java.security.spec.AlgorithmParameterSpec object, but the Diffie-Hellman protocol does not require any additional parameters.) Next, the two parties exchange public keys, typically through some kind of networking mechanism (the KeyAgreement class is not responsible for the actual exchange of keys). Each party passes the public key of the other party to the doPhase() method of its KeyAgreement object. There are only two parties to this agreement, so only one phase is required, and the second argument to doPhase() is true. At this point, both parties call generateSecret() to obtain the shared secret key. A three-party Diffie-Hellman key agreement requires two phases and is slightly more complicated. Let's call the three parties Alice, Bob, and Carol. Each generates a key pair and uses its private key to initialize its KeyAgreement object, as before. Then Alice passes her public key to Bob, Bob passes his to Carol, and Carol passes hers to Alice. Each party passes this public key to doPhase(). Since this is not the final doPhase(), the second argument is false, and doPhase() returns an intermediate Key object. The three parties exchange these intermediate keys again in the same way: Alice to Bob, Bob to Carol, and Carol to Alice. Now each party passes the intermediate key it has received to doPhase() a second time, passing true to indicate that this is the final phase. Finally, all three can call generateSecret() to obtain a shared key to encrypt future communication.
Returned By: KeyAgreement.getInstance()
This abstract class defines the service-provider interface for KeyAgreement. A cryptographic provider must implement a concrete subclass of this class for each encryption algorithm it supports. Applications never need to use or subclass this class.
Passed To: KeyAgreement.KeyAgreement()
This class provides an API for generating secret keys for symmetric cryptography. It is similar to java.security.KeyPairGenerator, which generates public/private key pairs for asymmetric or public-key cryptography. KeyGenerator is algorithm-independent and provider-based, so you must obtain a KeyGenerator instance by calling one of the static getInstance() factory methods and specifying the name of the cryptographic algorithm for which a key is desired and, optionally, the name of the security provider whose key-generation implementation is to be used. The "SunJCE" provider includes KeyGenerator implementations for the "DES", "DESede", and "Blowfish" encryption algorithms, and also for the "HmacMD5" and "HmacSHA1" message authentication (MAC) algorithms. Once you have obtained a KeyGenerator, you initialize it with the init() method. You can provide a java.security.spec.AlgorithmParameterSpec object to provide algorithm-specific initialization parameters or simply specify the desired size (in bits) of the key to be generated. In either case, you can also specify a source of randomness in the form of a SecureRandom object. If you do not specify a SecureRandom, the KeyGenerator instantiates one of its own. None of the algorithms supported by the "SunJCE" provider require algorithm-specific parameters. After calling getInstance() to obtain a KeyGenerator and init() to initialize it, simply call generateKey() to create a new SecretKey. Remember that the SecretKey must be kept secret. Take precautions when storing or transmitting the key, so that it does not fall into the wrong hands. You may want to use a java.security.KeyStore object to store the key in a password-protected form.
Returned By: KeyGenerator.getInstance()
This abstract class defines the service-provider interface for KeyGenerator. A cryptographic provider must implement a concrete subclass of this class for each key-generation algorithm it supports. Applications never need to use or subclass this class.
Passed To: KeyGenerator.KeyGenerator()
This class defines an API for computing a message authentication code (MAC) that can check the integrity of information transmitted between two parties that share a secret key. A MAC is similar to a digital signature, except that it is generated with a secret key rather than with a public/private key pair. The Mac class is algorithm-independent and provider-based. Obtain a Mac object by calling one of the static getInstance() factory methods and specifying the name of the desired MAC algorithm and, optionally, the name of the provider of the desired implementation. The "SunJCE" provider implements two algorithms: "HmacMD5" and "HmacSHA1". These are MAC algorithms based on the MD5 and SHA-1 cryptographic hash functions. After obtaining a Mac object, initialize it by calling the init() method and specifying a SecretKey and, optionally, a java.security.spec.AlgorithmParameterSpec object. The "HmacMD5" and "HmacSHA1" algorithms can use any kind of SecretKey; they are not restricted to a particular cryptographic algorithm. And neither algorithm requires an AlgorithmParameterSpec object. After obtaining and initializing a Mac object, specify the data for which the MAC is to be computed. If the data is contained in a single byte array, simply pass it to doFinal(). If the data is streaming or is stored in various locations, you can supply the data in multiple calls to update(). End the series of update() calls with a single call to doFinal(). Note that some versions of doFinal() return the MAC data as the function return value. Another version stores the MAC data in a byte array you supply. If you use this version of doFinal(), be sure to call getMacLength() to instantiate an array of the correct length. A call to doFinal() resets the internal state of a Mac object. If you want to compute a MAC for part of your data and then proceed to compute the MAC for the full data, you should clone() the Mac object before calling doFinal(). Note, however, that Mac implementations are not required to implement Cloneable.
Hierarchy: Object-->Mac(Cloneable) Returned By: Mac.getInstance()
This abstract class defines the service-provider interface for Mac. A cryptographic provider must implement a concrete subclass of this class for each MAC algorithm it supports. Applications never need to use or subclass this class.
Passed To: Mac.Mac()
Signals that no implementation of the requested padding scheme can be found.
Hierarchy: Object-->Throwable(Serializable)-->Exception-->java.security.GeneralSecurityException-->NoSuchPaddingException Thrown By: Cipher.getInstance(), CipherSpi.engineSetPadding()
This trivial subclass of Cipher implements an identity cipher that does not transform plain text in any way. Unlike Cipher objects returned by Cipher.getInstance(), a NullCipher must be created with the NullCipher() constructor.
Hierarchy: Object-->Cipher-->NullCipher
This class is a wrapper around a serializable object. It serializes the object and encrypts the resulting data stream, thereby protecting the confidentiality of the object. Create a SealedObject by specifying the object to be sealed and a Cipher object to perform the encryption. Retrieve the sealed object by calling getObject() and specifying the Cipher or java.security.Key to use for decryption. The SealedObject keeps track of the encryption algorithm and parameters so that a Key object alone can decrypt the object.
Hierarchy: Object-->SealedObject(Serializable)
This interface represents a secret key used for symmetric cryptographic algorithms that depend on both the sender and receiver knowing the same secret. SecretKey extends the java.security.Key interface, but does not add any new methods. The interface exists in order to keep secret keys distinct from the public and private keys used in public-key, or asymmetric, cryptography. See also java.security.PublicKey and java.security.PrivateKey. A secret key is nothing more than arrays of bytes and does not require a specialized encoding format. Therefore, an implementation of this interface should return the format name "RAW" from getFormat() and should return the bytes of the key from getEncoded(). (These two methods are defined by the java.security.Key interface that SecretKey extends.)
Hierarchy: (SecretKey(java.security.Key(Serializable))) Implementations: javax.crypto.spec.SecretKeySpec Passed To: SecretKeyFactory.{getKeySpec(), translateKey()}, SecretKeyFactorySpi.{engineGetKeySpec(), engineTranslateKey()} Returned By: KeyAgreement.generateSecret(), KeyAgreementSpi.engineGenerateSecret(), KeyGenerator.generateKey(), KeyGeneratorSpi.engineGenerateKey(), SecretKeyFactory.{generateSecret(), translateKey()}, SecretKeyFactorySpi.{engineGenerateSecret(), engineTranslateKey()}
This class defines an API for translating a secret key between its opaque SecretKey representation and its transparent javax.crypto.SecretKeySpec representation. It is much like java.security.KeyFactory, except that it works with secret (or symmetric) keys rather than with public and private (asymmetric) keys. SecretKeyFactory is algorithm-independent and provider-based, so you must obtain a SecretKeyFactory object by calling one of the static getInstance() factory methods and specifying the name of the desired secret-key algorithm and, optionally, the name of the provider whose implementation is desired. The "SunJCE" provider provides SecretKeyFactory implementations for the "DES", "DESede", and "PBEWithMD5AndDES" algorithms. Once you have obtained a SecretKeyFactory, use generateSecret() to create a SecretKey from a java.security.spec.KeySpec (or its subclass, javax.crypto.spec.SecretKeySpec). Or call getKeySpec() to obtain a KeySpec for a Key object. Because there can be more than one suitable type of KeySpec, getKeySpec() requires a Class object to specify the type of the KeySpec to be created. See also DESKeySpec, DESedeKeySpec, and PBEKeySpec in the javax.crypto.spec package.
Returned By: SecretKeyFactory.getInstance()
This abstract class defines the service-provider interface for SecretKeyFactory. A cryptographic provider must implement a concrete subclass of this class for each type of secret key it supports. Applications never need to use or subclass this class.
Passed To: SecretKeyFactory.SecretKeyFactory()
Signals that an output buffer is too short to hold the results of an operation.
Hierarchy: Object-->Throwable(Serializable)-->Exception-->java.security.GeneralSecurityException-->ShortBufferException Thrown By: Cipher.{doFinal(), update()}, CipherSpi.{engineDoFinal(), engineUpdate()}, KeyAgreement.generateSecret(), KeyAgreementSpi.engineGenerateSecret(), Mac.doFinal() Copyright © 2001 O'Reilly & Associates. All rights reserved. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|