11.1 The JCA and JCE Frameworks

 <  Day Day Up  >  

The J2EE and J2SE APIs and runtime environments are shipped with security- related classes. The set of core classes in the Java 2 platform can be divided into two subsets :

  1. Security-related core classes, which can be further subdivided as those related to access control and cryptography, respectively

  2. Other core classes, which can be further subdivided as those providing message digest, digital signature, and certificate management; and those providing encryption, key exchange, and message authentication code (see Section 10.2.2.4 on page 356).

The first set of cryptography-related core classes is part of the JCA; the second set, part of the JCE. Together, the JCA and the JCE provide a platform-independent cryptography API.

Originally, the JCE was released separately as a standard extension to the Java 2 SDK, in accordance with the U.S. export control regulations. Starting with the Java 2 SDK V1.4, the JCE is shipped as part of the core classes in the package javax.crypto and its subpackages javax.crypto.interfaces and java.crypto.spec . However, at that time, only a weak-encryption version of the JCE could be exported outside the United States, whereas a strong-encryption version can now be exported too, as long as proper protection mechanisms are in place. [1]

[1] Cipher strength is controlled by the size of the key used in the encryption algorithm. Symmetric encryption is defined to be weak if the key length is 40 bits or less. A key of this size can be cracked in a matter of hours with quite modest computing facilities. Each extra bit doubles the key space, so a key size of 64 bits is 16 million times tougher than 40 bits. A similar rule applies to public-key encryption, where a 512-bit modulus is inadequate, but a 1,024-bit modulus is expected to remain effective for the next 10 years , at least for commercial use.

11.1.1 Terms and Definitions

A few terms need to be explained in order to become familiar with the JCA and JCE. These terms are engine, algorithm, and provider.

  • Engine. This term is used to depict an abstract representation of a cryptographic service that does not have a concrete implementation. A cryptographic service is always associated with a particular algorithm and can have one of the following functions:

    • To provide cryptographic operations, such as those for digital signatures or message digests

    • To generate or supply the cryptographic material ”keys or parameters ”required for cryptographic operations

    • To generate and manage data objects, such as certificates or databases of keys and certificates, called keystores , that encapsulate cryptographic keys in a secure fashion

    Message digests (see Section 10.2.2.4 on page 356) and digital signatures (see Section 10.3.3 on page 370) are examples of engines. In the JCA and JCE, engines are represented by classes called engine classes . Users of the JCA and JCE API request and use instances of the engine classes to carry out corresponding operations.

  • Algorithm. An algorithm can be looked on as an implementation of an engine. For instance, the MD5 algorithm is one of the implementations accessible through the java.security.MessageDigest engine class, which provides access to the functionality of a message digest regardless of the underlying algorithm. The internal implementation of the MD5 algorithm can vary depending on the source that provides the MD5 algorithm class.

  • Provider. The term cryptographic service provider (CSP), or simply provider , refers to a package or a set of packages that supply a concrete implementation of a subset of the cryptographic services supported by the Java security API. These packages must implement one or more cryptographic services, such as digital signature, message digest, and key conversion. Although every provider can choose how to implement a particular cryptographic service, the API exposed must be the same. Each set of algorithm classes from a particular source is managed by an instance of the java.security.Provider class. Installed providers are listed in the java.security properties file present in the lib/security subdirectory of the Java home directory.

From this brief discussion, one can see that to form a complete provider package, cryptographic solutions require a whole collection of tools and functions, which include not only the encryption algorithms themselves but also functions for message digests, certificate management, and key generation.

11.1.2 The Principles of JCA and JCE

The JCA and the JCE are frameworks for accessing and developing cryptographic functionality for the Java platform. This functionality encompasses the parts of the Java 2 security API related to cryptography. The JCA and the JCE were designed around four principles: implementation independence, implementation interoperability, algorithm independence, and algorithm extensibility.

11.1.2.1 Implementation Independence

