11.3 The JCE API

 <  Day Day Up  >  

The JCE framework and implementations for encryption, key generation, key agreement, and MAC algorithms supplement the message digest and digital-signature interfaces and implementations provided in the JCA. Support for encryption includes symmetric, asymmetric, block, and stream ciphers. The software also supports secure streams and sealed ”encrypted ”objects. JCE was previously shipped as a standard extension to the Java 2 SDK V1.2 and 1.3 but has now been integrated into the Java 2 SDK V1.4.

JCE is based on the same design principles found elsewhere in the JCA: implementation independence and interoperability, and algorithm independence and extensibility (see Section 11.1.2 on page 379). Additionally, the JCE uses the same provider architecture (see Section 11.1.3 on page 382). Providers signed by a trusted entity can be plugged into the JCE framework, and new algorithms can be added seamlessly.

The JCE includes two software components :

  1. A framework that defines and supports cryptographic services that providers can supply implementations for. This framework includes everything in the javax.crypto package and its subpackages javax.crypto.spec and javax.crypto.interfaces .

  2. A provider supplying the implementation for a number of cryptographic services.

The JCE API covers

  • Encryption and decryption. The JCE provides support for both symmetric and asymmetric encryption and for password-based encryption (PBE). [2] Encryption and decryption are performed using a cipher. A cipher is an object capable of carrying out encryption and decryption according to an encryption scheme , or algorithm.

    [2] PBE derives an encryption key from a password. In order to make the task of getting from password to key very time consuming for an attacker, most PBE implementations will mix in a random number, known as a salt, to create the key.

  • Key agreement. A key agreement is a protocol by which two or more parties can establish the same cryptographic keys without having to exchange any secret information. An example of a key-agreement protocol is offered by the Diffie-Hellman (DH) algorithm (see Section 10.3.1.2 on page 362).

  • MAC. As we discussed in Section 10.2.2.4 on page 356, a MAC provides a way to check the integrity of information transmitted over or stored in an unreliable medium, based on a secret key. Typically, MACs are used between two parties that share a secret key in order to validate information transmitted between them.

The JCE provider consists of the main package javax.crypto and its two subpackages javax.crypto.spec and javax.crypto.interfaces .

  • The javax.crypto package forms the main body of the JCE class structure. The package consists primarily of classes that represent the concepts of cipher, key agreement, and MAC, and their corresponding SPI classes.

  • The javax.crypto.spec package consists of various key-specification and algorithm parameter specification classes.

  • The javax.crypto.interfaces package contains the DHKey interface and its subinterfaces, DHPrivateKey and DHPublicKey . These are the interfaces for the keys based on the DH algorithm (see Section 10.3.1.2 on page 362). This package contains also the interface PBEKey , for password-based keys.

11.3.1 The javax.crypto.Cipher Class

The javax.crypto.Cipher engine class forms the core of the JCE framework. This class provides the functionality of a cryptographic cipher used for encryption and decryption. Like other engine classes, the Cipher class is instantiated using its getInstance() static factory method. This method takes as argument a String object that represents a transformation. A transformation is a string that describes the operation or set of operations to be performed on the given input to produce some output. A transformation always includes the name of a cryptographic algorithm, which may be followed by a mode and padding scheme. Therefore, a transformation is of the form algorithm / mode / padding or just algorithm . For example, the following is a valid way to create a Cipher object:

 Cipher c = Cipher.getInstance("DES/CBC/PKCS5Padding"); 

This Cipher object will be used for encryption or decryption using the DES algorithm with CBC mode of operation. Algorithms usually operate on blocks having a predefined size. Plaintext packets whose length is not a multiple of that size must be padded according to a specified padding scheme prior to encryption. The Cipher c above uses a padding scheme known as PKCS5Padding (see Section 12.1.2 on page 435).

If mode and padding are not specified, provider-specific default values are used. Optionally, getInstance() can accept, as a second argument, the name of the provider after the transformation parameter.

A Cipher object obtained from getInstance() must be initialized for encryption, decryption, wrap , or unwrap mode . These modes are defined as final int constants in the Cipher class. These four modes can be referenced by their symbolic names : ENCRYPT_MODE , DECRYPT_MODE , WRAP_MODE , and UNWRAP_MODE , respectively. The Cipher will perform a different function, based on its initialized mode.

  • If the Cipher has been initialized in encryption mode, it will perform encryption of data.

  • If the Cipher has been initialized in decryption mode, it will perform decryption of data.

  • If the Cipher has been initialized in wrap mode, it will be used to wrap a java.security.Key object into bytes so that the key can be securely transported.

  • If the Cipher has been initialized in unwrap mode, it will be used to unwrap a previously wrapped key into a java.security.Key object.

