TOC
BACK
FORWARD
HOME

Java 1.1 Unleashed

- 16 -
The Security Package

by Qusay H. Mahmoud

IN THIS CHAPTER

  • An Introduction to Cryptography
  • Java Security Mechanisms in JDK 1.0
  • Overview of the Java Security API
  • The Java Security API Core Classes
  • The javakey Tool
  • Certificates

The introduction of worldwide computer networks has led to issues related to communication channels protection. Communication channels are accessible to eavesdroppers, and the only way to enforce protection for those communication channels is to apply cryptography. Cryptography is used to protect information to which illegal access is possible. Cryptography is defined as "the science of secret writing."

An Introduction to Cryptography

The Java Security API is based on cryptography. A quick review of some of the concepts of cryptography will help you better understand how the security package works.

In cryptography, the main operation is encryption. Encryption is a special computation that converts messages into representations that are meaningless to all parties except the intended receiver. Because there are many encryption algorithms, you have a choice to make--and your choice depends on the sensitivity of the data you want to encrypt. There are two types of encryption: conventional encryption and public-key encryption.

Conventional Encryption

Conventional encryption is also known as symmetric encryption, or single-key encryption, or private-key encryption. Conventional encryption was the only type available before the development of public-key encryption.

In conventional encryption, the process of encryption consists of an algorithm and a key. The key is shared between the sender of the message and the intended receiver. The original message, referred to as plain text, is converted into scrambled text known as cipher text. The output of the algorithm used depends on the key; different keys return different output. Once the message has been encrypted, it can be transmitted. After it is received, it can be converted back to plain text by using a decryption algorithm with the same key used to encrypt the message. Note that the key must be kept secret.

An example of conventional encryption is DES (Data Encryption Standard).

Public-Key Encryption

Public-key encryption is also known as asymmetric encryption. One of the main reasons that led to the development of this type of encryption was the problem of distributing the keys (key management) in conventional encryption. In public-key encryption, there are two keys (rather than the one key used in conventional encryption): One is public (can be seen by anyone), and one is private (must be kept secret and only its owner should know it).

An example of public-key encryption is RSA (Rivest-Shamir-Adleman).

Digital Signatures

In the real world, it is easy to differentiate between originals and copies. In the digital world, the task of differentiating between originals and copies is almost impossible because all information is represented as bits. Therefore, in the digital world, we need a mechanism by which one party can send a "signed" message to another party so that:

  • The receiver can verify the claimed identity of the sender.

  • The sender cannot duplicate the message at a later time.

A digital signature therefore establishes sender authenticity. It is analogous to an ordinary written signature in that:

  • It must be able to verify the author, data, and time of the signature.

  • It must be verifiable by third parties to resolve disputes.

For digital signatures to be of practical use, they must have the following properties:

  • They must be easy to produce.

  • They must be easy to recognize and verify.

  • It must be computationally infeasible to forge a digital signature, either by constructing a new message for an existing digital signature or by constructing a fraudulent digital signature for a given message.

Public-key cryptosystems provide a simple scheme for implementing digital signatures. However, note that conventional cryptosystems, such as DES, do not in themselves provide sender authenticity because the sender and receiver share the same key.

An example of digital signature is DSA (Digital Signature Algorithm), which is supported by the security API in JDK 1.1.

Certification

One of the limitations of public-key encryption is its inability to verify that a public key belongs to the individual you believe it does. It is possible that a hostile individual can send you a message signed with a secret key, claiming to be from another party. The attacker then advertises a public key; you retrieve that key and decrypt the signature. Believing that you have verified the author, you now trust information written by that hostile source. Thus, there is a need for a certificate and a certification authority.

A certificate is a block of data that identifies a user and is signed by a third party, the certification authority. The most common certificate format is the X.509. The X.509 certificate contains standard information about the user name, organization, e-mail address, and so on, along with the user's public key.

Certification Authorities (CA) have four primary functions: to issue new certificates and key pairs to users, to store a copy of each user's private key, to answer queries for users' certificates, and to keep an updated list of revoked and invalid certificates.

Java Security Mechanisms in JDK 1.0

Until now, applets have been one of the most exciting uses of the Java platform. However, the use of applets adds security vulnerabilities to your programming tasks. The automatic distribution and execution of applets makes it likely that software can be obtained from untrusted third parties; because this software is downloaded to the user's machine and run locally, the untrusted software can potentially steal or damage information and files stored on the user's machine.