Implementation independence allows a Java program to use cryptographic functions without having to deal with their implementation. This is achieved by using a provider-based architecture. The JCA and the JCE allow any number of vendors to register their own implementations of the algorithms. For example, if a particular application uses the MD5 implementation supplied by provider A and if it is later decided that the implementation supplied by provider B would be more appropriate, the application code does not need to be changed. Providers can be configured declaratively . Therefore, the choice of one provider over another does not influence the code of an application (see Figure 11.1).

Figure 11.1. Implementation Independence

graphics/11fig01.gif

The provider infrastructure permits implementations of various algorithms to be found at runtime, without any changes to the code. Thanks to the principle of implementation independence, providers may be updated transparently to the application: for example, when faster or more secure versions are available.

11.1.2.2 Implementation Interoperability

Implementation interoperability means that various implementations can work with one another, use one another's keys, or verify one another's signatures. For example, if user Alice signs a document using a program that relies on the DSA implementation supplied by provider A , user Bob can verify the authenticity of that signature with his own program, even if it relies on the DSA implementation supplied by provider B (see Figure 11.2). Similarly, for the same algorithm, a key generated by one provider would be usable by another.

Figure 11.2. Implementation Interoperability

graphics/11fig02.gif

11.1.2.3 Algorithm Independence

Algorithm independence is achieved by defining types of cryptographic services and introducing classes that provide the functionality of these cryptographic services. These classes are called engine classes . An engine class defines API methods that allow applications to access the specific type of cryptographic service it provides. The implementations supplied by providers implement the corresponding SPI classes. Examples of engine classes are the MessageDigest , Signature , and KeyFactory classes in package java.security ; the corresponding SPI classes are MessageDigestSpi , SignatureSpi , and KeyFactorySpi , still in package java.security . Representing all functions of a given type by a generic engine class masks the idiosyncrasies of the algorithm behind a standardized Java class behavior. Thanks to the principle of algorithm independence, implementations of various algorithms providing the same cryptographic functions must expose the same API.

For example, an implementation of the MD5 algorithm and an implementation of SHA-1 need to expose the same API because, even though MD5 and SHA-1 are different algorithms, they both represent the same cryptographic service (see Figure 11.3). Application code invokes the MessageDigest API class, specifying the desired algorithm. The MessageDigest API class transparently invokes the MessageDigestSpi class supplying the implementation for the specified algorithm.

Figure 11.3. Algorithm Independence

graphics/11fig03.gif

As Figure 11.3 shows, application code needs to interact only with API engine classes. Providers transparently supply the various cryptographic service implementations through the SPI provider classes.

If an application needs to generate MD5 message digests, the getInstance() factory method in the MessageDigest API can be invoked, specifying, for example, that the MD5 algorithm should be used. If the application developer later decides that SHA-1 should be used in place of MD5, the call to getInstance() should be changed to reflect the requirement for SHA-1, but all the other MessageDigest method calls can stay the same.

11.1.2.4 Algorithm Extensibility

Algorithm extensibility (Figure 11.4) means that new algorithms that fit in one of the supported engine classes can easily be added. For example, if a new message-digest algorithm is invented and an implemenentation of that algorithm becomes available, that implementation can be plugged into the JCA and JCE frameworks as long as it is compliant with the MessageDigest API.

Figure 11.4. Algorithm Extensibility

graphics/11fig04.gif

11.1.3 JCA and JCE Providers

The concept of provider is essential in the JCA and the JCE. In this section, we look at the design behind the concept of provider and study how to manage providers in the Java language.

11.1.3.1 Design

A framework that supports multiple underlying implementation modules needs to be coupled with the supported modules in some fashion. The coupling can be very rigid or very flexible and capable of selecting more than one module for use. At one extreme is a monolithic framework that is so tightly bound to a single module as to preclude the use of other modules or even different implementations of the same module. At the other end of the spectrum is a highly extensible and configurable framework offering seamless and near-effortless pluggability of different modules and their implementations.

