The Architecture of Security Providers
The Provider Class
The Security Class
The Architecture of Engine Classes
Summary
The cryptographic engines in Java that provide for digital signatures, message digests, and the like are provided as a set of abstract classes in the Java security package. Concrete implementations of these classes are provided by Sun in the JDK, and you also have the option of obtaining third-party implementations of these engines. All of this is made possible through the security provider infrastructure. The provider infrastructure allows concrete implementations of various classes in the security package to be found at runtime, without any changes to the code. In terms of programming, the infrastructure provides a consistent API that can be used by all programs, regardless of who is providing the actual implementation.
Like many other tools discussed in this book, security providers are useful only to developers and users of Java applications. Java-enabled browsers do not implement the security provider infrastructure, nor do they implement any of the cryptographic engines we discuss in the remainder of this book. On the other hand, one of the key features of the Java Plug-in for Internet Explorer and Netscape Communicator is that it does implement the entire security provider infrastructure for use within a browser (subject to the restrictions that might be in place by the access controller and security manager). All the features discussed in this chapter are available in both Java 1.1 and 1.2,[1] with some slight differences we'll mention.
[1]1.2 is now Java 2.
In terms of actual programming, the classes we're going to examine in this chapter are rarely used--hence, we will not delve much into programming. For most developers, end users, and administrators, this chapter focuses on the architecture of the security provider, since that gives us the ability to substitute new implementations of the cryptographic engines we'll use in the rest of the book. Following that discussion, we'll move into the implementation of the architecture, for those readers who are interested in the details.
The security provider abstracts two ideas: engines and algorithms. In this context, "engine" is just another word for operation; there are certain operations the security provider knows about, and in Java, these operations are known as engines. An algorithm defines how a particular operation should be executed. An algorithm can be thought of as an implementation of an engine, but that can lead to confusion, because there may be several implementations of an algorithm.
As a simple example, the Java security package knows about message digests. A message digest is an engine: it is an operation a programmer can perform. The idea behind a message digest is independent of how any particular message digest may be calculated. All message digests share certain features, and the class that abstracts these common features into a single interface is termed an engine. Engines are generally abstract, and are always independent of any particular algorithm.
A message digest may be implemented by a particular algorithm, such as MD5 or SHA. An algorithm is generally provided as a concrete class that extends an abstract engine class, completing the definition of the class. However, there may be many classes that provide a particular algorithm; you may have an SHA class that came with your Java platform, and you may also have obtained an SHA class from a third party. Both classes should provide the same results, but their internal implementations may be vastly different.
Security providers are the glue that manages the mapping between the engines used by the rest of the security package (such as a message digest), the specific algorithms that are valid for those engines (such as an SHA digest), and the specific implementations of that algorithm/engine pair that might be available to any particular Java virtual machine. The goal of the security provider interface is to allow an easy mechanism where the specific algorithms and their implementations can be easily changed or substituted. The security provider allows us to change the implementation of the SHA digest algorithm that is in use, and to introduce a new algorithm to generate a digest.
Hence, a typical programmer only uses the engine classes to perform particular operations. You don't need to worry about the classes that actually perform the computation. The engine classes provide the primary interface to the security package.
The architecture surrounding all of this has these components:
These classes come with the Java virtual machine as part of the core API.
At the basic level, there is a set of classes that implement particular algorithms for particular engines. A default set of these classes is provided by the supplier of the Java platform, and other third-party organizations (including your own) can supply additional sets of algorithm classes. These classes may implement one or more algorithms for one or more engines; it is not necessary for a set of classes from a particular vendor to implement all possible algorithms or all possible engines. A single algorithm class provides a particular algorithm for a particular engine.
Each set of algorithm classes from a particular vendor is managed by an instance of the class Provider. A provider knows how to map particular algorithms to the actual class that implements the operation.
The Security class maintains a list of the provider classes and consulting each in turn to see which operations it supports.
In later chapters, we'll look at the individual algorithms and engines of this architecture; for now, we'll discuss the Provider and Security classes. These two classes together make up the idea of a security provider.
The security providers rely on cooperation between themselves and the rest of the Java security package in order to fulfill their purpose. The details of this cooperation are handled for us--when we use the MessageDigest class to generate a digest, for example, it's the responsibility of the MessageDigest class to ask the Security class which particular class to use to generate the digest. The Security class in turn asks each of the providers whether or not they can supply the desired digest.
So a typical program that wants to use the security package does not interact directly with the security provider. Instead, the security provider provides its usefulness transparently to the programmer and to the end user. An end user, a system administrator, or a developer can configure the security provider; this is a result of the security provider being based on a set of provider classes. While there is a default provider class, the end user or system administrator can replace the default provider with another class. In addition, a user or programmer can augment the default provider class by adding additional provider classes.
When the security package needs to perform an operation, it constructs a string representing that operation and asks the Security class for an object that can perform the operation with the given algorithm. For example, the idea of generating a message digest is represented by a particular engine; its name (i.e., MessageDigest) is the first component in the request to the security provider. There can be many algorithms that can provide a message digest. SHA-1 and MD5 are the two most common, though we'll explore other possibilities when we look in depth at the corresponding classes that handle digests in Java. So the name of the algorithm (e.g., MD5) forms the second component of the string provided to the security class. These components are concatenated into a single string separated by a dot (e.g., MessageDigest.MD5).
Nine cryptographic engines are supported in the Java security package. In addition, thirteen cryptographic algorithms are common enough to have standard names recognized by the Java security package. However, not every algorithm can be used to perform every operation; the valid combinations Java supports are listed in Table 8-1. Italicized entries are operations that the Java security specification defines as legal, but are not implemented by the default security provider.
Engine |
Algorithm Name |
---|---|
AlgorithmParameters |
DSA |
AlgorithmParameterGenerator |
DSA |
CertificateFactory |
X509 |
KeyFactory |
DSA |
KeyPairGenerator |
DSA |
KeyPairGenerator |
RSA |
KeyStore |
JKS |
MessageDigest |
MD5 |
MessageDigest |
SHA-1 |
MessageDigest |
MD2 |
SecureRandom |
SHA1PRNG |
Signature |
DSA[2] |
Signature |
MD2/RSA |
Signature |
MD5/RSA |
Signature |
SHA-1/RSA |
[2]This becomes SHA/DSA in Java 1.2, though DSA is still accepted.
The names in this table are the strings passed to the security provider in order for it to find the class implementing the operation. In addition, the security provider can be passed certain alias strings that map an alias to one of these valid strings. For example, although the standard name of the secure hash algorithm is SHA-1 (to distinguish it from SHA-0, the first such algorithm, which is now obsolete), this algorithm is often referred to as SHA. So while
MessageDigest.SHA-1
is a valid string to pass to the security provider, there is a way to construct alias strings so that the alias refers to the original algorithm. Such a string has the form:
Alg.Alias.MessageDigest.SHA
This string specifies to the security provider that SHA is a valid name for the message digest operation implemented by this provider. We'll see an example of this alias in use when we discuss the Provider class.
A word about the algorithm names in Table 8-1: Though the documentation for the Java security package talks about these algorithm names as the valid names that are supported by Java, that notion is not very helpful. As the entries in italics show, not all pairs of engines and algorithm names are provided by the default JDK. So, even though it's reasonable to ask the Java security package for an engine that provides digital signatures using an RSA algorithm, you won't be successful in obtaining such an engine unless you've installed special software to provide it. Similarly, although these are the supported algorithm names, there's nothing that prevents us from using another name to refer to a new algorithm. If you develop a new algorithm that performs a message digest operation, you can give that algorithm whatever name you like and use that name freely within the Java security package.
As it happens, there are many standard algorithms that have well-known names which are not included in the set of names that the Java security specification defines; there are some six to eight well-known message digest algorithms even though the Java documentation mentions only three of them. Nothing prevents you from using any of these algorithms.
In fact, the default security provider in Sun's provider uses other names for the algorithms it does implement, although those names are undocumented. On the other hand, it is not very useful to have arbitrary names for algorithms; these other names that the Sun provider uses are known as OID names. OID stands for Object IDentifier and is a way that some algorithm names are standardized by the U.S. government. If you're used to dealing with algorithm definitions at that level, rest assured that the Sun provider has aliases for them, but for our purposes, we'll stick with the default names.
When the Java virtual machine begins execution, it is responsible for consulting the user's properties in order to determine which security providers should be in place. These properties must be located in the file $JAVAHOME/lib/security/java.security. In the reference release of the JDK, this file contains this line (among others):
security.provider.1=sun.security.provider.Sun
This line tells us that there is at least one provider class that should be consulted, and that class should be an instance of the sun.security.provider.Sun class.
Each provider given in this file must be numbered, starting with 1. If you want to use an additional provider, you can edit this file and add that provider at the next number. Say that you obtain a security provider from XYZ Corporation. When you obtain this provider, you are told that the provider's class name is com.xyz.XYZProvider; hence, you add this line to the java.security file:
security.provider.2=com.xyz.XYZProvider
Note that there's no reason why the new provider class had to be added at position 2--it would have been perfectly acceptable to add the XYZProvider class as security.provider.1 if the sun.security.provider.Sun class were changed to security.provider.2 (or, alternately, removed altogether). The Security class keeps the instances of the providers in an array so that each class is found at the index specified in the java.security file. As long as the providers in the java.security file begin with 1 and are numbered consecutively, they may appear in any order.
The numbers in this example are significant; when the Security class is asked to provide a particular engine and algorithm, it searches the listed providers in order to find the first one that can supply the desired operation. All engine classes use the security class to supply objects. When the message digest engine is asked to provide an object capable of generating SHA message digests, the engine will ask the Security class which provider to use. If the first provider in the list can perform SHA message digests, that provider will be used. Otherwise, the second provider is checked, and so on, until there are no providers left (and an exception is thrown) or until a provider that implements the desired operation is found. Hence, the number that follows the security.provider string indicates the order in which providers will be searched for particular implementations.
For end users and administrators, that's all there is to adding new security providers. For developers, there is also a programmatic way in which a security provider may be added; we'll explore that when we discuss the interface of the Security class. But as we mentioned earlier, the programmatic interface provided by the two classes we're about to discuss is not often needed; you'd need them only if you wanted to supply your own security provider, or if you wanted to inspect or set programmatically the list of existing providers. Otherwise, the classes are interesting only because they are used by the engine classes we'll begin to examine in the next chapter.
Copyright © 2001 O'Reilly & Associates. All rights reserved.