home | O'Reilly's CD bookshelfs | FreeBSD | Linux | Cisco | Cisco Exam  


Book Home Java Security Search this book

8.2. The Provider Class

The first class we'll examine in depth is the Provider class (java.security.Provider).

public abstract class Provider extends Properties

This class forms the basis of the security provider architecture. There is normally a standard subclass that implements a default security feature set; other classes can be installed to implement other security algorithms.

In the core Java API, the Provider class is abstract, and there are no classes in the core Java API that extend the Provider class. The default provider class that comes with the reference JDK is the class Sun in the sun.security.provider package. However, since this class is in the sun package, there's no guarantee that it will be available with every implementation of the Java virtual machine.

In theory, this should not matter. The concepts of the security package will work according to the specification as long as the Java implementation provides an appropriate provider class and appropriate classes to perform the operations a Java program will expect. The exact set of classes a particular program may expect will depend, of course, on the program. In the next section, we'll discuss how different implementations of the Provider class may be loaded and used during the execution of the virtual machine.

8.2.1. Using the Provider Class

The Provider class is seldom used directly by a programmer. This class does contain a number of useful miscellaneous methods we'll review here; these methods are generally informational and would be used accordingly.

public String getName()

Return the name of the provider.

public double getVersion()

Return the version number of the provider.

public String getInfo()

Return the info string of the provider.

public String toString()

Return the string specifying the provider; this is typically the provider's name concatenated with the provider's version number.

As an extension of the Properties class, the Provider class also shares its public interface. Beginning in Java 1.2, the Provider class overrides three of those methods:

public synchronized void clear() figure

If permission is granted, clear out all entries from the provider.

public synchronized Object put(Object key, Object value) figure

If permission is granted, add the given property, keyed off the given key.

public synchronized Object remove(Object key) figure

If permission is granted, remove the object associated with the given key.

Permission to perform these last three options is granted if the checkSecurityAccess() method grants permission based on the argument string (which is clearProviderProperties, putProviderProperty, and removeProviderProperty, respectively) as follows: argument + getName().

Since the interface to this class is simple, we won't actually show how it is used, although we will use some of these methods later in this chapter. Note also that there is no public constructor for the Provider class--a provider can only be constructed under special circumstances we'll discuss later.

8.2.2. Implementing the Provider Class

If you're going to provide your own set of classes to perform security operations, you must extend the Provider class and register that class with the virtual machine. In this section, we'll explore how to do that. Most of the time, of course, you will not implement your own Provider class--you'll just use the default one, or perhaps install a third-party provider using the techniques that we explore in the next section.

Although the Provider class is abstract, none of its methods are abstract. This means that implementing a provider is, at first blush, simple: all you need do is subclass the Provider class and provide an appropriate constructor. The subclass must provide a constructor, since there is no default constructor within the Provider class. The only constructor available to us is:

protected Provider(String name, double version, String info)

Construct a provider with the given name, version number, and information string.

Hence, the basic implementation of a security provider is:

Class Definition

public class XYZProvider extends Provider {
	public XYZProvider() {
		super("XYZ", 1.0, "XYZ Security Provider v1.0");
	}
}

Here we're defining the skeleton of a provider that is going to provide certain facilities based on various algorithms of the XYZ Corporation. Throughout the remainder of this book, we'll be developing the classes that apply to the XYZ's cryptographic methods, but they will be examples only--they lack the rigorous mathematical properties that these algorithms must have. In practice, you might choose to implement algorithms that correspond to the RSA algorithms for the cryptographic engines.

Note that we used a default constructor in this class rather than providing a constructor similar to the one found in the Provider class itself. The reason for this has to do with the way providers are constructed, which we'll discuss at the end of this section. When you write a provider, it must provide a constructor with no arguments.

This is a complete, albeit useless, implementation of a provider. In order to add some functionality to our provider, we must put some associations into the provider. The associations will perform the mapping that we mentioned earlier; it is necessary for the provider to map the name of an engine and algorithm with the name of a class that implements that operation. This is why the Provider class itself is a subclass of the Properties class--so that we can make each of those associations into a property.

