Chapter 2. Java Security Overview

The Java 2 security model is, in general, a reliable way to build secure, distributed Java applications. The Java 2 security model implementation represents a bedrock of components and tools that any serious Java architect or developer must comprehend. We present an overview of Java security history in this chapter. Note that the early Java security features, which have been replaced by Java 2 security features, are largely ignored throughout the remainder of this book.

In this chapter, you will learn about

  • The historical context for the evolving security model in Java.

  • An overview of the Java security model as provided by the Java 2 platform.

  • The role of the JVM's byte code verifier in providing security.

  • The security aspects of the JVM's class loader.

  • The security aspects of the JVM's security manager.

  • The basic cryptographic infrastructure provided with the Java platform.

The History of Security in Java

Java was originally promoted for use by developers to create Java applets. Java applets enable code to be downloaded directly into a Web browser. This technology was one of the first to turn the Web browser into a framework that could support the execution of applications downloaded over the Web. Such a framework promised to provide a new paradigm for computing, in stark contrast to traditional desktop computing. With desktop computing, applications were loaded and executed by you on your machine. Whenever you needed updates to the application software, you had to obtain distributions from sources such as CDs and disks. You then loaded the updates yourself. Java applets promised a new paradigm in which mobile code is downloaded dynamically to the Web browser and automatically updated whenever you revisit the Web site from which the code was downloaded.

However, network performance has helped curb such grand visions by limiting the size of Java applets that users consider reasonable to download and, therefore, limiting the complexity of applications downloaded. Furthermore, the performance of JVM implementations equipped with Web browsers has also hampered the proliferation of Java applets on the Internet. Nevertheless, because Java applets brought mobile code into the limelight, Sun wisely considered security early in the development of the Java APIs. After all, if you are now downloading mobile Java code from some remote Web site onto your local machine, how much access to your local machine do you want to relinquish to such code? Many traditional desktop applications require access to your local file system. Do you really want a Java applet downloaded from some malicious Web site to have access to your file system?

Thus, security was a key consideration for Java from day one. Enterprise applications can also benefit from these security features. Although early models for Java security addressed Java applet issues, Java applications can now take advantage of the more sophisticated security model (available in the Java 2 platform) to enhance the security of distributed enterprise services.

The evolution of the Java security model introduced with each major Java version release is depicted in Figures 2.1 through 2.3. The Java version 1.0 platform provided a very limited security model known as the sandbox model , as shown in Figure 2.1. In the sandbox model, only local code had access to all the valuable resources (for example, files and new network connections) that are exposed and accessed by the Java Virtual Machine (JVM) . Code downloaded from remote sources, such as applets, had access only to a limited number of resources. Thus, file-system access and the capability to create new connections were limited for remote code. This was a primary concern for JVM implementations equipped with Web browsers.

The Java 1.0 sandbox security model.

Figure 2.1. The Java 1.0 sandbox security model.

The Java 1.0 security model was somewhat too restrictive, however. The vision of providing downloadable applications over the Web was being stifled because such applications could not perform key operations such as file access or the creation of new network connections. If Web-browser vendors treated remote code like local code, the path was opened for malicious code to corrupt the local machine. Such an all-or-nothing model was replaced in Java 1.1 when a trusted security model was employed, as depicted in Figure 2.2.

The Java 1.1 trusted code security model.

Figure 2.2. The Java 1.1 trusted code security model.

With the trusted code model, you can optionally designate whether code signed by certain providers would be allowed to have the full resource access it desired. Thus, you might actually trust that some Java code from Microsoft would be able to run inside your browser with full access to your system resources, much as you trust Microsoft when you install one of its many products on your system. Code or applet signing permits a company like Microsoft to sign its applet in such a way that you can verify that this code really came from this company. Thus, the signed applet is granted access to all your system resources, whereas untrusted code is confined to the sandbox.

The Java 2 platform (also called Java 1.2) really has paved the way for application security with a finer-grained security model, as depicted in Figure 2.3. Now local and remote code alike can be confined to utilize only particular domains of resources according to configurable policies. Thus, some Java code Foo might be limited to access resources confined by one domain, whereas some other Java code Bar might have access to a set of resources confined by some other domain. Domains of access and configurable security policies make the Java 2 platform much more flexible. Furthermore, relieving developers from making the distinction between remote and local code allows for more widely applicable solutions to application security problems instead of simply focusing on mobile code and Java applet security prob lems.

