Digital signatures provide a way to authenticate documents and other data. They solve one of the Internet's biggest problems: given that you've received a message from Ms. X, how do you know that the message really came from Ms. X and not an imposter? Just as important for Java, let's say that you've downloaded a great new applet written by your favorite author, Pat Niemeyer, and you'd like to grant it some additional privileges, so that it can do something cool for you. You trust that this particular author wouldn't intentionally distribute something harmful. But how do you know that the author really is who he says he is? And what if you downloaded the applet from a third party location, like an archive? How can you be sure that someone hasn't modified the applet since the author wrote it? With Java's default security manager, such an applet can't do anything serious, but when we're talking about configuring your browser to grant additional privileges to applets coming from trusted sites, you would be in for trouble--if it weren't for digital signatures.
Like their inky analogs, digital signatures associate a name with an item in a way that is difficult to forge. In reality, a digital signature is actually much more difficult to forge than a traditional signature. Furthermore, digital signatures provide another benefit: they allow you to authenticate a document, proving that it hasn't been altered in transit. In other words, you know who the sender is, and that the data you received is exactly what the sender sent. Some malicious person can't clip out a digital signature, modify the original document (or applet), and attach the old signature to the result. And he can't generate a new signature--at least, he can't generate a signature claiming that the document came from its original sender. (He could, of course, attach his own signature--but that would be like signing the stick up note you hand to the bank teller.)
Digital signatures are based on public-key cryptography, which is beyond the scope of this book. However, the basics are important and interesting. In a public-key system, there are two pieces of information: a public key (as you might have guessed) and a private one. They have a special, asymmetric relationship, such that a message encrypted with one key can only be decrypted with the other key. Furthermore, if you only know one key, it is very difficult to compute the other. Therefore, if I give you my public key, you can send me messages that only I can read. No one else, including you, has enough information to go through the process of decrypting the encoded message, so it's safe to send it over untrusted networks. Furthermore, I can (and probably will) give my public key to anyone in the world, since the public key only lets people send me messages; it doesn't let them read my messages.
 See Bruce Schneier's encyclopedic Applied Cryptography (John Wiley & Sons).