A Cipher object is initialized by calling the init() method. When this happens, the object loses all previously acquired states. In other words, initializing a Cipher is equivalent to creating a new instance of that Cipher and initializing it.

Data can be encrypted or decrypted in one step ( single-part operation) or in multiple steps ( multiple-part operation). This depends on whether you call the doFinal() or the update() method, respectively. A multiple-part operation is useful if the exact length of the data is not known in advance or if the data is too long to be stored in memory all at once.

11.3.2 The CipherInputStream and CipherOutputStream Classes in the javax.crypto Package

The JCE introduces the concept of secure streams, which combine a java.io.InputStream or a java.io.OutputStream with a Cipher object. Secure streams are provided by the CipherInputStream and CipherOutputStream classes.

  • CipherInputStream . The javax.crypto.CipherInputStream class is a java.io.FilterInputStream that encrypts or decrypts the data passing through it. The class is composed of an InputStream , or one of its subclasses, and a Cipher . CipherInputStream represents a secure InputStream into which a Cipher object has been interposed. The read() methods of CipherInputStream return data that is read from the underlying InputStream but has additionally been processed by the embedded Cipher object, as shown in Figure 11.10.

    Figure 11.10. Representation of a CipherInputStream

    graphics/11fig10.gif

    Note that the Cipher object must be fully initialized before being used by a CipherInputStream . For example, if the embedded Cipher has been initialized for decryption, the CipherInputStream will attempt to decrypt the data it reads from the underlying InputStream before returning it to the application.

  • CipherOutputStream . The javax.crypto.CipherOutputStream class is a java.io.FilterOutputStream that encrypts or decrypts the data passing through it. The class is composed of an OutputStream , or one of its subclasses, and a Cipher . CipherOutputStream represents a secure OutputStream into which a Cipher object has been interposed. The write() methods of CipherOutputStream first process the data with the embedded Cipher object before writing it out to the underlying OutputStream , as shown in Figure 11.11.

    Figure 11.11. Representation of a CipherOutputStream

    graphics/11fig11.gif

    The Cipher object must be fully initialized before being used by a CipherOutputStream . For example, if the embedded Cipher has been initialized for encryption, the CipherOutputStream will encrypt its data before writing it out to the underlying OutputStream .

The code fragment in Listing 11.12 shows how to

  1. Read data from an input file, inputFile

  2. Encrypt it, using a Cipher object, cipher1 , initialized for the DES algorithm in encrypt mode

  3. Decrypt it, using another Cipher object, cipher2 , initialized for the DES algorithm in decrypt mode

  4. Print the result of the decryption, which is the same as the original plaintext, to an output file, outputFile

The code makes use of the CipherInputStream class to encrypt and decrypt data passing through a FileInputStream .

Listing 11.12. How to Use the CipherInputStream API
 import java.io.FileInputStream; import java.io.FileOutputStream; import java.security.Key; import javax.crypto.Cipher; import javax.crypto.KeyGenerator; import javax.crypto.CipherInputStream; // Other code goes here... // Generate Cipher objects for encryption and decryption. Cipher cipher1 = Cipher.getInstance("DES"); Cipher cipher2 = Cipher.getInstance("DES"); // Generate a KeyGenerator object. KeyGenerator kg = KeyGenerator.getInstance("DES"); // Generate a DES key. Key desKey = kg.generateKey(); // Initialize the Cipher objects. cipher1.init(Cipher.ENCRYPT_MODE, desKey); cipher2.init(Cipher.DECRYPT_MODE, desKey); // Create the encrypting CipherInputStream. FileInputStream fis = new FileInputStream(inputFile); CipherInputStream cis1 = new CipherInputStream(fis, cipher1); // Create the decrypting CipherInputStream. CipherInputStream cis2 = new CipherInputStream    (cis1, cipher2); // Write the decrypted data to output file. FileOutputStream fos = new FileOutputStream(outputFile); byte[] b2 = new byte[1024]; int i2 = cis2.read(b2); while (i2 != -1) {    fos.write(b2, 0, i2);    i2 = cis2.read(b2); } // Close the streams. fos.close(); cis1.close(); cis2.close(); // Other code goes here... 

11.3.3 The javax.crypto.SecretKey Interface