Java 1.2/2 configurable and fine-grained access security model.

Figure 2.3. Java 1.2/2 configurable and fine-grained access security model.

Java Security Architecture

Figure 2.4 depicts the primary components of the standard set of APIs and mechanisms used to provide security for Java 2–based applications. In the lower half of the diagram are the core Java 2 security architecture and Java Cryptography Architecture (JCA), which together compose the Java 2 security platform that comes with the Java 2 platform. In the upper half of the diagram are the standard Java security extensions that ship separately from the Java 2 platform but are still dependent on different aspects of the Java 2 platform.

Java security architecture standard components.

Figure 2.4. Java security architecture standard components.

Although many commercial off-the-shelf (COTS) packages external to the components shown here are available, the components in Figure 2.4 represent what Sun designates as those that provide a standard interface. However, various COTS service-provider implementations that adhere to the interface standards defined by many of these Java security components are indeed available. Let's take a brief look at each of these components in more detail before we explore the details behind them in this chapter and throughout the book.

Core Java 2 Security Architecture

Figure 2.5 shows the core Java 2 security architecture in the context of the rest of the Java 2 platform, operating system, resources, and Java code running atop the Java 2 platform. The pieces of this architecture that form the core of Java Security are the byte code verifier, class loader, security manager, access controller, permissions, policies, and protection domains.

The core Java security architecture.

Figure 2.5. The core Java security architecture.

The byte code verifier verifies that the byte codes being loaded from Java application code external to the Java platform adhere to the syntax of the Java language specification. The class loader is then responsible for actual translation of byte codes into Java class constructs that can be manipulated by the Java runtime environment. In the process of loading classes, different class loaders can employ different policies to determine whether certain classes should be loaded into the runtime environment. The class loader and the Java 2 platform classes themselves help limit access to valued resources by intercepting calls made to Java platform API and delegating decisions as to whether such calls can be made to the security manager. Java 1.0 and 1.1 made exclusive use of a security manager for such decision making, whereas Java 2 applications can use the access controller for more flexible and configurable access-control decision making. Finally, execution of code would not be possible without the beloved runtime execution engine.

Access control is the most significant addition to the Java 2 security platform, helping extend the security model to enable configurable and fine-grained access control. Java 2 permissions encapsulate configurable and extendable ways to designate access limitations and allowances that might be associated with valued resources. Java 2 policies provide the mechanisms needed to actually associate such permissions with valued resources in a configurable fashion. Finally, means for encapsulating domains of access control are also provided with the core Java 2 security model.

The java.security package contains the classes and interfaces that define the core Java security architecture. The java.security.acl package also contained access-control classes and interfaces that were core to the Java 1.1 security architecture, but have been superseded in Java 2 by newer access-control constructs. Finally, other security-related classes are embedded throughout the entire collection of Java platform packages. In this chapter and the next, I highlight which classes play a role in supporting the core Java security architecture.

Java Cryptography Architecture

The Java Cryptography Architecture (JCA) provides an infrastructure for performing basic cryptographic functionality with the Java platform. The scope of cryptographic functionality includes protecting data against corruption for the sake of data integrity using basic cryptographic functions and algorithms. Cryptographic signature-generation algorithms used for identifying sources of data and code are also built into the JCA. Because keys and certificates are a core part of identifying data and code sources, APIs for handling such features are also built into the JCA.

Although the JCA is part of the built-in Java security packages, as are the core Java 2 security architecture features, I distinguish the JCA from such core APIs largely due to the JCA's underlying service-provider interface. That is, different cryptographic implementations can be plugged into the JCA framework, whereas Java applications can still adhere to the same basic JCA interfaces. Sun does equip a default set of cryptographic functions with the JCA, however.

Java Cryptography Extension

The terms encryption and cryptography are sometimes used interchangeably. However, Sun adheres to the definition of cryptography that designates the provision of the basic data integrity and source identity functions supported by the JCA. Encryption is used to mean those functions used to encrypt blocks of data for the sake of added confidentiality until the data can be subsequently decrypted by the intended receiver. The Java Cryptography Extension (JCE) is provided as a Java security extension for these auxiliary encryption purposes.