The JCA and the JCE are examples of the latter type of framework. They use a CSP infrastructure to support various implementations of cryptographic algorithms and other security mechanisms. The CSP architecture was introduced in the Java 2 SDK V1.2.

Modules in a framework provide services that are used by the framework and ultimately by applications that use the framework. Therefore, the framework has to interface with its pluggable modules. The framework/module interface forms the basic coupling between the framework itself and a module.

For pluggability, extensibility, and module independence, the JCA and JCE provider architectures use SPIs. An SPI is a set of Java-language interfaces and abstract classes used to provide the implementation of one or more cryptographic services. JCA and JCE providers are pluggable modules, and each of them provides concrete implementations of some SPI methods.

The design of the SPI depends on the kind of framework being developed. The design dictates whether a module implements all or a subset of the SPI. The design also determines the granularity of pluggable modules. Object-oriented and Java-language class and interface design principles play a major role in the design of the SPI.

The set of SPIs used by the JCA and JCE is very granular. The java.security package and its subpackages contain many SPI interfaces that pluggable JCA security providers can implement. Similarly, the javax.crypto package and its subpackages contain many SPI interfaces that pluggable JCE security providers can implement.

11.1.3.2 Implementation

The JCA Provider class in the java.security package defines the concept of a provider. This abstract class must be subclassed by specific providers. The constructor of a Provider class sets the values of various properties that are required for the Java security API to look up the algorithms or other facilities implemented by the provider. Each Provider class instance has a case-sensitive name , a version number, and a string description of the provider and its services. These three pieces of information can be obtained by calling the methods getName() , getVersion() , and getInfo() , respectively. Additionally, the Provider class has methods for accessing information about the implementations of the algorithms, such as key generation, conversion and management facilities, signature generation, and message digest creation.

A provider is said to be a main provider if it implements all the SPI methods. Every provider must exhibit a master class , which is a subclass of java.security.Provider . The only requirement of a master class is that it must have a default constructor so that it can be loaded by the JCA and JCE infrastructure when the JVM starts up. The essential function of a master class is to define property/value pairs, in which each property is an SPI label and the corresponding value is the name of a class that implements that SPI.

For each cryptographic service, a particular implementation is requested and instantiated by calling a getInstance() factory static method on the corresponding engine class, specifying the name of the desired algorithm and, optionally , the name of the Provider whose implementation is desired. If none is specified, getInstance() relies on the java.security.Security class to search the registered providers for an implementation of the requested cryptographic service associated with the named algorithm. In any JVM, providers are installed in a given preference order specified in the java.security file. Listing 11.1 shows the frag ment of a java.security file enumerating all the providers installed in a Java 2 Runtime Environment (J2RE) V1.4.

Listing 11.1. Fragment of a java.security File Listing the Providers Installed on a J2RE
 security.provider.1=com.ibm.jsse.IBMJSSEProvider security.provider.2=com.ibm.crypto.provider.IBMJCE security.provider.3=com.ibm.security.jgss.IBMJGSSProvider security.provider.4=com.ibm.security.cert.IBMCertPath 

This java.security file fragment enables the following four IBM providers:

  1. IBMJSSEProvider , supplying the Java implementation of the Secure Sockets Layer and Transport Layer Security protocols for use by applications importing the Java Secure Socket Extension API (see Chapter 13 on page 449)

  2. IBMJCE , supplying the implementation for the cryptographic services supported by the JCA and JCE

  3. IBMJGSSProvider , supplying the implementation for Generic Security Services (see Section 9.5 on page 340)

  4. IBMCertPath , supplying the implementation for CertPath (see Section 1.1.4 on page 8)