The beta release of JDK 1.0 introduced the SecurityManager class. The SecurityManager class defines and implements a security policy by centralizing all access-control decisions. Web browsers such as HotJava, Netscape, and Microsoft IE use the SecurityManager class to define and implement their own browser security policies when dealing with applets. When executing untrusted code from an applet, the browser installs its own security manager.

Java protects its users from the dangers of untrusted code by placing strict limitations on applets:

  • Applets cannot read from or write to the local disk.

  • Applets cannot make network connections to computers other than the one from which they were downloaded.

How Can Cryptography Help?

Encryption can expand the capabilities of applets. If an applet is digitally signed with a public key, you can identify the person who has created the applet. With the Java Security API, you can establish trust relationships. In a trust relationship, applets do not have the restrictions just listed and can do the following:

  • Read from and write to files residing on your computer

  • Make network connections to computers throughout the Internet

Overview of the Java Security API

The java.security package lets developers incorporate low-level and high-level security functionality into their Java applications (both applets and standalone applications). The Java Security API provides APIs for digital signatures, message digests, key management, and access control lists. A message digest is a condensed form of the original message, produced using a hash function that maps a variable-length message into a fixed-length value called a hash code. Key management refers to the technique used for the distribution of public keys and the use of public-key encryption for the distribution of private keys. An access control list lists users and their permitted access rights to objects.

The JDK 1.1 includes the javakey utility for managing keys and certificates and for digitally signing files. The JDK also includes facilities for signing and verifying Java ARchive (JAR) files.

The Java Security API introduces the notion of a Security Package Provider (SPP). A provider is a package or a set of packages that provides a particular implementation of a subset of the Java Security API. The Java Security API itself provides mostly interfaces and abstract classes. To put security to work, you need implementations for the interfaces and abstract classes; this is where SPPs come in to play. SPPs are explicitly installed and configured according to the developer's preferences.

The JDK 1.1 comes with a default security provider known as the Sun Security Provider, or SUN. The sun.security.provider package provides implementations for the following:

  • The digital signature standard

  • The message digests MD5 and SHA (Secure Hash Algorithm)

In addition, the SUN provider provides implementations of a simple key system and a trust management mechanism. Note that the sun.security.provider classes should not be accessed directly. If the requested security provider is SUN, those classes are instantiated and used by the Java Security API.

The Java Security API Core Classes

The following sections describe the core classes of the Java Security API.

The Signature Class

The Signature class is a subclass of Object. The Signature class provides the functionality of the Digital Signature Algorithm (DSA). Once a Signature object has been created, it can be used for two purposes:

  • To sign data

  • To verify that an alleged signature is in fact the authentic signature of the data associated with it

A Signature object can be in one of three states: UNINITIALIZED, SIGN, or VERIFY. Thus, a Signature object can do only one type of operation at a given time.

When you first create a Signature object, it is in the UNINITIALIZED state. Two initialization methods are defined in the Signature class: initSign() and initVerify(). These methods change the state of the Signature object to SIGN and VERIFY, respectively.

Once a Signature object has been created and initialized, it can be used to sign data or verify a signature.

Creating a Signature Object

Some developers may be interested in implementing other digital signature algorithms (that is, the developer may want to request a particular signature algorithm). If a developer wants to implement a particular signature algorithm, he or she has to provide a Security Provider Package (SPP). Thus, it is possible to call a particular signature algorithm from a particular package.

To create a Signature object, the programmer has the choice of specifying an algorithm or both an algorithm and a provider using one of the following methods:

public static Signature getInstance(String algorithm) throws NoSuchAlgorithmException

public static Signature getInstance(String algorithm, String provider) throws NoSuchAlgorithmException, NoSuchProviderException

A call to the first method generates a Signature object that implements the specified algo- rithm as the argument to getInstance(). If no such algorithm exists, the exception, NoSuchAlgorithmException, is thrown.

A call to the second method generates a Signature object that implements the specified algorithm supplied by the specified provider. If no such algorithm is implemented by the specified provider, the exception, NoSuchAlgorithmException, is thrown. If the specified provider does not exist, the exception, NoSuchProviderException, is thrown.

Once a Signature object has been created, it must be initialized. As mentioned earlier, a Signature object can be in one of three states: UNITIALIZED (when the object is created), SIGN, and VERIFY. The state to which you initialize a Signature object depends on whether the object is going to be used for signing data or verifying a signature.

If the Signature object is going to be used for signing data, that object must be initialized with the private key of the entity whose signature is going to be generated. You can initialize the Signature object to the SIGN state by calling this method:

public final void initSign(PrivateKey priv) throws InvalidKeyException

