JavaEnterprise JavaSigning Code with JDK 1.2

Signing Code with JDK 1.2

Developer.com content and product recommendations are editorially independent. We may make money when you click on links to our partners. Learn More.


Last week we examined Netscape’s Object Signing model for granting applets higher privileges. This week, we’ll examine the code signing system in Sun’s JDK 1.2, which is a complete replacement for the system in JDK 1.1.

There are two tools responsible for code signing in JDK 1.2. One tool, keytool, manages and generates keys and certificates in a database. The other, jarsigner, is responsible for signing and verifying JAR files. Both tools require access to a “keystore” that contains certificate and key information to operate. New to JDK 1.2 is the notion of policy, which provides fine-grained control as to which resources applets are granted access to outside of the sandbox.

Getting started with keytool

The first step in working with JDK 1.2 is getting the latest beta version from Sun. Members of the Java Developers Connection (JDC) can download Early Access releases of JDK 1.2 software. Membership in the JDC is free with registration. Once registered, point your browser to Java Development Kit 1.2.

The name of the default keystore file is “.keystore”, and is located in the directory named by the “user.home” Java System Property. The value of “user.home” on Solaris is “$HOME/”; on Win32 it is either the value of USERPROFILE, or the combination of HOMEDRIVE and HOMEPATH, or the directory in which JDK 1.2beta4 is installed. It is possible to have multiple keystores. Changing the keystore that the current keytool command will operate on is done through the -keystore <path to keystore> option.

Generating a public and private key pair and self-signed certificate can be performed from the command line in one shot without the need to create any directives files. All keys and certificates stored in the keystore are accessible through an alias. An alias is a name associated with a certificate entry that keytool uses to uniquely identify each certificate under its control. To generate a certificate keyed by the alias keyname, run the command:

keytool -alias keyname -genkey

keytool will begin prompting for information. The first prompt is for a keystore password. This password will be needed for all further keytool and jarsigner operations on this keystore. It must be at least six characters long and is unfortunately echoed to the screen as it is typed. This means that the keystore password can be leaked to casual observers whenever keytool or jarsigner is used.

Once the password has been entered, keytool prompts for some personal information, such as name, company name, city, state, and country. All this information is stored in the generated self-signed certificate which is saved in the default keystore location. All the personal information is displayed for verification before keytool generates the keys and certificate. After the certificate and keys are generated, keytool prompts for another password. Each certificate has its own password, separate from the keystore password. Entering nothing does not give the key an empty password. It gives the certificate the same password as the keystore. jarsigner will not prompt for the passwords of certificates that have the same password as the keystore, so it may appear that a certificate has no password. However, if the password of the keystore changes, the passwords of the certificates do not change, so jarsigner will start prompting for not only the password of the keystore, but for the certificate as well. The command to change the password of a keystore is:

keytool -storepasswd

keytool will prompt for the old password, and the new password twice, all in cleartext. This command does not affect the passwords of certificates in the keystore, including those that happen to have the same password as the keystore.

A small weakness of the keytool certificate generation system is that a user can accept all the default values for the personal information prompted for before certificate generation. The default value for all the questions is “Unknown”. So keytool will generate a valid certificate which can be used to sign JAR files, but is filled with bogus information. No data validation is performed by keytool, so it is possible to, say, create a certificate for Thomas Jefferson. However, there are a set of options to keytool that allow certificates to be exported in a format suitable for submission to a Certificate Authority. The Certificate Authority authenticates that the keytool certificate is for who it says its for and returns a certificate that can be imported into keytool with another set of command line options. We were unable to find a Certificate Authority to do this for us, so we can’t say if it works or not. What we can say is that if a CA hasn’t vouched for a keytool generated certificate, the JDK still accepts it as being valid.

Certificates generated by the system will be valid for just under one year by default. To change the length of validity for a certificate to n days, add the flag

-validity
n to the keytool

-genkey
command.

To view the fingerprints of certificates in the keystore, use the command:

keytool -list

To view the personal information about the issuer and owner of the certificate, run:

keytool -list -v

Signing a JAR

Once a private key has been generated, jarsigner can be used to mark a JAR file with the public key of the signer. The command to sign a JAR file called

SignMe.jar
with the keyname private key generated above is:

  jarsigner SignMe.jar keyname

jarsigner will prompt for the keystore password, and the private key password if different than the keystore password before signing the JAR file. To monitor the progress of the signing process, run:

  jarsigner -verbose SignMe.jar keyname

jarsigner can also be used to verify that a JAR has or has not been signed and by whom. For a simple signed/not signed answer for a JAR file

Unknown.jar
, run:

  jarsigner -verify Unknown.jar

To get more information from the verification process, such as the signing status of each file in the JAR file, the personal information from the certificates used to sign each file in the JAR, and whether or not the certificate is known in the default keystore, run:

  jarsigner -verify -verbose -certs Unknown.jar

