Using Digital Signatures

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 this person 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 policies for web browsers, such an applet can't do anything serious, but when we're talking about granting additional privileges to applets coming from trusted sites, you would be in for troubleif 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 much more difficult to forge than a traditional signature. Furthermore, digital signatures provide another benefit: they allow you to authenticate the contents of 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 well beyond the scope of this tutorial. However, the basics are important and interesting.[2] In a public-key system, there are two pieces of information: a public key and a private one. The keys have a special, asymmetric relationship, such that a message encrypted with one key can only be decrypted with the other key. Furthermore, if you know only one key, it is very difficult to compute the other. Therefore, if I give you my public key, you can use it to create an encrypted message 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.

[2] See Bruce Schneier's encyclopedic Applied Cryptography (John Wiley & Sons) or Jonathan Knudsen's Java Cryptography (Oracle).

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 it does. 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. We'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." 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 the resulting message digest matches the message he received. If it does, the recipient knows the message hasn't been altered, and 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 hasn't been tampered with. Or do we? There is a larger problem 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.

Certificates

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 (CA)the entity that issued the certificate. The certificate is, in effect, a proclamation by the CA that the identity listed is validin other words, that the listed public key really does belong to the entity named. If we decide to trust the CA, 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 CA. Of course, we no longer issue certificates on fancy vellum scrolls, as shown in Screenshot-3; the digital format for modern certificates is described by a standard called X.509.

Certificate authority certificates

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. To do that we need to know the CA'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 CA. But shifting the problem helps quite a bit. We have reduced the number of public keys

Screenshot-3. An old-fashioned certificate
Java ScreenShot

we need to know from an unlimited number (all the identities we might ever encounter) to a very small number: one for each CA. We have chained our trust of the identity to the trust of the CA's identity. Chaining can be allowed to extend further, to an arbitrary depth, allowing CAs to back up lower CAs, 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 CA for itself, containing its own public key. "What good is that?" you might ask. As for the authenticity of the top-level CAs themselves, we have to rely on strong, well-known certificates that we have acquired by very secure or perhaps very tangible means. Web browsers typically come with CA certificates for several popular CAs. Netscape 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 let you download new CA certificates dynamically, using a secure connection.

Site certificates

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 Sockets Layer) protocol. Browsers like Netscape and IE 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 certificatesthough 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.

User (signer) certificates

Finally, you, the user, can have your own identity and your own certificates to validate your identity. Browsers store user certificates that can identify you to third parties. A user certificate is associated with a private keythe 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? 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, you send your public key to the CA 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. The Java SDK supplies the keytool utility to create and manage keys and certificates. We'll talk about it next.

The keytool Utility

keytool is the standard Java utility for managing a database of identities. With it, you can generate or import key pairs and certificates. You can then use these keys and certificates to sign JAR files used by the Java Plug-in. The packages that implement cryptography in Java are part of the Java Cryptography Extension (JCE). As of Java 1.4, the JCE is bundled with the standard version of Java. Prior to that, it was an optional package (and subject to export restrictions). The JCE provides both framework and implementations of algorithms. The implementations are called "provider" packages; Sun's security provider package comes with the SDK by default. Other packages can be installed to provide additional or alternate implementations of the cryptographic algorithms. By default, keytool uses the implementations found in Sun's provider package, though it can use other packages if any are available. The user interface to keytool is awkward. It's a good bet that someone will implement a key management utility with a friendlier GUI; maybe it will be supplied with a future version of Java. In any event, we won't spend a great deal of time discussing the details of keytool; it's more important to understand the concepts.

What about Netscape and Internet Explorer?

Before the debut of the Java Plug-in, Netscape and Microsoft both invented their own code-signing schemes. As a result, signed applets in the Java 1.1 world were a disaster. There were three different ways to sign and deploy code, one each for Netscape, Sun's HotJava, and Microsoft's IE. Unless you knew in advance that you had only one kind of browser worry about, you were pretty much out of luck. The Java Plug-in levels the field for signed applets, because the packaging and deployment strategy is the same for all browsers.

The TestWrite example

Before we dive into the details, let's look at an example to show that the process really works with the Java Plug-in. Use your browser to navigate to http://examples.oracle.com/learnjava3/TestWrite/Unsigned.html. (At the time of this writing, on the Mac, this example works in Safari. Other browsers on the Mac should catch up with the Java 5.0 release for OS X.) You'll see the applet shown in Screenshot-4. When you push the button, this applet attempts to write a harmless file on the local host. Give it a try. The applet should fail with a security exception and display a message.

Screenshot-4. An unsigned applet violating security policy
Java ScreenShot

Now try a web page that displays the same applet: http://examples.oracle.com/learnjava3/TestWrite/Signed.html. (Again, currently on the Mac, this example only works with the Safari web browser on OS X.) The only difference is that we've signed the JAR file containing the applet, with a key and certificate generated using keytool. When the Java Plug-in loads the applet's JAR file, it examines the signature. What happens next depends on whether you're using the latest Java Plug-in. With Java 1.4 or later, the Plug-in prompts the user to decide whether or not to trust the code. In that case, you get a dialog like Screenshot-5.

Screenshot-5. Signed applet prompt
Java ScreenShot