If the key provided is invalid, the exception, InvalidKeyException, is thrown. Once this method is called, the state of the Signature object becomes SIGN.

If the Signature object is going to be used for verifying a signature, it must be initialized with the public key of the entity whose signature is going to be verified. In this case, you initialize the Signature object to the VERIFY state by calling this method:

public final void initVerify(PublicKey pub) throws InvalidKeyException

Again, if the key provided is invalid, the exception, InvalidKeyException, is thrown.

Signing Data

If a Signature object is in the SIGN state (that is, it has been initialized for signing with the initSign() method), the data to be signed can be supplied to the object by making one or more calls to one of the following update() methods:

  • public final void update(byte b) throws SignatureException Updates the data to be signed by byte.

  • public final void update(byte data[]) throws SignatureException Updates the data to be signed using the specified array of bytes.

  • public final void update(byte data[], int off, int len) throws SignatureException Updates the data to be signed or verified using the specified array of bytes, starting at the specified offset.

A SignatureException is thrown by any of these methods if the Signature object is not initialized properly.

To generate the signature, you simply call the sign() method:

public final byte[] sign() throws SignatureException

Verifying a Signature

If a Signature object is in the VERIFY state (that is, it has been initialized with the initVerify() method), the data to be verified can be supplied to the object by making one or more calls to the three update() methods just described.

Once the updating process is complete, the signature can be verified simply by calling the verify() method:

public final boolean verify(byte signature[]) throws SignatureException

Listing 16.1 is an example of how to sign a file by using the private key of a signer defined and generated by the javakey tool. The example uses some of the other classes' methods, explained later in this chapter.

Listing 16.1. Signing a file with a private key defined and generated by the javakey tool.

// package java.security
import java.io.*;
import java.util.*;
import java.security.*;

public class SigFile {
public static void main(String argv[]) {
        IdentityScope ss = IdentityScope.getSystemScope();
        Signer sig = (Signer) ss.getIdentity("alice");
        PrivateKey priv = sig.getPrivateKey();
        PublicKey pub = sig.getPublicKey();
        try {
          Signature signature;
          signature = Signature.getInstance("DSA");
          signature.initSign(priv);
          File file = new File("test.data");
          FileInputStream fis = new FileInputStream(file);
          while (fis.available() != 0) {
            signature.update((byte)fis.read());
          }
          byte[] realSignature = signature.sign();
          System.out.println(realSignature);
          signature.initVerify(pub);
          File f = new File("test.data");
          FileInputStream fx = new FileInputStream(f);
          while (fx.available() != 0) {
            signature.update((byte)fx.read());
          }
          if (signature.verify(realSignature)) {
            System.out.println("Signature checks out");
          } else {
            System.out.println("Signature does not check out");
          }
        }catch(Exception e) {
           System.err.println(e);
        }
    }
}

To run the program in Listing 16.1, you must first create a file called test.data. Put anything you like in that file. Note that a signer with the user name alice must exist in the database managed by javakey. When you run the program, you should see something like this:

