JavaSecurity Overview and Reference

Security in JDK 1.1

The JavaSecurity API and Digital Signatures


Draft 1.0 -- Last updated December 3, 1996


An overview of the JDK 1.1 JavaSecurity features appears below. First, here are some quick links to relevant pages in this and other related documents:


JavaSecurity: the Java Security API

Introduction

The JavaSecurity API is a new Java Core API, built around the java.security package. JavaSecurity is designed to let developers incorporate both low-level and high-level security functionality into their Java applications. This includes digital signatures, data encryption, key management and access control.

The first release of JavaSecurity, available in JDK 1.1, contains a subset of this functionality, including APIs for

Digital Signatures
Digital signature algorithms, such as DSA or MD5withRSA. The functionality includes generating public/private key pairs as well as signing and verifying arbitrary digital data.

Message Digests
Cryptographically secure message digests, such as MD2, MD5, or SHA. These algorithms, also called one-way hash algorithms, are useful as "digital fingerprints" of documents and are frequently used in digital signatures, and other applications that need unique identifiers for digital data.

Key Management
A set of abstractions for managing principals (entities such as individual users or groups), their keys and their certificates. It allows applications to design their own key management system, and to interoperate with other systems at a high level.

Access Control Lists
A set of abstractions for managing principals and their access permissions. A high-level overview is available. This facility will be expanded in subsequent releases.
JDK 1.1 includes a command-line utility for managing keys and certificates and for digitally signing files. See the javakey reference page for Solaris or the javakey reference page for Windows for more information.

JDK 1.1 also has facilities to sign and verify Java ARchives (JAR), which contain arbitrary data, including class files, images, sounds, etc. The JDK 1.1 Security Manager used, for example, by appletviewer and by the HotJava browser) is aware of these signatures, and, working in conjunction with the javakey tool, will grant special privileges to signed and trusted code.

Subsequent releases of JavaSecurity will include APIs for data encryption and key exchange as well as more sophisticated security policies. The encryption API will be an API for block, stream, symmetric and asymmetric encryption, with support for multiple modes of operation and multiple encryption.

Security Package Providers

JavaSecurity is essentially an abstract layer, and introduces the notion of a Security Package Provider (SPP). An SPP is a package (or set of packages) providing a concrete implementation of a subset of JavaSecurity. JDK 1.1 comes standard with a default provider, the Sun Security Provider.

SPPs are explicitly installed in a Java environment, and configured according to the user's preferences. This is to allow end users of Java runtimes to be able to select the implementations of their choice for critical algorithms, if they so desire.

In JDK 1.1, high-level provider configuration information appears in the java.security file. Registration of an SPP can be done by specifying (in java.security) the Provider subclass whose constructor sets the values of various properties that are required for the JavaSecurity API to look up the algorithms or other facilities implemented by the SPP. An SPP can also be registered dynamically, via a call to the Security addProvider or insertProviderAt method.

The Sun Security Provider

In JDK 1.1, there is one default provider, called Sun Security. The sun.security.provider package includes:

Note: the sun.security.provider classes should never be accessed directly. They will be instantiated and used by the JavaSecurity API if "SUN" is the requested service provider, or if no other service provider is specified, since they provide the default implementation. Applications should always call the JavaSecurity API (java.security) interfaces.

Implementations of various classes that are useful when using java.security are available in other sun.security subpackages. This includes

Unlike the sun.security.provider package, the classes in the other sun.security subpackages can be accessed directly.

Architecture and Design

JavaSecurity is built around several important design principles:

Implementation and Format Independence
JavaSecurity provides interfaces to various algorithms, such as DSA, MD5 and SHA. It does this while making it possible for applications to request an algorithm without regard to implementation details. For example JDK 1.1 includes the Sun Security Provider, which implements DSA entirely in Java. Another implementation could be done in hardware, or written in another language, transparent to the application.

Certain types of data relevant to JavaSecurity, such as certificates, are often encoded in a variety of formats. In the case of certificates, JavaSecurity defines a certificate interface, and a mechanism to dynamically specify which classes to use to parse certificates. In sun.security we include an X.509 certificate. Another provider may add a PGP or SDSI certificate parser.

The same mechanism is true of keys and of algorithms. That is, JavaSecurity defines an interface for each of these, and a mechanism to dynamically specify which classes to actually use for them.