The dialog informs you that the applet is signed, in this case, by Pat Niemeyer, and asks if you want to grant access to it. You can get information about the certificate used to sign the JAR by clicking on View Certificate. The two fields of interest are Issuer and Subject. In this case, we have signed the JAR file with our own self-issued certificate; the issuer and subject (or signer) are both identified as "Pat Niemeyer." After pondering our trustworthiness, you can grant the TestWrite applet permissions or deny them. You can also specify whether the permissions last for one session or are remembered until the certificate expires. If you deny permissions, the applet may still run; it depends on what it tries to do and how it handles being denied access. In this case, the applet prints the security exception when it tries to write the file. If you granted the applet permissions, hitting the button should show a successful write. In the next section, we'll look at how we created our certificate and signed this JAR. If you have an older version of the Plug-in (Java 1.3 or lower), Java informs you it can't verify the signature because it doesn't know about our certificate, and older versions didn't prompt the user to accept unknown signers. We could manually install our certificate in the local Java environment, but this procedure is complicated. Fortunately, with the Plug-in, we can specify the version required if we want to. (If you are interested in learning how to install keys manually for older versions of Java, you can find out all about it in the expanded material included on the accompanying CD and on this tutorial's web site.)

Keystores, Keys, and Certificates

The SDK supports keystores that hold identities along with their public keys, private keys, and certificates. It includes the utility we covered earlier, keytool. We'll 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. keytool refers to entities in the local database by IDs or aliases. These names are arbitrary and are not used outside the keystore (and possibly local Java security policy files that reference the keystore). Identities that have a private key stored locally in the keystore, as well as a public key, are called signers. These identities can be used to sign JAR files. The default location for a keystore is the file .keystore in the user's home directory. On a single user system, the Java installation directory is used instead of the user's home directory. The default keystore location is used by keytool unless you specify another keystore with the -keystore option. If you are going to maintain any private keys in a keystore (if you will have any signers), you must take special care to keep the keystore file safe (and not publicly readable). Private keys must be kept private.

Public and private keys

We can create a new entry in the default keystore, complete with a key pair, with the following keytool command:

 C:\> keytool -genkey -alias Pat -keyalg DSA -keysize 1024 -dname "CN=Pat
 Niemeyer, \
 OU=Technical Publications, O=Oracle, C=US" -keypass secure \
 -storepass boofa


There are a lot of options to explain. The most important one is -genkey, which tells keytool to create a new key pair for this entry. A key pair enables this entry to sign code. The -alias option supplies an alias for this entry, Pat. The -keyalg argument, DSA, is the algorithm for which we are going to generate the keys. Java supports at minimum DSA, the Digital Signature Algorithm, which is a U.S. government standard for signing. Check the documentation for your Java installation to see what other algorithms may be supplied with it. The -keysize argument is the key length in bits. For most algorithms, larger key sizes provide stronger encryption. DSA supports keys of either 512 or 1,024 bits. You should use the latter, unless you have a specific reason to do otherwise. keytool generates the keys and places them in the default keystore. Private keys are specially protected using the -keypass option. To retrieve Pat's private key, you need the correct key password. The integrity of the keystore as a whole is protected by the -storepass option. You need to supply the same keystore password to retrieve data from this keystore later. Once we've created a keystore entry, we can display it with the command:

 C:\> keytool -list -alias Pat -storepass boofa


To see more detail, add the -v option (for "verbose"):

 C:\> keytool -list -alias Pat -v -storepass boofa


We can also list the entire contents of the database:

 C:\> keytool -list -storepass boofa


Certificates

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 CA and receive a certificate in return. keytool can generate such a request in a standard format called a Certificate Signing Request (CSR). To generate a signing request for the entry we just created, you would do this:

 C:\> keytool -csr -alias Pat -file Pat.csr -keypass secure -storepass boofa


You need to specify the alias for the entry you want, a filename where the CSR will be written, and the password for the private key. The output file will contain the public key, along with the name and organizational information you provided. Once you've generated the CSR file, you can send it off to your favorite Certificate Authority. After they've performed some identity checks on you, and once you pay them, they will send a certificate back to you. Suppose they send it back in a file called Pat.x509. You can then use keytool to import this certificate as follows:

 C:\> keytool -import -alias Pat -file Pat.x509 -keypass secure -storepass boofa


However, to demonstrate the features of keytool, we can serve as our own authority (as we did in the example) and use our own self-signed certificate. It turns out that keytool already did this for us when we created keys! A self-signed certificate already exists in the keystore; all we have to do is export it as follows:

 C:\> keytool -export -alias Pat -file Pat.cer -storepass boofa


The jarsigner Utility

If we have a signer keystore entry, initialized with its private and public keys, we are ready to sign JAR files. This is accomplished using another command-line utility, jarsigner. All we need to do is specify which keystore entry should do the signing, which JAR needs to be signed, and the keystore password:

 C:\> jarsigner -storepass boofa testwrite.jar Pat


If we list the archive, we see that jarsigner has added two files to the META-INF directory: PAT.SF and PAT.DSA. PAT.SF is the signature file; it's like the manifest file for this particular signature. The signature file lists the objects that were signed and the signature algorithms. PAT.DSA is the actual binary signature. That's it! We've now created a JAR file that could only have come from us. Signed JAR files with the Java Plug-in level the playing field for Java apps by allowing them to do anything that a regular Java app could do, safely.

Comments