The order in which the providers are enumerated in the java.security file is also the one in which the Security class searches them when no specific provider is requested. If the implementation is found in the first provider, that implementation is used. If it is not found, an implementation is searched for in the second provider, and so on. If an implementation is not found in any provider, a java.security.NoSuchAlgorithmException is raised. Calls to getInstance() methods that include a Provider argument enable developers to specify from which provider they want an algorithm. A program can also obtain an array of all the installed Provider s using the java.security.Security.getProviders() static method; the program can then choose a Provider from the returned array. Alternatively, it is possible to invoke the Security.getProvider() static method, which returns the Provider with the name specified in the argument or null if the specified Provider is not found.

Listing 11.2 enumerates all the providers installed on your Java 2 SDK system and shows for each of them the name, version number, and general information on the cryptographic services supported and the algorithms implemented.

Listing 11.2. GetProviderInfo.java
 import java.security.Provider; import java.security.Security; class GetProviderInfo {    public static void main(String[] args)    {       System.out.println           ("Providers installed on your system:");       System.out.println("-------------------------------");       Provider[] providerList = Security.getProviders();       for (int i = 0; i < providerList.length; i++)       {          System.out.println("[" + (i + 1) +             "] - Provider name: " +             providerList[i].getName());          System.out.println("Provider version number: " +             providerList[i].getVersion());          System.out.println("Provider information:\n" +             providerList[i].getInfo());          System.out.println("----------------------------");       }    } } 

As you can see, the GetProviderInfo Java program uses the getProviders() method in the Security class and builds an array of Provider objects with all the providers installed on the system. Then, on each Provider object, the program invokes the methods getName() , getVersion() , and getInfo() to get the provider's name, version number, and general information, respectively. Listing 11.3 shows how you can discover some more information by adding the following lines of code to the for loop of GetProviderInfo.java .

Listing 11.3. Additional Code to Be Added to GetProviderInfo.java
 Enumeration properties = providerList[i].propertyNames(); while (properties.hasMoreElements()) {    String key, value;    key = (String) properties.nextElement();    value = providerList[i].getProperty(key);    System.out.println("Key: " + key + " - Value: " + value); } 

These additional lines of code make use of the fact that the Provider class extends java.util.Properties and so inherits the propertyNames() method, which returns a java.util.Enumeration object. The while loop goes through all the properties of the Provider objects installed on the system and prints a list of the keys and values, from which you can understand the cryptographic services supported by the providers installed on your system and the algorithms implemented. For this code to work, GetProviderInfo.java must also import java.util.Enumeration .

Let us now consider a scenario in which an application needs an implementation of the MD5 message-digest algorithm. The application will typically obtain an instance of the MessageDigest engine class and pass the java.lang.String "MD5" as the argument to the getInstance() factory method:

 MessageDigest md = MessageDigest.getInstance("MD5"); 

Internally, the getInstance() method asks the Security class to supply the required object. As no specific Provider has been passed as an additional argument to the getInstance() method, the Security class in turn asks all the providers in the sequence they are listed in the java.security file, until a provider implementing the requested algorithm is found. A provider manages the individual algorithm classes. When the first provider in the list receives the request from the Security class, two things can happen.

  1. If it has an implementation of the MD5 message-digest algorithm, the provider will reply to the Security class with the requested algorithm class name.

  2. If the provider does not have an implementation for the MD5 message digest algorithm, the Security class will ask the second provider in the list, and so on, until a provider with the requested implementation is found, if any. If the Security class cannot find any implementation of the MD5 message-digest algorithm, a NoSuchAlgorithmException is raised.

If an MD5 implementation has been found by one of the providers in the list, the Security class passes the MD5 implementation to the getInstance() method of the MessageDigest class, which returns a MessageDigest object, md . The MessageDigest object is now ready to be used. For example, if an array of bytes, say, inputData , has to be hashed into a digest using the MD5 algorithm, the update() method for the MessageDigest object will be used:

 md.update(inputData); 

To compute the digest value, the digest() method for the MessageDigest object will be used:

 byte[] digest = md.digest(); 

This way, we have demonstrated how the provider architecture allows for implementation and algorithm independence in the case of the message digest cryptographic service. The same procedure is adopted with any other cryptographic service, such as digital signature and key-pair generation. Figure 11.5 shows how vendor and algorithm independence are achieved when a particular Java application requests the implementation of a key-pair generation algorithm.