After each signed file in the listing will be the personal information encoded in the certificate for the entity that signed the file. If that certificate is known in the keystore, the alias it is known by will appear in parenthesis after the certificate’s personal information.

Turning over the keys

Until the certificate used to sign the JAR is made public, no one can grant any permissions to the enclosed applet. To export a copy of the keyname certificate from the keystore into a file

mycert
use:

keytool -export -alias keyname -file mycert

As usual, keytool will prompt for the appropriate passwords. When the command finishes, the file

mycert
can be distributed to users wishing to grant additional privileges to applets signed by that certificate.

There is currently limited support for a JAR signed with the JDK tools. Sun provides support through the Java Plug-In. The Java Plug-In is meant to be a replacement VM for browser’s default VMs. (For an article on the Java Plug-In itself, check out The Java Plug-in: using the newest versions of Java with your old browser.)

Plug-In version 1.1.1 does not necessarily support JDK 1.2. Though the Java Plug-In can be configured to use different VMs installed on the local system, the Plug-In hangs the browser when pointed to a 1.2 VM on Solaris. Documentation for the Win32 version of the Plug-In mentions running a program off the Start Menu to configure the Plug-In. The installation script does not create a program group for a Plug-In Control Panel as advertised under Windows NT unless the user performing the installation has permission to create program groups. The latest version of the Plug-In for Win32 ships with JDK 1.2beta4. An Early Access version of Plug-In version 1.2 for Solaris is available to members of the Java Developer’s Connection at Java Plug-in 1.2 Early Access 1.

Any HTML pages that contain 1.2-signed JAR files must be converted using the HTMLConverter application. Converting the HTML ensures that the applet will run in the Plug-In and not in the browser’s default VM. HTMLConverter changes the <APPLET> tag on an HTML page into the <EMBED> and <OBJECT> tags for Communicator and Internet Explorer respectively. Download the HTMLConverter application from Java HTML Converter Download Page.

Running a signed applet

The first step upon encountering a signed applet is to locate the certificate of the entity that signed the JAR file and import it into the local keystore. Assuming that the certificate can be located and placed into a file called

acert
, run:

keytool -import -alias analias -file acert

An entry in the keystore is created keyed by the name

analias
for the certificate stored in

acert
. This is now a trusted entity. What code signed by a trusted entity can do is controlled by the system’s policy.



Creating a simple policy for signed applets

JDK 1.2 introduces the notion of policy. Creating, understanding, and managing security policy for signed mobile code is a difficult and complex problem. Since this discussion is about signing code and not about constructing policy, an extremely simple example of how to construct policy is presented. Creating good policy is beyond the scope of this article. The example policy is strong enough to allow an applet limited file access to the host machine.

Java policy files can be created with a new tool that comes with JDK 1.2 called policytool. This application has a GUI to guide users though the many twists and turns encountered when creating policy files. It’s a very simplistic GUI with no on-line help. In it’s current form as of beta4, it is only useful if one does not know the syntax of a policy file.

Policy files are plain-text files that follow a format outlined at Default Policy Implementation and Policy File Syntax.

Permissions that can be granted in a java policy file are outlined at Permissions in JDK1.2.

To grant an applet permission to write or create any file in the c:tmp directory, assuming the applet comes from http://www.friendly.com/~mybuddy/applets/ and is signed by a certificate known in the default keystore by the alias friend, create a file called .java.policy in the directory known to the VM as user.home which contains:


keystore “.keystore”;
grant signedBy “friend”,
codeBase “http://www.friendly.com/~mybuddy/applets/” {
permission java.io.FilePermission “c:tmp*”, “write”;
};

Note the double backslashes. All Win32 pathnames must use double backslashes to indicate directories. UNIX pathnames use regular singleton forward slashes. codeBase entries follows URL syntax.

Sign only privileged code

Applets that request permission to leave the sandbox are usually built for doing serious business tasks. These applets that require access to the local system are most likely some of the larger applets in terms of code size. It is unlikely that these applets will be built completely by one developer or one software company. Chances are some of the components of an applet will be bits of utility code found on the Internet or purchased from a tool vendor. A smart organization wants to sign only code that it produced. Third party utility code cannot be safely vouched for.

If all the code is signed, then any code can leave the sandbox based on the policy. But if some code in an applet is from a third-party, it should not be signed unless the individual signing the code is willing to vouch that the third-party code won’t try to do anything malicious or introduce a security hole that others can exploit. To say the least, we don’t recommend signing code you don’t completely understand.