Digital signatures are based on the reverse process. If I encrypt something with my private key, anyone can use my public key to read the message. That may not sound very useful, since I already said that I'd give my public key away to anyone who wants it. But in this case, we're not trying to keep the message secret, we're trying to prove that I'm the only one who could have sent the message. And that's exactly what we've done. No one else has my private key, so no one else can send a message that can be decrypted with my public key. Therefore, only the real me could have sent the message.
I've simplified the process in one crucial way. Encrypting a large message with complex algorithms takes a long time, even with fast computers. And some public key algorithms just aren't suitable for encrypting large amounts of data for other reasons as well. For digital signatures then, we don't usually encrypt the entire message. First, we use a standard algorithm to create a "hash" or "message digest." We've already seen a message digest; they're the special SHA and MD5 values that jar puts into the manifest. To produce the signature, we then encrypt the (relatively small) message digest with the private key. The recipient can then decrypt the signature with the public key and check whether or not the resulting message digest matches the message he received. If it does, the recipient knows that the message hasn't been altered and that the sender is who he claims to be.
Digital signatures can be used to authenticate Java class files and other types of data sent over the network. The author of an object signs the data with his or her digital signature, and we use the author's public key to authenticate that signature after we retrieve it. We don't have to communicate with anyone in order to verify the authenticity of the data. We don't even have to make sure that the communications by which we received the data are secure. We simply check the signature after the data arrives. If it is valid, we know that we have the authentic data and that it has not been tampered with . . . or do we?
Well, there is a larger problem that digital signatures alone don't solve: verifying identity. If the signature checks out, we know that only the person (or entity) that published the public key could have sent the data. But how do we know that the public key really belongs to whomever we think it does? How do we associate an identity with that public key in the first place? We've made it more difficult to counterfeit a message, but it's not impossible. A forger could conceivably create a counterfeit Java class, sign it with his own private key, and try to trick you into believing that his public key is that of the real author or the trusted web site. In this case, you'll download the bad applet, then use the wrong public key to verify the applet, and be tricked into thinking that there's nothing wrong. This is where certificates and certificate authorities come into play.
A certificate is a document that lists a name and a public key. By a name, we mean some real world information describing a person or entity. For example, a certificate might contain your full name and address, or the name of a company and the location of its headquarters. We'll consider the combination of a name and a public key in this way to make up an identity. If we have valid information for a particular identity, we can verify data that the identity has signed.
A certificate is signed with the digital signature of a certificate authority--the entity that issued the certificate. The certificate is, in effect, a proclamation by the certificate authority that the identity listed is valid--in other words, that the listed public key really does belong to the entity named. If we decide to trust the certificate authority, we can then believe the identities contained in the certificates it issues are valid. The certificate acts as a sort of electronic ID card, backed up by the credentials of the certificate authority. Of course, we no longer issue certificates on fancy vellum scrolls, as shown in Figure 3.2; the format for modern certificates is described by a standard called X.509.
This is all well and good, but the original problem remains: in order to verify the authenticity of a certificate, we need to verify its signature. Now, to do that, we need to know the certificate authority's public key; rather than solving the problem, we simply seem to have shifted the problem to a new front. If a counterfeiter could substitute her public key for the public key of one entity, she might be able to do the same for the certificate authority. But shifting the problem helps quite a bit. We have reduced the number of public keys that we need to know from an unlimited number (all of the identities we might ever encounter) to a very small number: one for each certificate authority. We have chained our trust of the identity to the trust of the certificate authority's identity. Chaining can be allowed to extend further, to an arbitrary depth, allowing certificate authorities to back up lower certificate authorities, and so on. At some point, of course, the chain has to stop, and that usually happens with a "self-signed" or certificate authority certificate; that is, a certificate that is issued by the certificate authority for itself, containing its own public key. "What good is that?" you might ask.
As for the authenticity of the top-level certificate authorities themselves, we will have to rely on very strong, well-known certificates that we have acquired by very secure or perhaps very tangible means. Web browsers, like Netscape Navigator and Microsoft Internet Explorer, come with CA certificates for several popular certificate authorities. Netscape Navigator and MSIE are, for example, shipped with a CA certificate for Verisign (http://www.verisign.com/), so that you can safely verify any certificates signed by Verisign, wherever you encounter them. So, if all is working, we've reduced the problem to just that of your getting your copy of the Web browser software securely the first time. As far as maintenance goes, browsers like Netscape Navigator let you download new CA certificates dynamically, using secure transports.
Certificates are presented to your Web browser for verification when you encounter signed objects (signed JAR files). They are also issued by Web servers when you make a secure connection using the HTTPS (HTTP Secure Socket Layer) protocol. Browsers like HotJava, Netscape, and Internet Explorer may save these certificates encountered from third party locations, so that you can assign privileges or attributes to those identities and so that they can be recognized again. We'll call these certificates site certificates--though they may belong to any third party, like a person or an organization. For example, you might declare that objects signed by a certain site are allowed to write local files. The browser then saves that site's certificate, marking it with the privileges (writing local files) that it should grant.
Finally, you, the user, can have your own identity and your own certificates to validate your identity. Browsers like Netscape Navigator store user certificates that can be used to identify you to third parties. A user certificate is associated with a private key--the private key that goes with the public key in the certificate. When you use a private key to sign an object, the corresponding certificate is shipped as part of the signature. Remember, the recipient needs the public key in your certificate to validate your signature. The certificate says on whose authority the recipient should trust that public key.
So, where do you get private keys, public keys, and certificates validating your public keys? Well, as for the keys, you generate those yourself. No other party should ever have access to your private key, much less generate it for you. After you generate a public and private key pair, using a utility like javakey, you send your public key to the certificate authority to request that they certify you. The CA can make you jump through whatever hoops are necessary; when they are satisfied that you are who you say you are, they grant you a certificate.
In Netscape Navigator, this entire process can be accomplished by the user, within the browser, using the KEYGEN extension to HTML. You can then use Netscape tools to sign JAR files, send secure email, etc. HotJava is not quite as slick. The JDK supplies a utility called javakey for managing keys, certificates, and signing JAR files. We'll discuss that in detail in the next section.
For a variety of reasons, including antiquated cryptography export control laws in the U.S. and patents on various cryptographic algorithms, JavaSoft was forced to separate the security API itself from the packages that actually implement encryption algorithms. The packages that implement cryptography are called "provider" packages; Sun's security provider package comes with the JDK by default. Other packages can be installed to provide additional or alternate implementations of the cryptographic alogrithms. By default, javakey uses the implementations found in Sun's provider package, though it can use other packages if any are available.
 See the JCE (Java Cryptography Extensions), which are available to U.S. citizens from http://java.sun.com/products/jdk/1.1/jce/.
The user interface to javakey is awkward, to say the least. It's a good bet that someone will implement a key management utility with a more friendly graphical interface; it wouldn't be surprising if this utility was built into HotJava. Therefore, we won't spend a great deal of time discussing the details of javakey; it's more important to understand the concepts.
Netscape's Navigator 4.0 has extensive support for signed JAR files. Netscape has extended the core Java security model to create a capabilities-based model in which signed objects request permission for specific operations. (These are the kinds of features found in the U.S. government's C2 security specification.) Because Netscape's extensions are not part of the core Java API, we won't go into them here. Netscape says that it is possible to write code that will function in both Netscape's security model and the basic Java model.
Unfortunately, there are currently incompatabilities between the Netscape and Java digital signature implementations. The largest obstacle is that Netscape supports Version 3 of X.509 certificates, while Java currently supports Version 1 only. Support for X.509 v3 certificates is promised in JDK 1.2. Future releases of Netscape Navigator, HotJava, and others should be able to share certificates and signed JAR files.
Microsoft has plans for Java object-signing capabilities in Internet Explorer 4.0. They too are planning to extend Java's security model.
Before we dive into the muck, let's take a look at an example, just to prove that you can actually sign objects with the current releases of the JDK and HotJava. In the process of discussing the example, we'll point out some things that are lacking.
Make sure that HotJava's security is set to the default level, and navigate to http://www.ooi.com/exploringjava/examples/jar/unsigned/testwrite.html. You'll see the applet shown in Figure 3.3.
When you push the button, this applet attempts to write a harmless file on the local host. Give it a try. If you do have HotJava's security at the default level, a dialog will pop up, asking if you'd like to authorize the write operation. Feel free to click No. The applet should fail with a security exception and display a message. Had you clicked Yes, the action would have been authorized for this session, but the next time you came across this applet, you would have to authorize the write operation again.
At this point you can navigate to a Web page that displays the same applet: http://www.ooi.com/exploringjava/examples/jar/signed/testwrite.html. The only difference is that I've wrapped the applet in a JAR file that I have signed, with a key and certificate that I generated using javakey. When it loads the applet's JAR file, HotJava also retrieves the signature. However, nothing has changed for the applet just yet. When you click the button, you still get a dialog asking if you want to allow the write operation. If you say Yes, HotJava allows the applet to write. As before, the permission is good for this session only; if you run across the applet again, you'll be asked to authorize the write operation.
But now that an identity is associated with the applet, we can do more. Select the Security Preferences page from the HotJava menu, and go to the Advanced Security section. You should see my certificate in the list, as shown in Figure 3.4.
Pat Niemeyer, O'Reilly & Associates, Java Author
This is where we need to be in order to grant this applet greater (or lesser) permissions based on my signature. But there is a problem. HotJava doesn't recognize the certificate authority that signed my certificate. That's because I signed my own certificate (I've decided to be my own certificate authority of one, for now). What can we do? Well, in Netscape or a future version of HotJava, we might have a means to download my certificate and use it as a new certificate authority. Or I might simply get a certificate signed by a more popular authority than myself.
For now, you can verify my certificate by hand. We'll do this not only to get through this example, but because it presents a useful analogy to the process of certification. By selecting my certificate and clicking the Details button, HotJava presents you with a dialog showing my certificate's information, which is shown in Figure 3.5.
The "thumbprint" that HotJava displays should match this:
If everything looks good, you can click on the box to accept my certificate. By doing so, you are saying that you accept my identity, and believe that I have in fact signed certificates that match this thumbprint. You have effectively chained your trust to this printed page rather than an online certificate authority. You can be reasonably sure that no one has gone to the effort of issuing counterfeit O'Reilly books.
You can now assign greater privileges to this applet. For example, you can allow it to write files. HotJava stores the certificate and marks it with whatever permissions you have decided to grant. The next time you visit this page (or find this applet anywhere else on the Web!), it will be allowed to write the file.
 HotJava 1.0 stores its site certificates in the user's .hotjava/security directory. It does not add these certificates to the Java system identity database. In Java 1.2, the certificate database will be separated from the system's identity database.
So, how did I create my certificate and sign the JAR file? Let's move on.
The Sun security provider package implements an identity database that holds a set of identities along with their public keys, private keys, and certificates. The identities in this database are visible to Java. We'll discuss what that means in a bit, but for the most part, we'll only use this database as a repository while we create and work with our identity locally.
An identity can be a person, an organization, or perhaps a logical part of an organization. Before it can be used, an identity must have a public key and at least one certificate validating its public key. javakey refers to entities in the local database by IDs or handles. These names are arbitrary and are not used outside of the local database. Identities that have a private key stored locally in the database, along with their public key, can be used to sign JAR files. These identities are also called signers.
The default location for the identity database is the file:
identitydb.obj in the user's home directory. On a single
user system, the Java installation directory is used instead of the
user's home directory.
As an option, the system
administrator can take control of the database and put it in a
unique location systemwide. You can control the location by setting
identity.database property in the
java.security properties file (located in the
lib/security directory of the JDK installation):
 Beware if you have both the JDK and HotJava installed in separate locations on a single user system, such as a PC running Windows. You will have to inform HotJava where to find the JDK package using the
If you are going to maintain any private keys in the identity database (if you will have any signers), you must take special care to keep the identities.obj file safe (and not publicly readable). Private keys must be kept private.
% javakey -cs sarah
-c option tells javakey to create a new identity.
s says that this identity is to be a signer: that is, it will be capable of
signing JAR files using its private key. You can leave off the
you are creating an identity that will only be used locally and won't
be used to sign files.
sarah, is the name I'm
assigning to the new identity. I could have used a
longer name by quoting it; however, since this ID is only used as a
convenient way to access the local database, the name doesn't really
% javakey -li sarah
Or we can list the entire contents of the database:
% javakey -ld
So far, we've created an identity but haven't created any keys for it. Since we're on our way to signing JAR files, the next thing we need to do is create a key pair for our signer. We can create a key pair with the command:
% javakey -gk sarah DSA 1024
gk option tells javakey to
generate a key. The first argument,
sarah, is the ID for which we are going to
keys. The next argument,
DSA, is the
algorithm for which we are going
to generate the keys. The current release of Java only supports one:
DSA, the Digital Signature Algorithm, which is a U.S. government
standard for signing. The
final argument is the key length in bits. For most algorithms, larger
key sizes provide stronger encryption. DSA supports keys of either
512 or 1024 bits. You should use the latter, unless you have
a specific reason to do otherwise. It's not clear what a valid reason would be for using the smaller key size, particularly in the
context of signed applets.
javakey generates the keys and places them in the database. To retrieve the public key we have generated, give the command:
% javakey -ek sarah sarah.public
-ek option tells javakey to
export the key belonging to the named identity. The next
sarah.public, is the name of the file into which
javakey places the public key it retrieves. Obviously, we could
have picked any filename we wanted. Once you've exported a key, you
can move the file to another system and import it into another key
To retrieve both public and private keys, give the command below; the third argument, sarah.private, is the name of the file in which javakey stores the private key. Be careful about exporting any private key. It should never be stored in an unsecure or publicly readable location.
% javakey -ek sarah sarah.public sarah.private
% javakey -ik sarah sarah.public
% javakey -ikp sarah sarah.public sarah.private
Now that we have keys, we want a certificate in which to wrap our public key for distribution. Ideally, at this point, we'd send a public key to a trusted certificate authority and receive a certificate in return. A number of different and incompatible standards for certificates are available. Currently, javakey understands and generates only X.509 Version 1 certificates; it should soon be able to work with X.509 Version 3 certificates. Once you have received a compatible certificate from a certificate authority or some other source you trust, you can import the certificate from a file with the command:
% javakey -ic sarah sarah.x509
The file sarah.x509 contains the certificate for the identity sarah in X.509 format. If you want this identity to be able to sign files, you should have already placed the matching private key in the database.
To demonstrate the features of javakey, we will serve as our own authority (as we did in the example) and create our own self-signed certificate. To create a certificate we have to make a "directives" file. It might look something like this:
issuer.name=sarah subject.name=sarah subject.real.name=Sarah Burcham subject.org.unit=Student subject.org=Washington University subject.country=US start.date=1 Jan 1997 end.date=1 Jan 1998 serial.number=1001
This file includes all the information
necessary to generate a certificate. The first piece of information,
issuer.name, specifies the
javakey ID of the identity that is creating the
certificate. This identity must be a signer and must have public and
private keys initialized. In this case we are signing our own
certificate, so we don't have
to have a certificate to back up our signature.
The next few lines describe the subject of the certificate (i.e., the person for whom we're issuing the certificate). It includes a "real world" name and organization information. The type of information in these fields is defined by X.500 standards; it is also the subject of much debate and extension activity. This information is very important because a certificate is useless if the receiver does not know exactly what it proports to be certifying.
The start and end dates describe the period in which the certificate is valid. An invalid certificate should not be honored.
should be a unique number assigned by the issuer (signer). You should
never issue two certificates with the same serial number. We've
picked an an arbitrary number--1001--here for a reason. Whenever
javakey imports or creates a certificate for an identity, it
assigns its own, internal (sequential), number to that certificate.
(In our case, javakey's serial number is probably "1",
because this is the first certificate associated with this identity.)
This can be confusing. javakey's certificate numbers are
like its identity names; they are used for reference only within
javakey. We will use the number that javakey has
assigned later, when we want to use a certificate to sign a JAR file.
The serial number included in the directive file, however, is a real
world piece of information that may be referenced outside of
If we have a signer identity, initialized with its private and public keys, and at least one certificate, we are ready to sign JAR files. We start by creating another directives file that specifies what identity we are using to sign the JAR file and some other parameters. This file contains at least four pieces of information; it might look like this:
signer=sarah cert=1 chain=0 signature.file=sarahSig
signer directive specifies the
identity that will be used to generate the signature.
cert is the javakey certificate
number of the certificate that will be included with the digital
signature--not the serial number that we assigned when creating this
certificate. To see javakey's certificate numbers, use
the javakey -li or -ld commands.
In the current release of the JDK, the
chain directive is
unimplemented; its value should be set to 0. In the future, this
will allow you to specify a depth of certificate chaining:
the maximum number of intermediate certificates that may be used to
authenticate this signature.
specifies the name of the signature
file to be placed in the signed JAR. The file is stored in the META-INF
directory, along with the manifest. Signature filenames should be eight
characters or less, and case is not significant. javakey
adds the extension .SF to the filename you give.
Now we're ready to sign a JAR file. Place the four directives into a file called sarah.sdirs, and give the command:
javakey -gs sarah.sdirs foo.jar
If we now list the archive, we will see that javakey has added two files to the META-INF directory: SARAHSIG.SF and SARAHSIG.DSA. SARAHSIG.SF is the signature file--it is like the manifest file for this particular signature. It lists the objects that were signed and the signature algorithms. SARAHSIG.DSA is the actual binary signature in PKCS format.
We've covered a lot of territory in the past few pages. Here's a summary of what we can and can't do. We can:
Use javakey to create an identity, and create public and private keys for that identity.
Use javakey to create a self-signed certificate for that identity, or import a certificate that has been issued by a certificate authority, containing the public key we generated.
Use javakey to sign a JAR file.
Tell HotJava (by hand) what certificates it should trust, and what permissions to grant to each identity.
Unfortunately, we can't do any of this automatically. We can't just tell HotJava, "I trust John Doe" and have HotJava try to look up a verifiable certificate for John Doe. Furthermore, we are personally responsible for the security of our javakey database. This is a major hole: it's important that private keys remain private; but anyone who walks up to my computer can use javakey to retrieve information about any identity that I have issued. It's easy to imagine that a future version of HotJava will make it much easier to work with signed classes. The other problem, keeping secrets secret, is much more difficult.