Figure 11.5. Implementation and Algorithm Independence

graphics/11fig05.gif

11.1.3.3 Configuration and Management

Providers can be installed by first copying the provider package into the file system and then configuring the provider.

Copy the Provider Package

Simply place the JAR file(s) containing the provider library classes anywhere on the application class path or even on the boot class path. However, the best solution is to supply the provider library as an installed or bundled extension, by placing the JAR file(s) in the extension class path .

Configure the Provider

For this, you simply need to add the provider to your list of approved providers. This can be done in two ways.

  1. Static configuration. A provider configuration can be done statically by adding the provider to the security provider list in the java.security file. The order number with which the provider is added to the list is very important; if an implementation is supplied in multiple providers, the implementation of the provider with the higher preference ”corresponding to the lower order number ”is chosen by the JVM. In the same way, a provider is removed by simply deleting the entry corresponding to it in the java.security file.

    The configuration information in the java.security file is static insofar as it is effective in a Java runtime. Like most text- file-based configuration information, it is read once when the runtime starts. Any subsequent modification to the file is ignored until after the Java runtime has been restarted.

  2. Dynamic configuration. Fortunately, the JCA and JCE provider infrastructures enable dynamic configuration of master provider modules. In fact, providers may also be registered dynamically, after the Java runtime has started up. To do so, it is necessary to call either the addProvider() or the insertProviderAt() static method in the Security class. The addProvider() method adds a new provider at the end of the list of the installed providers. On the other hand, the insertProviderAt() method adds a new provider at a specified position in the array of providers. If the given provider is installed in the requested position, the provider that used to be at that position, and all the providers with a position greater than that, are shifted up one position, toward the end of the list of the installed providers. Both methods return the preference position in which the provider was added, or -1 if the provider was not added because it was already installed.

    If the preference position of a provider has to be changed, the provider must be first removed and then inserted back at the new preference position. A provider can be removed by calling the removeProvider() method of the Security class.

    Note that the dynamic provider registration is not persistent and can be done only by trusted programs or, in other words, programs that have been granted the necessary Permission s (see Section 8.2 on page 258). To add a provider whose name is indicated with name or to insert it in a specified position in the list, the Permission required is a java.security.SecurityPermission "insertProvider. name " . To remove it, the Permission required is a java.security.SecurityPermission "removeProvider. name " .

11.1.4 Engine and SPI Classes

The provider architecture of JCA and JCE has been designed to allow implementation and algorithm independence. This way, implementations of various cryptographic services can be found at runtime, without any changes to the code. For this reason, abstract representations of cryptographic services are offered through generic engine classes. The engine classes are the interfaces between the user code and the implementations of the underlying algorithms offered by the installed providers, as shown in Figure 11.6.

Figure 11.6. User Code, Engine Classes, and Providers

graphics/11fig06.gif