JDK 1.2 presents an API for privileged blocks. Privileged blocks are meant to be small sections of code that have a higher privilege than the code that invoked them. JDK 1.2beta4 changed the API for privileged blocks from previous beta releases. Using this API, the only code that needs to be signed is the code that invokes the AccessController class, and the code that performs the privileged action. All other code can remain unsigned, preventing it from leaving the sandbox on its own. Documentation on the new API can be found at New API for Privileged Blocks.

There are two things to consider when writing signed code that will be integrated with unsigned code. First, make the code in the privileged block as small as possible. The less code that is privileged, the less chance that granting it higher privilege will result in nasty and unwanted side-effects. Second, to prevent mix-and-match attacks, all the code for the applet should live in one JAR file, even if the third-party libraries that are used by the applet live in their own JAR.

To sign some portions of a JAR file and leave others unsigned takes a number of steps we’ll cover now. First create a JAR file containing all classes that need to be signed. Classes that need to be signed when using privileged blocks include all classes that contain the call to the

AccessController.doPrivileged()
method, and the class that is passes as the argument to

doPrivileged()
.

jar cvf MyApp.jar Signme1.class Signme2.class

List all the classes that need to be signed in this step. Once the JAR containing classes that need to be signed is created, sign the JAR with jarsigner:

jarsigner MyApp.jar mykey

Now, add the remainder of the classes in the application to

MyApp.jar
. The JDK 1.2 version of jar added the v flag, which allows JAR files to be updated with new files.

jar uvf MyApp.jar Other1.class Others.class

All the remaining classes in the application should be added to the JAR in this step. If parts of the application are already in a JAR or ZIP file, they will need to be un-archived before being JARed into the new partially signed JAR file. To verify that all went correctly, use jarsigner to verify the contents:

jarsigner -verify -verbose MyApp.jar

Only the classes that were added before jarsigner was invoked to create the signature will be marked as signed. All the other classes will be listed, but no certificate or signature will be associated with their listing. If jarsigner fails to verify the entire JAR, or classes that are supposed to be signed appear not to be, use the jar command to list the contents of the JAR.

jar tvf MyApp.jar

The first entry in the JAR must be META-INF/MANIFEST.MF. If the manifest file is missing or not in the first position in the file, the JAR will not verify properly. Following the MANIFEST.MF file should be a .SF and .DSA file. If either of those files is missing, then the signature is missing from the JAR. Remove the JAR file and start over. If the commands listed above still move the META-INF/MANIFEST.MF file out of the first position in the file, it may not be possible to create a JAR containing signed and unsigned code. The JAR command with JDK 1.2beta4 did not move the META-INF/MANIFEST.MF file around in the JARs we created using the process described above.

Comparison to Netscape Object Signing

Last week, we discussed Netscape Object Signing. Here’s a brief summary of major differences from the JDK 1.2 keytool/jarsigner system.

  • Netscape requires getting a certificate from a Certificate Authority. Keytool generates self-signed certificates.
  • Netscape uses its own security classes for granting and managing privileges. JDK 1.2 uses only classes in the core API for its security work.
  • Netscape manages certificates for the user. JKD1.2 requires the user to locate and install certificates by hand using keytool.
  • Netscape prompts the user for approval when an applet tries to request privileges. JDK 1.2 consults the policy file when privileges are request, without informing the user.
  • The level of granularity of control differs. When Netscape grants a “UniversalFileRead” privilege, the applet can read anything on the client. JDK 1.2 can restrict file reads to only certain directories on the client.

Summing up

It’s difficult to say if Sun’s approach will enter widespread use. It may require one of the major browser vendors to adopt and support the use of keytool and jarsigner. Downloading and installing the Java Plug-In, finding, downloading and installing certificates, and managing policy are three hoops that Netscape users don’t have to jump through. For developers, there’s a trade off. Developing for Netscape requires paying for a certificate from a CA and tying their code to Netscape libraries. JDK 1.2 developers have to convert all their HTML pages with the HTMLConverter.

To see an example of an applet signed using jarsigner for JDK 1.2, take a look at To View the Signed Applet. Of course, to view the applet, you’ll need the Java Plug-in.

Next week, we’ll discuss Authenticode, Microsoft’s Java code signing scheme.

Resources

Tom O’Connor is a software engineer in the research division at Reliable Software Technologies. His interests are computer security and object-oriented software development.

John Viega is a research associate at Reliable Software Technologies, in Sterling, Va. He holds an M.S. in Computer Science from the University of Virginia. He developed and maintains Mailman, the Gnu mailing list manager. His research interests include software assurance, programming languages, and object-oriented systems.

Portions of this article will appear as an appendix in the forthcoming book Securing Java: Getting Down to Business with Mobile Code (John Wiley & Sons, 1998), the second edition of McGraw and Felten’s book, Java Security: Hostile Applets, Holes, & Antidotes.


Get the Free Newsletter!

Subscribe to Developer Insider for top news, trends & analysis

Latest Posts

Related Stories