Interoperability Across Implementations
This feature is a byproduct of JavaSecurity's implementation independence. A simple example of this is that keys generated by any implementation of a given algorithm (within common parameter bounds, if applicable) should work with any other similar implementation. To achieve this, JavaSecurity defines standard encoding rules, based on existing international standards such as X.509 and PKCS#8.

Implementation-Specific Security
JavaSecurity takes advantage of Java's type system to allow its implementations to design their own security policies while adhering to a set of common semantics.

Code Signing in Java

If you develop Java applications and Java applets, you may want to use the Java code signing features. Two sample reasons for using code signing features are the following:

The JDK 1.1 Security Manager (used, for example, by appletviewer and by the HotJava browser) is aware of signatures, and, working in conjunction with the javakey tool (which is used to sign code and specify who is trusted), will grant special privileges to signed and trusted applet code.

In order to be able to sign code, a developer must first take two basic steps:

  1. Generate a private/public key pair, and
  2. Obtain a certificate attesting to the authenticity of the public key (so parties verifying your signature using your public key are confident that the public key they are provided is authentic).

After obtaining a private key and a certificate, an application may sign code using them.

JAR Files and Digital Signatures

One new feature of JDK1.1 is the introduction of Java ARchives (or JAR files), which enable the packaging of class files, images, sounds and other digital data in a single file for faster and easier distribution. A tool included with JDK1.1 allows developers to generate JAR files. This tool is called jar. (See Windows for the Windows version.)

Another addition to JDK1.1 is a tool to manage identities, to generate and manage keys and certificates, and to sign and verify JAR files. The tool, javakey is a simple command-line driven utility which uses a persistent database. (See Windows for the Windows version.)

Managing identities, keys and certificates

This involves associating identity names with keys, and keys with certificates, as well as associating names and trust levels.

Creating, adding and removing Identities and Signers

Identities are real-world entities that can be authenticated using a public key that is associated with them. To create an identity means to associate a name with one or more certificates (all of which are for the same public key).

Signers are real-world entities which can both be authenticated and can produce signatures, i.e., they have both a public key and a private key (for signing) associated with them. To create a signer involves generating (or otherwise associating) a public/private key pair with a name, and possibly associating a set of certificates with the public key.

Associating trust with certain identities

In JDK 1.1, certain identities will be declared by the client (e.g., end user or system administrator) to be trusted. This will mean that code signed by trusted identities will have system code status. In later releases we will provide greater granularity in the trust levels that we allow.

Signing JAR files

This involves generating a signature for a given Signer and including that signature in a given JAR file.

We expect Java licensees to honor signatures generated using javakey.


Digital Signatures for the Java Platform

Digital Signature API

JavaSecurity provides a digital signature API (Application Programming Interface) and SPI (Service Provider Interface). The API classes for digital signatures are called the "Digital Signature Classes", and consist of:
Signature
Defines the API (Application Programmer Interface) as well as the SPI (Service Provider Interface) to signature functionality.
Provider
Represents an implementor of JavaSecurity, which may provide digital signature or message digest implementations, as well as its own key management classes.
Security
Manages providers, and centralizes all security properties and common security methods.
Key
A generic key, such as a DSA public or private key.
PublicKey
A Key subclass for public keys.
PrivateKey
A Key subclass for private keys.
DSAKey
An interface to a DSA public or private key.
DSAPublicKey
An interface to a DSA public key. This interface extends the DSAKey interface.
KeyPair
A simple holder for a key pair (a public key and a private key).
KeyParams
An interface to algorithm-specific key parameters.
DSAParams
An interface for DSA-specific key parameters. This interface extends the KeyParams interface.
Certificate
An interface of abstract methods for managing an identity certificate. An identity certificate is a guarantee by a principal that a public key is that of another principal. (A principal represents an entity such as an individual user or a group.)
Identity
An object representing a real-world object such as a person, company or organization that can be authenticated using a public key.
Signer
An Identity that can also sign code.
IdentityScope
A scope for identities, containing unique (withing the scope) Identity objects of all kinds, including Signers.

The Sun Security Provider for JavaSecurity is the default provider included in JDK 1.1. It implements the following classes:

DSA
An implementation of the Digital Signature Algorithm (NIST fips 186).
DSAPublicKey
A public key for DSA.
DSAPrivateKey
A private key for DSA.
MD5
An implementation of the MD5 message digest algorithm.
SHA
An implementation of the Secure Hash Algorithm (SHA) message digest algorithm (NIST fip 180 as superseded by fip 180-1).
SystemIdentity
A basic implementation of Identity.
SystemSigner
A basic implementation of Signer.
IdentityDatabase
A basic implementation of IdentityScope.
Main
The javakey utility to sign and verify files, and to manage trust.
X509Key
A public key encoded using the X.509/DER rules.
X509Cert
A certificate encoded using the X.509/DER rules.