The following engine classes are defined on the Java 2 platform as part of the JCA framework:

  • java.security.MessageDigest . Used to calculate the message digest (hash) of specified data.

  • java.security.Signature . Used to digitally sign data and verify digital signatures.

  • java.security.KeyPairGenerator . Used to generate a pair of public and private keys suitable for a specified algorithm.

  • java.security.KeyFactory . Used to convert keys ” opaque cryptographic keys of type java.security.Key ”into key specifications ”transparent representations of the underlying key material ”and vice versa.

    An opaque key representation is one in which you have no direct access to the key material that constitues a key. Therefore, an opaque key representation gives you limited access to the key itself. An encoded form of the key is available, but the parameters used to generate the key are not. A transparent key representation is one in which you can access each parameter value in the set individually, through one of the get methods defined in the corresponding specification class. For example, both classes RSAPublicKeySpec and RSAPrivateKeySpec in package java.security.spec define a method getModulus() to access the modulus n used in the RSA algorithm. In addition, RSAPublicKeySpec defines method getPublicExponent() to allow access to the public exponent e , and RSAPrivateKeySpec defines method getPrivateExponent() to allow access to the private exponent d .

    A KeyFactory object can also be used to translate a Key object, whose provider may be unknown or potentially untrusted, into a corresponding Key object of this KeyFactory .

  • java.security.KeyStore . Used to create and manage keystores.

  • java.security.AlgorithmParameters . Used to manage the parameters for a particular algorithm, including parameter encoding and decoding.

  • java.security.AlgorithmParameterGenerator . Used to generate a set of parameters suitable for a specified algorithm.

  • java.security.SecureRandom . Used to generate random or pseudorandom numbers .

  • java.security.certificate.CertificateFactory . Used to create public-key certificates and certificate revocation lists.

  • java.security.cert.CertPathBuilder . Used to build certificate chains.

  • java.security.cert.CertPathValidator . Used to validate certificate chains.

  • java.security.cert.CertStore . Used to retrieve certificates and CRLs from a repository.

In both the JCA and the JCE, an instance of an engine class encapsulates the implementation of a cryptographic service by one of the providers installed on the Java platform. The engine classes defined on the Java 2 platform as part of the JCE are as follows :

  • javax.crypto.Cipher . Provides the functionality of a cryptographic cipher for encryption and decryption.

  • javax.crypto.ExemptionMechanism . Provides the functionality of an exemption mechanism. Programs that use an exemption mechanism may be granted stronger encryption capabilities than those that do not.

  • javax.crypto.KeyAgreement . Provides the functionality of a key-agreement or key-exchange protocol.

  • javax.crypto.KeyGenerator . Provides the functionality of a symmetric key generator.

  • javax.crypto.Mac . Provides the functionality of a MAC algorithm.

  • javax.crypto.SecretKeyFactory . Represents a factory for secret keys.

Each engine class can be instantiated by using the getInstance() static factory method. If you pass this method a single argument, it must be the name of the algorithm to be used. In this case, the getInstance() method will ask the Security class to find the first provider in the preference list offering an implementation of that algorithm. Otherwise, you can force this decision and specify two arguments; in this case, along with the algorithm, you will explicitly pass in the Provider name or the Provider instance.

An engine class provides the methods to enable applications to access the specific cryptographic service it provides, independent of the particular type of cryptographic algorithm. The MessageDigest engine class, for example, provides access to the functionality for all message-digest algorithms, such as MD5, SHA-1, and so on. The application interfaces supplied by an engine class are implemented in terms of the corresponding SPI. That is, each engine class has a corresponding abstract SPI class, which defines the methods that cryptographic service providers must implement. The name of each SPI class is the same as that of the corresponding engine class, followed by Spi . For example, the SPI class corresponding to the Signature engine class is SignatureSpi . Each SPI class in the Java core API is abstract. It is the responsibility of a provider to subclass it and supply a concrete implementation for it.

To supply the implementation of a particular type of service, for a specific algorithm, a provider must subclass the corresponding SPI class and supply implementations for all the abstract methods. By convention, the abstract methods in the SPI class all begin with engine and are declared protected . For example, the SignatureSpi class defines abstract methods, such as engineInitVerify() and engineInitSign() . An instance of an engine class ”the API object obtained by calling the getInstance() factory method on the engine class itself ”encapsulates as a private field an instance of the corresponding SPI class, the SPI object . The implementations of the API object's methods invoke the corresponding methods in the encapsulated SPI object. For example, when called by an application, the digest() method of a MessageDigest API object calls the engineDigest() method on the MessageDigestSpi object encapsulated in it. Similarly, when the updateDigest() method of a MessageDigest API object is called, the method calls the engineUpdateDigest() method on the MessageDigestSpi object encapsulated in it. This scenario is shown in Figure 11.7.

Figure 11.7. MessageDigest Engine and SPI Classes

graphics/11fig07.gif

 <  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