Although most would logically argue that encryption is a core aspect of any secure system, Sun has purposely made JCE an extension to the Java platform. This is largely due to U.S. export restrictions on encryption technology. If Sun were to include the JCE as a core part of the Java platform, exportability of the Java platform itself would be hampered. Furthermore, although many commercial-grade encryption technologies have been developed by third parties, the JCE provides a standard service-provider and application-programmer interface model. Thus, even if different commercial-grade encryption implementations are used, the programmer still uses the same API to the different underlying implementations.

Java Secure Socket Extension

Because Secure Sockets Layer (SSL) is one of the more commonly used encryption-based protocols for integrity and confidentiality, Sun has developed the Java Secure Socket Extension (JSSE) as an extension to the Java security platform. The JSSE provides a standard interface along with an underlying reference implementation for building Java applications with SSL. Even if different commercial-grade underlying SSL implementations are used with the JSSE, the developer still uses the same interface to the applications. The JSSE is also more generically defined to provide a standard interface to support other secure socket protocols such as the Transport Layer Security (TLS) and Wireless Transport Layer Security (WTLS) Wireless Transport Layer Security (WTLS) protocols.

Java Authentication and Authorization Service

The Java Authentication and Authorization Service (JAAS) extension to the Java security platform was developed to provide a standard way for limiting access to resources based on an authenticated user identity. Thus, standard APIs for login and logout are provided. In these, a standard interface for passing around secure user credentials and context makes it possible to swap in and out different underlying authentication model implementations. Thus, whether you use Kerberos or Smart Cards, the same API is provided.

Byte Code Verifier

Before the class loader bothers to register a class that it loads with the Java runtime environment, it passes the loaded Java class byte codes to the byte code verifier (also called the class file verifier). The byte code verifier then analyzes the byte code stream for the Java class and verifies that this byte stream adheres to the Java language rules defined for classes. The byte code verifier accomplishes this task in two phases. In phase one of byte code verification, the internals of the Java class byte stream itself are analyzed. In phase two, the byte code verifier verifies references to other classes from this class.

Phase two verification occurs when the classes to which a class refers are actually referenced at runtime. Not only will the references be verified for correctness, but the relationship rules involving that reference also will be verified. For example, a referenced method on another class will be checked to see whether that method is visible (that is, public, package, protected, or private) to the current class.

The following verification checks are typically performed by the byte code verifier :

  • Verify that class bytes begin with the 0xCAFEBABE byte stream.

  • Verify that the byte stream is neither truncated nor padded with extra bytes.

  • Verify that class bytes adhere to the correct format for the class itself, methods, fields, and other components of a Java class.

  • Verify that there are no overflows of operands on the stack.

  • Verify that there are no underflows of operands on the stack.

  • Verify that proper visibility (public, package, protected, or private) of methods and field variables is defined and honored.

  • Verify that final classes are not subclassed.

  • Verify that final methods are not overridden.

  • Verify that final variables are assigned an initial value and not modified.

  • Verify that methods are invoked with the correct number and types of arguments.

  • Verify that field variables of the class are assigned with values of the correct type.

  • Verify that no local variable is accessed before it has an assigned value.

  • Verify that the class has one superclass (except, of course, for java.lang.Object).

Although Java 1.1 did not verify classes that were loaded from the CLASSPATH and always verified classes that were loaded from outside the CLASSPATH , this behavior could be slightly modified with parameters passed to the java and jre commands. The -verify flag could induce verification of classes on the CLASSPATH as well. The -noverify flag could turn off verification of classes not on the CLASSPATH. The default verification of all classes not on the CLASSPATH could also be indicated with the -verifyremote flag .

Java 2 verifies all classes except the classes that are part of the core Java platform suite. Although the standard java executable command documentation for Java 2 does not explicitly list the -verify, -noverify, and -verifyremote options as valid command flags, some JVM implementations can indeed support use of the flags.

Class Loader