The JCE offers a set of classes and interfaces to manage secret keys. At the root of the hierarchy is the javax.crypto.SecretKey interface, which contains no methods or constants. Its only purpose is to group and provide type safety for secret, or symmetric, keys. Provider implementations of this interface must overwrite the equals() and hashCode() methods inherited from java.lang.Object , so that secret keys are compared on the basis of their underlying key material, not on reference. Because it extends the Key interface (see Section 11.2.2 on page 393), this interface is an opaque representation of a symmetric key.

11.3.4 The javax.crypto.spec.SecretKeySpec Class

The javax.crypto.spec.SecretKeySpec class specifies a secret key in a provider-independent fashion. The class can be used to construct a SecretKey from a byte array, without the need to go through a provider-based SecretKeyFactory . This class is useful only for raw secret keys that have been pregenerated, can be represented as a byte array, and have no key parameters associated with them: for example, DES or Triple-DES keys. This class is a transparent representation of a symmetric key.

11.3.5 The javax.crypto.KeyGenerator Class

As we saw in Section 11.2.6 on page 394, the KeyPairGenerator class is used to generate a pair of public and private keys. The JCE provides for a KeyGenerator engine class, which is used to generate secret keys for symmetric algorithms. KeyGenerator objects are created using the getInstance() factory method of the KeyGenerator class. Note that a factory method is by definition static.

The getInstance() method takes as its argument the name of a symmetric algorithm for which a secret key is to be generated. Optionally, a package provider name may be specified. If only an algorithm name is specified, the system will determine whether an implementation of the requested key-generator algorithm is available in the environment; if there is more than one, the preferred one will be selected. If both an algorithm name and a package provider are specified, the system will determine whether an implementation of the requested key-generator algorithm is in the package requested and throw a NoSuchAlgorithmException if there is not. A KeyGenerator for a particular symmetric-key algorithm creates a symmetric key that can be used with that algorithm and associates algorithm-specific parameters, if any, with the generated key.

11.3.6 The javax.crypto.SecretKeyFactory Class

The javax.crypto.SecretKeyFactory class represents a factory for secret keys. A KeyFactory (see Section 11.2.4 on page 394) is bidirectional, which means that it allows building an opaque Key object from a given key specification ”key material ”or retrieving the underlying key material of a Key object in a suitable format. In general, a KeyFactory is used to convert keys ”opaque cryptographic keys of type Key ”into key specifications ”transparent representations of the underlying key material ”and vice versa. In particular, SecretKeyFactory operates only on secret, or symmetric, keys, whereas a KeyFactory object processes both the public- and the private-key components of a key pair.

Objects of type java.security.Key ”an interface of which PublicKey , PrivateKey (see Section 11.2.3 on page 393) and SecretKey (see Section 11.3.3 on page 421) are subinterfaces ”are opaque key objects; you cannot tell how they are implemented. The underlying implementation is provider dependent and may be software or hardware based. KeyFactory allows providers to supply their own implementations of cryptographic keys. For example, suppose that you have a key specification for a DH public key, consisting of the public value y , the prime modulus q , and the base generator a (see Section 10.3.1.2 on page 362). If you feed the same specification to DH KeyFactory objects from various providers, the resulting PublicKey objects will most likely have different underlying implementations.

A provider should document the key specifications supported by its SecretKeyFactory .

  • The SecretKeyFactory for DES keys supplied by the IBM provider supports the javax.crypto.spec.DESKeySpec class as a transparent representation of DES keys.

  • The SecretKeyFactory for Triple-DES keys supports javax.crypto.spec.DESedeKeySpec as a transparent representation of Triple-DES keys.

  • The SecretKeyFactory for PBE keys supports javax.crypto.spec.PBEKeySpec as a transparent representation of the underlying password.

11.3.7 The javax.crypto.SealedObject Class

This class enables a programmer to create a Serializable Object and protect its confidentiality with a cryptographic algorithm. This provides a way of storing Serializable Object s safely.

Given any Object whose class implements the java.io.Serializable interface, one can create a SealedObject that encapsulates the original Object , in serialized format ”a deep copy of the Object ”and seals, or encrypts, its serialized contents, using a cryptographic algorithm, such as DES, to protect the Object 's confidentiality. The encrypted content can later be decrypted and deserialized, yielding the original Object .

This class provides a variety of options for decrypting a SealedObject and recovering it in its original form. The original Object can be recovered by either passing the same Cipher object appropriately initialized with the same key and algorithm parameters as used for encryption or by passing only the decryption key; in this case, the appropriate Cipher object is automatically created with the decryption key and the same algorithm parameters that were stored in the sealed object.