The basic mechanism for obtaining an appropriate Signature object is as follows: A user requests a Signature object by calling the getSignature method in the Signature class, specifying a signature algorithm (such as "DSA"), and, optionally, a provider. The getSignature method finds a Signature subclass that fits the algorithm and provider parameters supplied. If no provider is specified, getSignature searches the registered providers, in preference order, for one with a Signature subclass implementing the specified algorithm. The provider preference order is set in the java.security file (and possibly modified when new providers are added dynamically). See configuration information.

The Key, Identity and IdentityScope classes are used for key, certificate and trust management. Identities are reflections of real-world entities, such as persons, companies and organizations. They have keys, certificates and trust levels (permissions) associated with them. There is a system IdentityDatabase, which holds system identities.

Application Programming Interface - Example

Overview

The JavaSecurity API gives developers the ability to generate and verify digital signatures. Digital signatures are used to guarantee the origin and integrity of digital data. The Digital Signature classes share two of the fundamental designs in the JavaSecurity API: implementation independence and flexible key management. Implementation independence means that a developer can request a particular algorithm and the system will return a suitable implementation. Key management flexibility means that keys can be handled either by the system, or by the application, as appropriate.

Obtaining a Signature Object

The Signature class is the developer's interface to all signature functionality for a given algorithm. It can sign, verify a signature, or generate keys for that algorithm. There are two ways to request a Signature object: either by specifying the algorithm desired, or by specifying the algorithm and a provider for that algorithm. (See Algorithm Names for information about algorithm names.) Here is an example of requesting a particular algorithm:

import java.security.Signature;
import java.security.NoSuchAlgorithmException;

public class SignFile {
    Signature signature;

    private void init(String algorithm) 
    throws NoSuchAlgorithmException{
        signature = Signature.getSignature(algorithm);
    }
}

Generating Keys

Keys required for signing digital data can be generated through the generateKeyPair method on Signature. Let us assume that we just obtained a DSA (Digital Signature Algorithm) signature object, and that we would like to generate 1024-bit keys. First, we must initialize the signature object for generation of keys of 1024 bits:
    signature.initGenerateKeyPair(1024);

Then we can generate the keys:

    // seed is a byte array with some random bits from, say, the user.
    // we use the random seed to generate the keys.
    KeyPair kp = signature.generateKeyPair(seed);
The kp object now contains two keys: a public key and the associated private key.

Signing Digital Data

We now have a DSA key pair, and a signature object. A future version of this document will show how to get a key pair for a given identity. Note that the application developer cannot get access to the private key object -- only java.security classes can. As with key generation, the Signature object must be initialized for signing before the signing is actually done:
	// initializing the signature object for signing
	signature.initSign(kp.getPrivate());
This will initialize the signature with the private key. Signing is done on a set of bytes. The signing interface is stream-oriented, letting you specify what bytes are to be signed through the update method. There are two types of update methods, one which works on byte arrays and one which works on a single byte at a time. Here is an example of the use of the latter, followed by a call to sign to calculate and return the signature of all the data:
	// signature is an initialized signature object
	// fileToSign has the name of the file containing the data bytes
	File file = new File(fileToSign);
	FileInputStream fis = new FileInputStream(file);
	while (fis.available() != 0) {
		signature.update(fis.read());
	}
	byte[] realSignature = signature.sign();

Verifying a Signature

Now suppose that we want to verify whether or not an alleged signature is in fact the authentic signature of the data associated with it. In order to verify the signature, we must first initialize a signature object for verification. To do so, we call initVerify with the public key for the identity whose signature is going to be verified. Assuming that kp is the key pair for the identity, kp.getPublicKey() is the public key:

        // initialize the signature with the public key for verification
        signature.initVerify(kp.getPublicKey());        

Then we must read in the bytes of data associated with the alleged signature:

	// fileToVerify has the name of the file containing the data bytes
	File file = new File(fileToVerify);
	FileInputStream fis = new FileInputStream(file);
	while (fis.available() != 0) {
		signature.update(fis.read());
	}

Finally, we verify the authenticity of the alleged signature:

	if (signature.verify(allegedSignature)) {
		System.out.println("Signature checks out");
	} else {
		System.out.println("Signature does not check out.");
	}

Please send feedback to java-security@java.sun.com