The class loader is one of the key components of a Java virtual machine responsible for managing the loading of Java class byte codes into the Java runtime environment. The class loader is responsible for

  • Loading Java class byte codes from an input stream (for example, file or network).

  • Inducing the byte code verifier to verify these byte codes.

  • Inducing the security manager and access controller to verify access to resources.

  • Handing the classes off to the Java runtime execution engine.

The class loader component is actually composed of one or more individual class loaders. A primordial class loader is baked into the Java virtual machine and is responsible for loading the core Java platform classes. Other class loaders can be implemented by adhering to a standard class loader interface. However, such class loaders are subject to more stringent security verifications than is the primordial class loader. Different class loader implementations are provided to offer different ways to load classes from different input streams using different policies.

Class Loader Architecture and Security

Figure 2.6 depicts the basic class loader architecture assumed by the Java security model. As a generic rule enforced by the standard class loader interface framework, when asked to load a class from an input stream, a class loader first checks its cached collection of classes to determine whether the class is already loaded. If it is loaded, the class loader can return the class without further processing. The security manager and access controller can then be consulted to determine whether access to the particular class is allowed. The primordial class loader is then consulted first to determine whether it can load the class. In Java 2, the primordial class loader loads core Java platform classes, whereas the Java 1.1 primordial class loader also loads classes from the CLASSPATH . In Java 2, auxiliary class loaders are consulted to load classes from the CLASSPATH as well as remote classes.

The class loader architecture.

Figure 2.6. The class loader architecture.

If the class was not loaded by the primordial class loader, the class is read from the input stream. The actual type of input stream used depends on the type of class loader. An input stream connected to a local file medium location or to a network medium location requires a class loader that can read classes from such mediums. The bytes read from the input stream are then fed through the byte code verification process. If successful byte code verification was performed, a java.lang.Class object is created that is used to describe the class for the virtual machine. Before the class is created, the security manager is consulted again to determine whether creation of such a class is permitted. Finally, if the class is loaded, the class is cached with its collection of other loaded classes.

When a class loaded by the class loader refers to another class, the same basic process for finding the referenced class is followed. Thus, you can see that a particular class loader will consult only the primordial class loader, its own cached collection of classes, and its associated input stream for a class to load. Different class loader instances within the same virtual machine do not consult one another to load classes. This separation of class loaders results in a separation of name spaces such that different implementations of classes with the same fully qualified name can live in different class loaders. This provides a security advantage in that a malicious class loader cannot corrupt the classes used by another class loader in the same virtual machine by purposely loading a malicious class into that class loader's name space.

Class-Loader Interfaces

In addition to the primordial class loader that is part of the Java platform, other common and predefined class loaders also exist. Perhaps one of the first common class-loader types to be implemented was the applet class loader. Applet class loaders were implemented by Web browser vendors to provide support for loading classes, which form the code of an applet, from the network via HTTP from within a Web browser. Applet class loaders typically determine from where to load these classes based on a CODEBASE tag that accompanies an applet tag in an HTML file.

Although no standard API for an applet class loader exists, Figure 2.7 shows the standard base class-loader class and three standard class-loader implementations that come equipped with the Java platform via a UML class diagram. These standard class loader types are briefly described here:

Standard class loader APIs.

Figure 2.7. Standard class loader APIs.

  • java.lang.ClassLoader : Represents the base class loader from which other class loaders should be extended. Core interfaces exist on the ClassLoader for loading classes, defining classes, finding classes, obtaining resource handles, and accessing class libraries. The loadClass() method is the single most important method on the ClassLoader. It is responsible for performing the actual load of a class.

  • java.security.SecureClassLoader : The SecureClassLoader was introduced in Java 2 as a base class that offers the capability to associate classes with permissions and a code source. A code source identifies the URL and certificate associated with a class.

  • java.net.URLClassLoader : This class loader was also introduced in Java 2 to load classes from a list of URLs. The URLs can be either JAR files or directories.

  • java.rmi.server.RMIClassLoader : Left over from Java 1.1, the RMIClassLoader is used by RMI applications during marshaling and unmarshaling of classes passed as para meters or return values. The RMIClassLoader loads classes from the network using a list of URLs.