The operations that our provider will be consulted about are listed in Table 8-2. In this example, we're going to be providing an SHA-1 algorithm for performing message digests, since that would be needed as part of the signature generation algorithm we want to implement. There's no absolute requirement for this; we could have depended on the default Sun security provider to supply this algorithm for us. On the other hand, there's no guarantee that the default security provider will be in place when our security provider is installed, so it's a good idea for a provider to include all the algorithms it will need.

Table 8-2. Properties Included by Our Sample Provider

Property

Corresponding Class

Class Definition

Signature.SHA-1/XYZ

Class Definition

XYZSignature

Class Definition

KeyPairGenerator.XYZ

Class Definition

XYZKeyPairGenerator

Class Definition

MessageDigest.XYZ

Class Definition

XYZMessageDigest

Class Definition

MessageDigest.SHA-1

Class Definition

SHA1MessageDigest

In order to make these associations from this table, then, our XYZProvider class needs to look like this:

Class Definition

public class XYZProvider extends Provider {
	public XYZProvider() {
		super("XYZ", 1.0, "XYZ Security Provider v1.0");
		put("Signature.SHA-1/XYZ", "com.xyz.XYZSignature");
		put("KeyPairGenerator.XYZ", "com.xyz.XYZKeyPairGenerator");
		put("MessageDigest.XYZ", "com.xyz.XYZMessageDigest");
		put("MessageDigest.SHA-1", "com.xyz.SHA1MessageDigest");
		put("Alg.Alias.MessageDigest.SHA", "SHA-1");
	}
}

The only properties a provider is required to put into its property list are the properties that match the engine name and algorithm pair with the class that implements that operation. In this example, that's handled with the first four calls to the put() method (but remember too that the provider can implement as few or as many operations as it wants to; it needn't implement more than a single engine with one algorithm, or it can implement dozens of engine/algorithm pairs). Note that the class name is the fully qualified package name of the class.

The provider also has the opportunity to set any other properties that it wants to use. If the provider wants to set aliases (as we've done with the final call to the put() method, using the syntax we showed earlier), it's free to do so. Our example allows the program using this provider to request an SHA message digest in addition to requesting an SHA-1 digest. Doing this for SHA is highly advisable, since that algorithm is typically referred to as SHA rather than SHA-1, but that's the only common case where that aliasing is needed.

A provider can set any other arbitrary properties that it wants as well. For instance, a provider class could set this property:

Class Definition

put("NativeImplementation", "false");

if it wanted the classes that use the provider to be able to determine if this particular XYZ implementation uses native methods.[3] It can also use the convention that certain properties are preceded with the word Alg and contain the algorithm name, like this:

[3]RSA algorithms often use native methods, because there are existing implementations of them that are written in C and have gone through an extensive quality acceptance test that many commercial sites have a level of confidence in. However, many third-party RSA implementations do not use native methods.

Class Definition

put("Alg.NativeImplementation.XYZ", "false");

There's no advantage to setting any additional properties--nothing in the core JDK will use them. They can be set to make the classes that accompany your provider class easier to write--for example, your XYZSignature class might want to inquire which particular providers have a native method implementation of the XYZ algorithm. Whatever information you put into your provider and how your accompanying classes use that information is a design detail that is completely up to you. The Security class will help you manage the information in these properties; this relationship to the Security class is the reason why we used a string value for the NativeImplementation property rather than a Boolean value.

There's one more nonpublic method of the Provider class that is used by the security API:

static Provider loadProvider(String className)

Instantiate a provider that has as its type the given class. This method is provided mostly for convenience--it simply loads the given class and instantiates it. However, this method also ensures that the loaded class is an instance of the Provider class.

This method creates an instance of a provider. The importance of this method stems from how it performs its task: it creates the instance of the provider object by calling the newInstance() method of the Class class. In order for that operation to succeed, the provider class must therefore have a default constructor--that is, a constructor that requires no arguments. This is why in our example we provided such a constructor and had the constructor hardwire the name, version number, and information string. We could have provided an additional constructor that accepts those values as parameters, but it would never be called, since the only way in which the virtual machine uses providers is to load them via this method.

In the next section, we'll look into the details of how the virtual machine loads those provider classes we want to use.



Library Navigation Links

Copyright © 2001 O'Reilly & Associates. All rights reserved.