The code fragment in Listing 11.13, which could be embedded in any J2EE application, illustrates the use of the SealedObject class.

Listing 11.13. How to Use the SealedObject Class
 import java.security.Key; import javax.crypto.Cipher; import javax.crypto.KeyGenerator; import javax.crypto.SealedObject; // Other code goes here... // Generate Cipher objects for encoding and decoding. Cipher cipher1 = Cipher.getInstance("DES"); Cipher cipher2 = Cipher.getInstance("DES"); // Generate a KeyGenerator object. KeyGenerator kg = KeyGenerator.getInstance("DES"); // generate a DES key. Key desKey = kg.generateKey(); // Initialize the Ciphers for encryption and decryption. cipher1.init(Cipher.ENCRYPT_MODE, mykey); cipher2.init(Cipher.DECRYPT_MODE, mykey); // Seal a String object. SealedObject s = new SealedObject    ("SSN 123-456-7890", cipher1); // Recover the sealed String object String s1 = (String) s.getObject(cipher2); System.out.println ("The sealed object is: " + s1); // Other code goes here... 

11.3.8 The javax.crypto.KeyAgreement Class

Whenever two or more parties decide to initiate a secure conversation over a nonsecure communication channel using secret-key encryption, they need to use the same secret key ”which is called the session key ”without transmitting it in the clear over the channel. To achieve this result, public-key encryption can be used to transmit the session key securely.

An alternative is to use a key agreement , a protocol that allows two or more parties to calculate the same secret value without exchanging it directly. Therefore, the parties share the same secret key and can encrypt the communication using symmetric encryption. The most famous of these protocols is the DH algorithm (see Section 10.3.1.2 on page 362).

The javax.crypto.KeyAgreement class provides the functionality of a key-agreement protocol. The keys involved in establishing a shared secret key are created by KeyPairGenerator or KeyGenerator , a KeyFactory , or as a result from an intermediate phase of the key-agreement protocol.

Each party involved in the key agreement has to create a KeyAgreement object. This can be done using the getInstance() factory method of the KeyAgreement engine class. This method accepts as its argument a String representing a key-agreement algorithm as parameter. Optionally, you can specify a provider as the second argument.

  • If only an algorithm name is specified, the system will determine whether an implementation of the requested key-agreement algorithm is available in the environment; if there is more than one, the preferred one will be selected.

  • If both an algorithm name and a package provider are specified, the system will determine whether an implementation of the requested key-agreement algorithm is in the package requested and throw a NoSuchAlgorithmException if there is not.

If the DH algorithm is used, a DH private key is used to initialize the KeyAgreement object. Additional initialization information may contain a source of randomness and/or a set of algorithm parameters.

Every key-agreement protocol consists of a number of phases that need to be executed by each party involved in the key agreement. The doPhase() method is used to execute the next phase in the key agreement. This method takes two arguments: a Key and a boolean .

  1. The Key argument contains the key to be processed by that phase. In most cases, this is the public key of one of the other parties involved in the key agreement or an intermediate key that was generated by a previous phase. The doPhase() method may return an intermediate key that you may have to send to the other parties of this key agreement, so they can process it in a subsequent phase.

  2. The boolean parameter specifies whether the phase to be executed is the last one in the key agreement. A value of false indicates that this is not the last phase of the key agreement, and that more phases are to follow. A value of true indicates that this is the last phase of the key agreement and that the key agreement is completed.

After each party has executed all the required key-agreement phases, the key agreement can compute the shared secret by calling the generateSecret() method.

11.3.9 The javax.crypto.Mac Class

The Mac class provides the functionality of a MAC (see Section 10.2.2.4 on page 356). As with all other engine classes in the API, Mac objects are created using the getInstance() factory methods of the Mac class. A factory method is a static method that returns an instance of a class; in this case, an instance of Mac that provides the requested MAC algorithm. The getInstance() method takes as its argument the name of a MAC algorithm. Optionally, a package provider name may be specified.

  • If only an algorithm name is specified, the system will determine whether an implementation of the requested MAC algorithm is available in the environment; if there is more than one, the preferred one will be selcted.

  • If both an algorithm name and a package provider are specified, the system will determine whether an implementation of the requested MAC algorithm is in the package requested, and throw a NoSuchAlgorithmException if there is not.

 <  Day Day Up  >  


Enterprise Java Security. Building Secure J2EE Applications
Enterprise Javaв„ў Security: Building Secure J2EEв„ў Applications
ISBN: 0321118898
EAN: 2147483647
Year: 2004
Pages: 164

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net