The URLClassLoader class offers all you need in a class loader. It can load classes from the network via HTTP or from the file system. However, you might encounter a situation in which you need to implement your own class loader. For example, you might desire to load classes from a database or perhaps via some other protocol besides HTTP. In such a situation, you should still subclass ClassLoader, SecureClassLoader, or URLClassLoader. You'll then want to overload a few of the protected methods of the class you are extending.

Whereas Java 1.1–style class loader implementations often overrode the protected ClassLoader.loadClass(String, boolean) method, Java 2–style class-loader implementations are encouraged to override the protected ClassLoader.findClass(String) method. This is because most of the generic class loading logic can still be utilized by leaving the loadClass() method as is. The loadClass() method calls the findClass() method after preliminary calls are made, such as determining whether the class is already loaded and checking with the primordial class loader. The findClass() method can thus be specialized to look for the class on the specialized input stream that your class loader implementation desires to search for classes matching the fully qualified classname as an input parameter. If you have a block of data you've read from your input stream, you can run this class through the byte code verifier, as well as create an instance of a Class object by calling the defineClass() method. For example

public MyClassLoader extends ClassLoader
{
  protected Class findClass(String className){
    byte[] buffer = getMyData(className);
    Class returnClass = defineClass(className, buffer, 0, buffer.length);
    return returnClass;
  }

  private byte[] getMyData(String className){
    // This arbitrarily named method contains the logic
    // needed to retrieve the named class from the input stream
    // associated with your class loader implementation. Thus, this
    // method may read some data from a TCP/IP socket, for example,
    // given the name of a class and then return the bytes (byte codes)
    // associated with the read Java class.
  }
  ...
  // Implement other methods as needed
  ...
}

An explicit request to load a class using your custom class loader can be accomplished using the static forName(String, boolean, ClassLoader) method on java.lang.Class. The String parameter specifies the fully qualified classname to load, the boolean flag indicates whether the class should be initialized, and the ClassLoader parameter designates the class loader to use. Thus, you might explicitly load a class using your custom class loader and instantiate an instance of the class given the following call:

MyClassLoader myClassLoader = new MyClassLoader();
Class myQueryServerClass = Class.forName("com.beeshirts.QueryServer",
                                                 true, myClassLoader);
QueryServer myQueryServer
                     = (QueryServer) myQueryServerClass.newInstance();

Every reference to unloaded classes from the myQueryServer object can then use the class loader with which it was loaded. Of course, you can also call loadClass() on an instance of your custom class loader and use it to load the class initially, as shown here:

MyClassLoader myClassLoader = new MyClassLoader();
Class myQueryServerClass
                  = myClassLoader.loadClass("com.beeshirts.QueryServer");
QueryServer myQueryServer
                  = (QueryServer) 
							 myQueryServerClass.newInstance();

Security Manager

The security manager component of the core Java security architecture is responsible for determining whether certain requests to access particular valued resources are to be allowed. To make this decision, the security manager considers the source (that is, Java class) making the request. Because access to many valued resources must first pass through the core Java classes from a Java class making the request, the core Java classes take the opportunity to first ask the security manager whether the request is allowed. If access is denied, a java.lang.SecurityException is thrown. If access is permitted, the call will proceed as normal.

Each Java virtual machine process instance allows only one security manager instance to exist (that is, a singleton). After the security manager is instantiated, the JVM can be configured so that the security manager cannot be replaced. It thus exists for the lifetime of the Java virtual machine process. Many Java virtual machines embedded in Web browsers will instantiate a security manager instance before the first Java applet is ever loaded and not permit the security manager to be replaced. Thus, the security manager in Web browsers cannot be replaced by a malicious Java applet. A malicious Java applet might, after all, replace the security manager with its own security manager instance that relaxes restrictions on which valued resources can be accessed.

Although Java applets run in a Java virtual machine process that has already instantiated a security manager, regular Java applications you create don't have this advantage. In Java 1.1, creating your own security manager was a tad tedious. Java 2 makes creating your own security manager for a Java application much simpler and makes it easily configurable. This is because a default and configurable security manager can be used with Java 2 applications; this manager is rich enough in flexible feature support for many applications. Thus, as you'll see, use of a security manager to protect access to valued resources can also be provided for Java applications.