% java SigFile
[B@1dc62277
Signature checks out

The first line of the output of the program is the signature used for signing the file; the second line tells us that alice's signature has been verified.

The java.security.Key Interface

The Key interface is the base interface for all keys. The Key interface defines the characteristics shared by all Key objects. These characteristics are listed here:

  • An algorithm: This is the algorithm for that particular key. It is an encryption algorithm such as RSA or DSA. The name of the algorithm of a key can be obtained using this method:

public abstract String getAlgorithm()

This method returns the name of the algorithm for that key. A null value is returned if the algorithm for this key is unknown.

  • An encoded form: This is an external encoded form for the key used when a representation of the key is required outside the Java virtual machine. The key is encoded according to a standard format: X.509. The encoded key can be obtained by calling this method:

public abstract byte[] getEncoded()

A null value is returned if the key does not support encoding.

  • A format: This is the name of the format of the encoded key, which can be obtained by a call to this method:

public abstract String getFormat()

A null value is returned if the key does not support encoding.

In addition to the Key interface, you should be aware of two other key interfaces that are part of the Security API. The PrivateKey interface provides neither methods nor constants. It groups all private-key interfaces. Specialized private-key interfaces such as DSAPrivateKey extend this interface.

The PublicKey interface provides neither methods nor constants. It groups all public-key interfaces. Specialized public-key interfaces such as DSAPublicKey extend this interface.


NOTE: Interfaces are useful for designing reusable object-oriented software libraries and frameworks. The PublicKey and PrivateKey interfaces are methodless and are used mainly for type safety and type identification purposes.

The KeyPair Class

The KeyPair class extends the Object class. This class is a holder for a key pair--a public key and a private key. This class has two methods: public PublicKey getPublic() returns the public key, and public PrivateKey getPrivate() returns the private key.

The KeyPairGenerator Class

The KeyPairGenerator class is used to generate a pair of keys: a public key and a private key. You can generate keys using KeyPairGenerator in two ways: by using algorithm-independent or algorithm-specific methods. The only difference between the two approaches is the initialization of the object. You must initialize a KeyPairGenerator before you can use it to generate keys. Note that algorithm-independent initialization is sufficient for most cases; however, if you want more control over the parameter of the algorithm, you can use a particular algorithm initialization approach.

To generate a pair of keys, follow these steps:

1. Create a KeyPairGenerator object for generating keys. This line of Java code creates an instance object of KeyPairGenerator to generate keys for the DSA algorithm:

KeyPairGenerator keys = KeyPairGenerator.getInstance("DSA");

2. Initialize the KeyPairGenerator. Before you initialize the KeyPairGenerator, you must decide whether you want to generate keys using an algorithm-independent or algorithm-specific approach. For demonstration purposes, let's do it both ways:

Algorithm-independent initialization:

Initialization can be done using KeyPairGenerator's method, initialize():

public abstract void initialize(int strength, SecureRandom random)

The strength argument refers to the strength of the key; the random argument refers to the source of randomness--which can be obtained from the SecureRandom class, described later in this chapter. If you want to generate your keys with a modulus length of 1024 and an automatically seeded random number, you can use the following code:

keys.initialize(1024, new SecureRandom());

Algorithm-specific initialization:

As you can see in the preceding line of code, we have accepted the default parameters supplied by the SUN provider. However, if you want to have control over those parameters, you may use algorithm-specific initialization. In this approach, you must set the DSA-specific parameters: p, q, and g. q is a 160-bit prime number. p is a prime number with a length between 512 and 1,024 bits such that q divides (p-1); g is a number of the form h((p-1)/q) mod p where h is an integer between 1 and (p-1).

If you don't want to accept the default values for these parameters, set them yourself. You can do this with the following code:

DSAParams params = new DSAParams(p, q, g);
DSAKeyPairGenerator dsaKeys = (DSAKeyPairGenerator)keys;
dsaKeys.initialize(params, new SecureRandom());

In this example, we cast the KeyPairGenerator object created with KeyPairGenerator.getInstance() to a specific algorithm--DSA in this case, because that is what we want to use. The DSA algorithm implements the DSAKeyPairGenerator.

3. Generate the key pair using this code:

KeyPair pair = keys.generateKeyPair();

This statement generates a pair of keys: a public key and a private key. If you want to extract the public key from the pair, use the getPublic() method of KeyPair:

pair.getPublic();

To extract the private key, use the getPrivate() method of KeyPair:

pair.getPrivate();

Listing 16.2 shows the complete program for using an algorithm-independent initialization.

Listing 16.2. Key pairs generation using an algorithm-independent initialization.

import java.security.*;

class Ks {
    public static void main(String argv[]) {
        KeyPairGenerator keys = null;
        try {
          keys = KeyPairGenerator.getInstance("DSA");
        } catch(NoSuchAlgorithmException e) {
          System.err.println(e);
        }
        keys.initialize(1024, new SecureRandom());
        KeyPair pair = keys.generateKeyPair();

       System.out.println(pair.getPublic().toString());  // public key
       System.out.println(pair.getPrivate().toString()); // private key
    }
}

If you compile the code in Listing 16.2 and run it, you generate a pair of keys using algorithm-independent initialization. You should see something similar to the following. In this output, the y value refers to the public key, and the x value refers to the private key.

% java Ks        # the % is my csh prompt under unix
Sun DSA Public Key
parameters:
p: fd7f53811d75122952df4a9c2eece4e7f611b7523cef4400c31e3f80b6512669455d402251fb593d8d58fabfc5f 
5ba30f6cb9b556cd7813b801d346ff26660b76b9950a5a49f9fe8047b1022c24fbba9d7feb7c61bf83b57e7c6a8a61 
50f04fb83f6d3c51ec3023554135a169132f675f3ae2b61d72aeff22203199dd14801c7


q: 9760508f15230bccb292b982a2eb840bf0581cf5
g: f7e1a085d69b3ddecbbcab5c36b857b97994afbbfa3aea82f9574c0b3d0782675159578ebad4594fe67107108180b4 
49167123e84c281613b7cf09328cc8a6e13c167a8b547c8d28e0a3ae1e2bb3a675916ea37f0bfa213562f1fb627a01243 
bcca4f1bea8519089a883dfe15ae59f06928b665e807b552564014c3bfecf492a

y: 47006b52c5c2b766f6e6864758f4a79fd4f069317de66a13093f42ac32176217f23d174899dc02ffca009c21f78f77c 
4c258bdfa8069fd493148b78da6d5860216793a1b51faa9a7800867bcba912ca762847d30a57f87e4676dff91bbeb0d35c 
a2591d4c4293b910435fc552a346fc2bfef921f6b398d1a9531a486fbc28ff0

Sun DSA Private Key
parameters:
p: fd7f53811d75122952df4a9c2eece4e7f611b7523cef4400c31e3f80b6512669455d402251fb593d8d58fabfc5f5ba30f 
6cb9b556cd7813b801d346ff26660b76b9950a5a49f9fe8047b1022c24fbba9d7feb7c61bf83b57e7c6a8a6150f04fb83f6d 
3c51ec3023554135a169132f675f3ae2b61d72aeff22203199dd14801c7

q: 9760508f15230bccb292b982a2eb840bf0581cf5
g: f7e1a085d69b3ddecbbcab5c36b857b97994afbbfa3aea82f9574c0b3d0782675159578ebad4594fe67107108180b44916 
7123e84c281613b7cf09328cc8a6e13c167a8b547c8d28e0a3ae1e2bb3a675916ea37f0bfa213562f1fb627a01243bcca4f1b 
ea8519089a883dfe15ae59f06928b665e807b552564014c3bfecf492a

x: 3669898a02adf49267ac1f07fd49952132c133b4

The SecureRandom Class

This SecureRandom class extends Random and provides a platform-independent, cryptographically strong random-number generator. You can create an instance of SecureRandom in two ways: by using the default (automatically generated) seed or by providing the seed yourself. Not surprisingly, then, there are two constructors of SecureRandom:

SecureRandom()
SecureRandom(byte[] seed)

The SecureRandom class provides five methods for using and reseeding a SecureRandom object:

  • public synchronized void setSeed(byte seed[]) This method reseeds the SecureRandom object, where the given seed supplements (rather than replaces) the existing seed. Repeated calls never reduce randomness.

  • public void setSeed(long seed) This method reseeds a SecureRandom object using the eight bytes in the given long argument. As with the preceding method, the given seed supplements the existing one.

  • public synchronized void nextBytes(byte bytes[]) This method generates a user-specified number of random bytes. The array given as argument is filled with random bytes.

  • protected final int next(int numBits) This method generates an integer containing the user-specified number of random bits.

  • public static byte[] getSeed(int numBytes) This method returns the given number of seed bytes,.

You have already seen how to use methods from this class in the example in Listing 16.2.

The Identity Class

The Identity class is one of the key-management classes and is the basic key-management entity. This class represents real-world objects such as people, organizations, and so on whose identities can be authenticated using their public keys.

An Identity object has a name and a public key. An Identity object may also have a scope, as described in the following section. An Identity object also has a set of certificates that certify its public key. The Identity class can be inherited so that you can add your e-mail address, an image of your face, and so on.

The Identity class has a handful of methods. However, the most important ones are those that return the object's name, public key, and scope:

public final String getName()
public final IdentityScope getScope()
public PublicKey getPublicKey()

Note that the methods getName() and getScope() are final, thus the name and scope of the Identity object are immutable.

The IdentityScope Class

The IdentityScope class is a subclass of the Identity class. This class merely serves as an abstraction for repositories of Identity objects. These repositories can be used, for example, to assign permission and verify classes being loaded. They can also be used by signing tools to retrieve private keys and generate digital signatures.

An IdentityScope object is an Identity object itself. Thus, an IdentityScope object has a name, a public key, and a scope. Note that no two objects in the same scope can have the same public key.

Some of the important methods in the IdentityScope class are listed here:

  • public abstract void addIdentity(Identity identity) throws KeyManagementException This method adds an identity to this IdentityScope. An exception is thrown if the identity is not valid, if there is a name conflict, or if another identity has the same public key.

  • public abstract Identity getIdentity(String name) public abstract Identity getIdentity(PublicKey key) With these two methods, you can retrieve an identity, given either its name or its public key.

  • public abstract void removeIdentity(Identity identity)throwsKeyManagementException This method removes an identity from this IdentityScope. An exception is thrown if the identity is missing.

  • public abstract Enumeration identities()With this method, you can get an enumeration of all identities in this identity scope.

Listings 16.3 through 16.5 show some examples of how to retrieve identities.

Listing 16.3. Retrieving an identity, given its name.

import java.security.*;

// retrieve an identity given its name
class ID {
    public static void main(String argv[]) {
        IdentityScope id = IdentityScope.getSystemScope();
        // please see javakey tool below to see how we created the identity "alice".
        Identity d  = id.getIdentity("alice");
        System.out.println(d.toString());
   }
}

Compiling and running this program gives an output similar to the following. Note that if an identity with the user name alice has not been created, the program returns an error.

% java ID
[Signer]alice[identitydb.obj][trusted]

Listing 16.4. Retrieving an identity, given its public key.

import java.util.*;
import java.security.*;

// retrieve all the identities in the system scope
class Ids {
    public static void main(String argv[]) {
        IdentityScope id = IdentityScope.getSystemScope();
        Enumeration e = id.identities();
        while (e.hasMoreElements()) {
           System.out.println(e.nextElement());
        }
    }

}

Compiling and running this code gives the following output:

% java ID2
[Signer]alice[identitydb.obj][trusted]

Listing 16.5. Retrieving all identities in the system scope.

import java.util.*;
import java.security.*;

// retrieve all the identities in the system scope
class Ids {
    public static void main(String argv[]) {
        IdentityScope id = IdentityScope.getSystemScope();
        Enumeration e = id.identities();
        while (e.hasMoreElements()) {
           System.out.println(e.nextElement());
        }
    }
}

Compiling and running this program gives the following output. Note that your output will differ depending on how many identities have you created.

% java Ids
[Signer]duke[identitydb.obj][trusted]
jane[identitydb.obj][not trusted]
[Signer]alice[identitydb.obj][trusted]

The Signer Class

The Signer class extends the Identity class. The Signer class can be used to represent an entity that can sign data. Such an entity must have a public and a private key.

There are two main methods in the Signer class:

  • public PrivateKey getPrivateKey() This method returns the signer's private key.

  • public final void setKeyPair(KeyPair pair) throws InvalidParameterException, KeyException This method is used to set the key pair for the signer. An exception is thrown if the key pair is not properly initialized.

An example of how to use this class was shown in Listing 16.4, which retrieved an identity given its public key.

The Provider Class

The Provider class represents a Security Package Provider (SPP, but provider for short). A provider is a package or a set of packages that implements some or all parts of Java security, including algorithms (such as DSA, RSA, MD5, and so on) and key-generation and key- management facilities.

Each provider has a name and a version number. JDK 1.1 comes with a default provider with the name SUN. The main methods in the Provider class are listed here:

  • public String getName() This method returns the name of the provider

  • public double getVersion() This method returns the version number of the provider.

  • public String getInfo() This method returns information about the provider such as a description of the provider and its services.

Listing 16.6 shows an example of the usage of this class.

Listing 16.6. Using the SUN provider and the Provider class methods.

import java.security.*;

class Supp {
    public static void main(String argv[]) {
        Provider p = Security.getProvider("SUN");  // please see the Security class below
        System.out.println(p.getName());           // name of the security package provider (SPP)
        System.out.println(p.getVersion());        // the version number
        System.out.println(p.getInfo());           // information about the SPP
    }
}

Compile and run this code to see the following output:

% java Supp
SUN
1.0
SUN Security Provider v1.0, DSA signing and key generation, SHA-1 and MD5 message digests.

The Security Class

The Security class is a subclass of Object. The Security class has two main purposes:

  • To manage security properties

  • To manage Security Package Providers (SPPs)

The Security class provides only static methods and is never instantiated. Thus, the methods in this class can be called only from a trusted program. Here, a trusted program refers to either a local application or a trusted applet.

One of the main purposes of the Security class is to manage providers. Thus, this class provides methods to do just that:

  • public static int addProvider(Provider provider) This method adds a provider to the next position available. The method returns either the position in which the provider was added or -1 if the provider was not added for some reason (for example, it is already installed).

  • public static void removeProvider(String name) This method removes the provider with the specified name. Nothing is returned if the provider is not installed.

  • public static Provider[] getProviders()This method returns an array of all providers currently installed.

The small program in Listing 16.7 shows how to get a list of currently installed providers.

Listing 16.7. Getting a list of currently installed providers.

import java.security.*;

class Supps {
    public static void main(String argv[]) {
        Provider p[] = Security.getProviders();
        for (int i=0; i<p.length; i++) {
           System.out.println(p[i].toString());
        }
    }
}

Compiling and running this program gives the following output:

% java Supps
SUN version 1.0

The MessageDigest Class

The MessageDigest class is an engine that provides a cryptographically secure message digest such as SHA-1 (Secure Hashing Algorithm) or MD5. Message digests, or digital fingerprints, are used to produce unique identifiers of data. A digest has the following properties:

  • It should be computationally infeasible to find another input string that generates the same digest.

  • The digest does not reveal anything about the input string that was used to generate it.

There are three steps to follow in computing a message digest:

1. Create a MessageDigest object by calling this method:

public static MessageDigest getInstance(String algorithm)

If you want to specify a particular provider, call this method:

public static MessageDigest getInstance(String algorithm, String provider)

2. Update the MessageDigest object by making one or more calls to one of the update() methods:

public void update(byte input)

public void update(byte[] input)

public void update(byte[] input, int offset, int len)

3. Compute the digest (hash) by calling one of the digest() methods:

public byte[] digest()

public byte[] digest(byte[] input)

Listing 16.8 is an example that shows how to compute a digest.

Listing 16.8. Computing a digest.

import java.util.*;
import java.security.*;

// message digest
class Med {
    public static void main(String argv[]) {
        MessageDigest md = null;
        try {
          md = MessageDigest.getInstance("SHA");
        } catch(NoSuchAlgorithmException nsa) {
           System.err.println("Error: "+nsa);
        }
        String alg = md.getAlgorithm();  // what algorithm are we using? obviously SHA
        System.out.println(alg);

        byte b[] = new byte[100];
        for (int i=0; i<100; i++) {
           b[i] = (byte) i;
        }
        md.update(b);
        byte[] hash = md.digest();
        System.out.println(hash.toString());


    }
}

Compile and run this program to see the following output. The first line of output is the algorithm used (SHA), and the second line is the hash code generated.

% java Med
SHA
[B@1dc607a2

The javakey Tool

The javakey tool manages a database of entities, their key pairs (public key and private key), and certificates. The tool is also used to generate and verify signatures for Java ARchive (JAR) files.

The javakey tool always generates signatures using the Digital Signature Algorithm (DSA). In its current implementation, there is one database per user.

The javakey tool manages two kinds of entities: identities and signers. Identities are real-world objects, such as people and organizations that have a public key associated with them; signers have private keys in addition to public keys associated with them. Note that you can sign files only with private keys--you cannot use public keys to sign files. Also note that before signing files, you must generate a public and private key pair. Using javakey, you can either import existing keys or generate new ones for entities and signers.

Entities (identities and signers) have a user name in the database managed by javakey. The user name is local to the database, so it does not have to be the same user name as your login ID on a UNIX machine, for example. User names can be specified when you add entities to the database. The javakey tool has many options you can specify on the command line, as shown in the following examples:

  • Create a trusted signer called alice:
% javakey -cs alice true
Created identity [Signer]alice[identitydb.obj][trusted]
  • Create a trusted entity called bob:
% javakey -c bob true
Created identity bob[identitydb.obj][trusted]
  • Generate a key pair for alice using 512 as the key size and writing a copy of the public key to the file /tmp/pk:

% javakey -gk alice DSA 512 /tmp/pk
Generated DSA keys for [Signer]alice[identitydb.obj][trusted] (strength:512).
Saved public key to /tmp/pk.

  • Get detailed information about the signer alice:

% javakey -li alice
Identity: alice
[Signer]alice[identitydb.obj][trusted]
public and private keys initialized
certificates:
No further information available.

Certificates

A certificate is a digitally signed statement from one signer that says the public key of some other entity (the identity of a signer) has a particular value. If you trust the entity that signed the certificate, then you trust that the association between the specified public key and another entity is authentic.

Generating a Certificate

You must first generate a directive file before generating a certificate. In the directive file, you must supply the following information:

  • Information about the signer of the certificate

  • Information about the entity whose public key is being authenticated

  • Information about the certificate itself

  • The name of a file in which to save a copy of the certificate (this is optional)

Listing 16.9 shows an example of a directive file. Note that lines starting with a pound sign (#) are comments. Note that the serial number must be unique to distinguish this certificate from other certificates signed by the issuer.

Also note that in the example in Listing 16.9, the signature.algorithm field is missing. This means that we are using the DSA algorithm. If you want to use another algorithm, you must use the signature.algorithm field to specify the algorithm you want to use.

Listing 16.9. A sample directive file.

# Filename: directivefile
# Information about the signer
issuer.name=alice
issuer.real.name=Alice Johnson
issuer.org.unit=Management
issuer.org=Sekurity Inc
issuer.country=SomeCountry

# Information about the entity whose public key is being authenticated
subject.name=alice
subject.real.name=Alice Johnson
subject.org.unit=Management
subject.org=Sekurity Inc
subject.country=SomeCountry

# Information about the certificate
state.date=1 March 1997
end.date=1 March 1997
serial.number=1002

# Name of a file in which to save a copy of the certificate
out.file=certif

To generate a certificate, issue the following command:

% javakey -gc directivefile
Generated certificate from directive file directivefile.

To display information about the certificate, issue this command:

% javakey -dc certif

[
X.509v1 certificate,
Subject is CN=Alice Johnson, OU=Management, O=Sekurity Inc, C=SomeCountry
Key:  Sun DSA Public Key
parameters:
p:
fca682ce8e12caba26efccf7110e526db078b05edecbcd1eb4a208f3ae1617ae01f35b91a47e6df63413c5e12ed0899bc d132acd50d99151bdc43ee737592e17

q: 962eddcc369cba8ebb260ee6b6a126d9346e38c5
g:
678471b27a9cf44ee91a49c5147db1a9aaf244f05a434d6486931d2d14271b9e35030b71fd73da179069b32e2935630e1 c2062354d0da20a6c416e50be794ca4
y:
2bfe2d23d60ce90554965bbb065b910c9fa874fa3b771a21850fd28222706c93409119b42f7d555ff86c267dae7241a93e 97c8a202dbe9f885e642e29bcad441
Validity <Fri Feb 28 16:00:00 PST 1997> until <Fri Feb 28 16:00:00 PST
1997>
Issuer is CN=Alice Johnson, OU=Management, O=Sekurity Inc, C=SomeCountry
Issuer signature used [SHA1withDSA]
Serial number =     03ea
]

Table 16.1 lists the options you can use with the javakey tool.


NOTE: The hyphen before each option is not necessary; however, you are encouraged to use it. Note that only one of the following options can be specified for each javakey command.

Table 16.1. The javakey tool options.

Option Description
-c identity {true | false} Creates a new identity in the database with the user name identity. The optional true or false specifies whether or not an identity is to be considered trusted. The default is false.
-cs signer {true | false} Creates a new signer in the database with the user name signer. The optional true or false specifies whether or not an identity is to be considered trusted. The default is false.
-t identityOrSigner {true | false} This option sets or resets the trust level for the specified identity or signer.
-l Lists all the user names in the database.
-ld Lists the user names in the database with detailed information about them.
-li identityOrSigner Gives detailed information about the specified identity or signer.
-r identityOrSigner Deletes the specified identity or signer from the database.
-ik identity keyfile Imports the public key in the file keyfile and associates it with the specified identity.
-ikp signer pubfile privfile Imports the public key in the file pubfile, and the private key in the file privfile, and associates them with the signer.
-ic identityOrSigner certificateFile Imports the public key certificate in the file certificateFile and associates it with the specified identity or signer. If a public key is associated with the identity or signer in the database, javakey makes sure that the public key is the same as the one in the file certificateFile.
-ii identityOrSigner Sets information for the specified identity or signer. When using this option, you can type as many lines of information as you want; when you are finished, type a period on a line by itself to exit.
-gk signer algorithm keysize Generates a key pair for the specified algorithm {pubfile} {privfile} signer using the specified with the keysize (strength). The key size has to be between 512 and 1024 bits. Optionally, you can specify a public key file and/or a private key file. If those options are specified, the generated keys are also written in those files. When you use this option to generate keys, you must type some random characters and end with a period on a line by itself. These random characters are used to generate the random number to be used by DSA.
-g signer algorithm keysize This option is a shortcut for the -gk option to {pubfile} {privfile}generate a key pair for the specified signer.
-gc directivefile Generates a certificate according to the information provided in the file directivefile.
-dc certfile Shows the certificate stored in the file certfile.
-gs directivefile jarfile Signs the jarfile according to information supplied in the directivefile.

Summary

After reading this chapter, you should be familiar with the Java Security API. This chapter introduced you to the important classes from the Java Security API that you can incorporate into your Java-based applications.

Although some of the examples given in this chapter are not full-fledged applications, they do demonstrate how to use the Java Security API. You can find more information about Java security issues in Chapter 34, "Java Security."

TOCBACKFORWARDHOME


©Copyright, Macmillan Computer Publishing. All rights reserved.