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."
|