Use of the default security manager can be specified from the command line during startup of your Java applications. The java.security.manager property can be passed to the Java virtual machine as a system property, specifying use of the default security manager in the following fashion:

java –Djava.security.manager MyApplication
java –Djava.security.manager=default MyApplication

Security Manager Interfaces

Figure 2.8 shows the java.lang.SecurityManager class encapsulating the key interface to the security manager currently instantiated in the Java machine process. The java.lang.System.getSecurityManager() method returns a handle to the currently instantiated SecurityManager object. If no security manager is instantiated, a null value is returned. The java.lang.System.setSecurityManager() call takes a SecurityManager input parameter and checks to see whether the existing security manager is allowed to be replaced by the calling class. If the security manager does not exist or if the class is allowed to replace the existing security manager, the operation proceeds and returns. Otherwise, a SecurityException is thrown.

Security manager classes.

Figure 2.8. Security manager classes.

The SecurityManager class is mainly populated with public check XXX () style methods. Each check XXX () method is defined to check whether access is allowed for a particular valued resource. If access is not allowed, the check XXX () methods will throw SecurityException objects. Most of these methods are left over from the Java 1.0 and 1.1 versions of the SecurityManager. Java 2 has introduced the checkPermission() method, which is a more generic form of the other check XXX () methods. In fact, each check XXX () method now calls the generic checkPermission() method under the hood of Sun's Java 2 platform implementation. The checkPermission() method in turn calls the java.security.AccessController class.

The AccessController was added in Java 2 to provide the fine-grained and configurable access control functionality that is central to the new security model. Although the SecurityManager is still maintained for backward compatibility with existing applications and serves as a primary security management interface, the AccessController really subsumes the responsibility for algorithmic-access checking that was once the province of the SecurityManager. In the next chapter, I describe in more detail the AccessController and how the functionality of the Java 2 access control mechanisms replaces most of the SecurityManager API calls. Because I discuss the legacy SecurityManager calls in the next chapter in the context of Java 2 access control, I do not cover the SecurityManager API calls here. I will, however, briefly outline how a SecurityManager might be customized by your applications in a Java 1.1 fashion.

Custom Security Managers

Java 1.1 required that a SecurityManager be extended to provide your own custom application-specific access control policies. Implementing your own SecurityManager is no longer recommended for Java 2–based applications, however. Instead of writing code that overrides the methods of a SecurityManager, I demonstrate customization of access decision making in the next chapter via configuration of security permissions and policies. Because the Java 2 platform discourages SecurityManager subclassing, all the protected SecurityManager methods—with the exception of getClassContext()—have been deprecated in Java 2.

Classes, such as the java.rmi.RMISecurityManager class introduced in Java 1.1 and shown in Figure 2.8, simply override the methods that relate to modified behavior for specific valued resource access checking. Thus, an overridden SecurityManager in Java 1.1 that extends a few file-access–checking operations might look like this:

public class CustomSecurityManager  extends SecurityManager
{
  public CustomSecurityManager()
  {
    super();
  }

  public void checkRead(String fileName)
  {
    if(fileName != null && fileName.endsWith(".java")){
    throw new SecurityException(" You are not allowed to read  "
        +" file names ending with .java");
    }
    super.checkRead(fileName);
  }

  public void checkWrite(String fileName)
  {
    if(fileName != null && fileName.endsWith(".java")){
      throw new SecurityException(" You are not allowed to write  "
        +" file names ending with .java");
    }
    super.checkWrite(fileName);
  }

  public void checkDelete(String fileName)
  {
    if(fileName != null && fileName.endsWith(".java")){
      throw new SecurityException(" You are not allowed to delete  "
        +" file names ending with .java");
    }
    super.checkDelete(fileName);
  }
}

To set this security manager as the security manager to use for your application's Java virtual machine process, you call System.setSecurityManager() with an instance of this CustomSecurityManager some time early in the process of starting your application (perhaps inside the main() method of your application) in this way:

System.setSecurityManager(new CustomSecurityManager());

At some point when your application attempts to access a file, the Java virtual machine will call the custom security manager for you under the hood. For example, the java.io.FileInputStream class calls checkRead() on the SecurityManager, which would actually use your registered CustomSecurityManager object. Your application code might also need to make such calls explicitly as shown here:

public void myFileAccessMethod(String fileName){
  SecurityManager secMgr = System.getSecurityManager();
  if(secMgr != null){
    secMgr.checkRead(fileName);
  }
  // If got this far, then can proceed on with method...
}

If the preceding explicit checkRead() call throws a SecurityException, the myFileAccessMethod() call will be terminated before the rest of the method can proceed and will return with the SecurityException thrown. Because the SecurityException class extends the java.lang.RuntimeException class, the fact that the myFileAccessMethod() can throw such an exception does not need to be explicitly declared in the method signature.

Java Cryptography Architecture

The Java Cryptography Architecture (JCA) equipped with the Java platform was first introduced with Java 1.1. The JCA provides basic cryptographic functions used for the following primary purposes:

  • To protect data communicated or stored for integrity

  • To identify a principal associated with data that has been communicated or retrieved from storage

  • To provide support for generating keys and certificates used to identify data sources

  • To provide a framework for plugging in different cryptographic algorithms from different service providers

You should not assume too much from the word cryptography in JCA. The JCA is not really useful for encrypting data communicated or stored for decryption by an intended receiver. Such cryptographic functionality used to provide confidentiality is possible with the JCE. Because of U.S. export restrictions, the JCE is shipped separate from the Java platform, whereas the JCA does not have such limitations. Furthermore, although SSL represents one of the more popular crypto-related protocols in the Internet era, it is not packaged with the JCA. Rather, SSL interface support is provided separately from the Java platform in the JSSE package.

This section provides an overview of the architecture of JCA. A more detailed explanation and sample uses of different components of the JCA are presented later in the book.

The Architecture of JCA

The JCA is composed of a number of classes and interfaces that implement basic cryptographic functionality. These are the Java 2 platform packages, which contain classes and interfaces that make up the JCA:

  • java.security : The set of core classes and interfaces for the JCA plug-and-play service provider framework and cryptographic operation APIs. Note that this package also contains core Java security architecture classes and interfaces.

  • java.security.cert : A set of certificate management classes and interfaces.

  • java.security.interfaces : A set of interfaces used to encapsulate and manage DSA and RSA public and private keys.

  • java.security.spec : A set of classes and interfaces used to describe public and private key algorithm and parameter specifications.

Figure 2.9 depicts the top-level architecture of the JCA with a mixture of actual Java classes and conceptual classes (that is, not real code-based classes, but simply representative of concepts). At the top of the diagram is the java.security.Security class that is mainly responsible for managing a collection of Cryptographic Service Providers (CSPs). A CSP represents a service provider that implements one or more cryptographic functions that adhere to the cryptographic interfaces defined in the JCA. Information about each CSP is encapsulated by the java.security.Provider abstract class. CSPs (shown as SomeCSPImpl in Figure 2.9) extend the Provider abstract class with specific implementations of methods on the Provider class.

The top-level architecture of JCA.

Figure 2.9. The top-level architecture of JCA.

Each CSP will implement one or more cryptographic engines. A cryptographic engine represents a particular cryptographic algorithm and set of parameters to that algorithm that perform some cryptographic functionality. For example, the MD5 message digest algorithm is a particular algorithm and set of parameters used to generate an encrypted stream of data based on another stream of data. MD5 belongs to a general class of cryptographic engines referred to as a message digest cryptographic engine.

The various cryptographic engines (for example, SomeCSPCryptoEngineImpl) supplied by a CSP implement some standard cryptographic service-provider interface (for example, ACryptoEngineClassSPI) provided by the JCA. Each service provider interface is extended by a cryptographic engine API (for example, ACryptoEngineClassAPI) used by the applications programmer. Each cryptographic engine API provides a static getInstance(String) method that takes the name of a particular algorithm name related to that engine class and returns a concrete instance of the requested cryptographic engine using that algorithm if it exists. Otherwise, a java.security.NoSuchAlgorithmException exception is thrown. A static getInstance(String, String) method on each cryptographic engine API specifies a particular CSP to use.

Cryptographic Engines

The java.security.MessageDigest abstract class is an example of a cryptographic engine API, whereas the java.security.MessageDigestSpi abstract class represents the cryptographic service-provider interface that must be implemented. The protected methods of MessageDigestSpi will be visible and relevant only to the CSP's implementation.

Figure 2.10 shows the various standard cryptographic engine types defined by the JCA. These cryptographic-engine APIs and their helper classes rest at the core of how the JCA is used by application developers. The primary functionality provided by each cryptographic engine type in Figure 2.10 is described here:

JCA cryptographic engine classes.

Figure 2.10. JCA cryptographic engine classes.

  • MessageDigest : Creates and verifies message digests

  • Signature : Creates and verifies digital signatures

  • KeyPairGenerator : Generates public and private key pairs

  • KeyFactory : Converts between secure keys and key specifications

  • KeyStore: Modifies information in a secure key storage repository

  • CertificateFactory: Generates engine)> certificates and certificate revocation lists

  • AlgorithmParameters: Encodes crypto algorithm parameters

  • AlgorithmParameterGenerator: Creates crypto algorithm parameters

  • SecureRandom: Creates random numbers

Cryptographic Service Providers

The JCA shipped with the Java platform comes equipped with a default CSP implemented by Sun. The String name used with the JCA APIs to designate this default provider is SUN. The default Sun CSP implementation provides support for the following cryptographic engine and algorithm combinations:

  • MD5 message digest algorithm

  • SHA-1 message digest algorithm

  • DSA for signatures

  • DSA key pair generator

  • DSA algorithm parameters

  • DSA algorithm parameter generator

  • DSA key factory

  • JKS key store (JKS involves a proprietary algorithm)

  • X.509 certificate factory

  • SHA1PRNG pseudo-random number generator algorithm (an IEEE standard)

You can install a CSP for use with your Java runtime environment by simply placing a JAR or ZIP file with the classes that implement the JCA CSP interfaces somewhere on your CLASSPATH. You must configure the java.security file under the root directory of your Java installation in the <JavaRootInstall>libsecuritydirectory. In that file, add the fully qualified classnames of your CSPs that extend the Provider class. The order of preference specifies which CSP to use in the event that an algorithm selected for use is implemented by another CSP. For example, the following java.security file entries specify that Sun's default CSP is preferred before Sun's RSA JCA CSP implementation:

# List of providers in order from most to least preferred CSP
security.provider.1=sun.security.provider.Sun
security.provider.2=com.sun.rsajca.Provider

CSPs can also be added and removed programmatically from within your JVM process. Only trusted applications can perform such operations, however. A trusted application here refers to an application running without a security manager installed or an application that has been granted permission to add and remove CSPs. We discuss how to configure such permissions in the next chapter. However, suffice it to say here that adding and removing CSPs with the Security class is rather straightforward, assuming that your application has the proper permissions:

// Install SunJCE CSP by first creating a Provider instance
Provider providerSunJce = new com.sun.crypto.provider.SunJCE();
// Then add provider using Security class and obtain preference number
int providerPreferenceJCE = Security.addProvider(providerSunJce);

// Or set the preference number yourself increasing preference numbers
// of any providers already installed at that preference level
Provider providerATI = new com.assuredtech.security.JCAProvider();
int providerPreferenceATI = Security.insertProviderAt(providerATI, 1);

// A provider can be removed dynamically using Security class
// For example, to 
							
							remove default Sun CSP
Security.removeProvider("SUN");

Summary

The Java security architecture provides a standards-based interface for Java developers to create secure Java applications. The core Java security architecture is composed of a byte code verifier, one or more class loaders, and a security manager/access control framework. The byte code verifier provides support for low-level object corruption checking. The class loader provides further protection in terms of authenticity of trusted code. The security manager and access control, of course, provide authorization protection.

Also part of the Java 2 platform, the JCA provides plug-and-play service-provider–based protection of data and programs for integrity and identity. Extensions to the Java platform such as the JCE and JSSE provide protection for confidentiality, whereas the JAAS provides enhanced authenticity and authorization protection. In terms of major security architecture components currently not supported by Java, any standard interfaces for nonrepudiation and security auditing are currently lacking. The next chapter describes practical usage of access control.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
18.224.73.125