сряда, 1 август 2007 г.

JAR files revealed

What is a JAR file?

The JAR file format is based on the popular ZIP file format, and is used for aggregating many files into one. Unlike ZIP files, JAR files are used not only for archiving and distribution, but also for deployment and encapsulation of libraries, components, and plug-ins, and are consumed directly by tools such as compilers and JVMs. Special files contained in the JAR, such as manifests and deployment descriptors, instruct tools how a particular JAR is to be treated.

A JAR file might be used:
  • For distributing and using class libraries

  • As building blocks for applications and extensions

  • As deployment units for components, applets, or plug-ins

  • For packaging auxiliary resources associated with components
The JAR file format provides many benefits and features, many of which are not provided with a traditional archive format such as ZIP or TAR. These include:
  • Security. You can digitally sign the contents of a JAR file. Tools that recognize your signature can then optionally grant your software security privileges it wouldn't otherwise have, and detect if the code has been tampered with.

  • Decreased download time. If an applet is bundled in a JAR file, the applet's class files and associated resources can be downloaded by a browser in a single HTTP transaction, instead of opening a new connection for each file.

  • Compression. The JAR format allows you to compress your files for efficient storage.

  • Transparent platform extension. The Java Extensions Framework provides a means by which you can add functionality to the Java core platform, which uses the JAR file for packaging of extensions. (Java 3D and JavaMail are examples of extensions developed by Sun.)

  • Package sealing. Packages stored in JAR files can be optionally sealed to enforce version consistency and security. Sealing a package means that all classes defined in that package must be found in the same JAR file.

  • Package versioning. A JAR file can hold data about the files it contains, such as vendor and version information.

  • Portability. The mechanism for handling JAR files is a standard part of the Java platform's core API.

Compressed and uncompressed JARs

The jar tool (see The jar tool for details) compresses files by default. Uncompressed JAR files can generally be loaded more quickly than compressed JAR files, because the need to decompress the files during loading is eliminated, but download time over a network may be longer for uncompressed files.

The META-INF directory

Most JAR files contain a META-INF directory, which is used to store package and extension configuration data, such as security and versioning information. The following files or directories in the META-INF directory are recognized and interpreted by the Java 2 platform for configuring applications, extensions, and class loaders:

  • MANIFEST.MF. The manifest file defines the extension- and package-related data.

  • INDEX.LIST. This file is generated by the new -i option of the jar tool and contains location information for packages defined in an application or extension. It is part of the JarIndex implementation and used by class loaders to speed up the class loading process.

  • xxx.SF. This is the signature file for the JAR file. The placeholder xxx identifies the signer.

  • xxx.DSA. The signature block file associated with the signature file stores the public signature used to sign the JAR file.

The jar tool

To perform basic tasks with JAR files, you use the Java Archive Tool (jar tool) provided as part of the Java Development Kit. You invoke the jar tool with the jar command. Table 1 shows some common applications:

Table 1. Common usages of the jar tool

Creating a JAR file from individual filesjar cf jar-file input-file...
Creating a JAR file from a directoryjar cf jar-file dir-name
Creating an uncompressed JAR filejar cf0 jar-file dir-name
Updating a JAR filejar uf jar-file input-file...
Viewing the contents of a JAR filejar tf jar-file
Extracting the contents of a JAR filejar xf jar-file
Extracting specific files from a JAR filejar xf jar-file archived-file...
Running an application packaged as an executable JAR filejava -jar app.jar

Executable JARs

An executable jar file is a self-contained Java application stored in a specially configured JAR file, which can be executed directly by the JVM without having to first extract the files or set up a class path. To run an application stored in a non-executable JAR, you have to add it to your class path and invoke the application's main class by name. But by using executable JAR files, we can run an application without extracting it or needing to know the main entry point. Executable JARs facilitate easy distribution and execution of Java applications.

Creating executable JARs

Creating an executable JAR is easy. You begin by placing all your application code in a single directory. Let's say the main class in your application is com.mycompany.myapp.Sample. You want to create a JAR file that contains the application code and identifies the main class. To do this, create a file called manifest somewhere (not in your application directory), and add the following line to it:

Main-Class: com.mycompany.myapp.Sample
Then, create the JAR file like this:

jar cmf manifest ExecutableJar.jar application-dir
That's all there is to it -- now the JAR file ExecutableJar.jar can be executed using java -jar.

An executable JAR must reference all the other dependent JARs it requires through the Class-Path header of the manifest file. The environment variable CLASSPATH and any class path specified on the command line is ignored by the JVM if the -jar option is used.

Launching executable JARs

Now that we've packaged our application into an executable JAR called ExecutableJar.jar, we can launch the application directly from the file using the following command:

java -jar ExecutableJar.jar

Package sealing

Sealing a package within a JAR file means that all classes defined in that package must be found in the same JAR file. This allows the package author to enforce version consistency among packaged classes. Sealing also provides a security measure to detect code tampering.

To seal a package, add a Name header for the package, followed by a Sealed header with value "true" to the JAR manifest file. Just as with executable JARs, you can seal a JAR by specifying a manifest file with the appropriate header elements when the JAR is created, as shown here:

Name: com/samplePackage/
Sealed: true
The Name header identifies the package's relative pathname. It ends with a "/" to distinguish it from a filename. Any headers following a Name header, without any intervening blank lines, apply to the file or package specified in the Name header. In the example above, because the Sealed header occurs after the Name header without intervening blank lines, the Sealed header will be interpreted as applying only to the package com/samplePackage.

If you try to load a class in a sealed package from another source other than the JAR file in which the sealed package lives, the JVM will throw a SecurityException.

Packaging for extensions

Extensions add functionality to the Java platform, and an extensions mechanism is built into the JAR file format. The Extensions mechanism allows JAR files to specify other required JAR files via the Class-Path headers in the manifest file.

Let's say that extension1.jar and extension2.jar are two JAR files in the same directory, with the manifest of extension1.jar containing the following header:

Class-Path: extension2.jar
This header indicates that the classes in extension2.jar serve as extension classes for purposes of the classes in extension1.jar. The classes in extension1.jar can invoke classes in extension2.jar without extension2.jar having to be part of the class path.

The JVM effectively automatically adds JARs referenced in a Class-Path header to the class path when loading a JAR that uses the extension mechanism. However, the extension JAR path is interpreted as a relative path, so in general the extension JAR must be stored in the same directory as the JAR referencing it.

For example, assume the class ExtensionClient, which references class ExtensionDemo, is bundled in a JAR file called ExtensionClient.jar, and that the class
ExtensionDemo is bundled in ExtensionDemo.jar. In order for ExtensionDemo.jar to be treated as an extension, ExtensionDemo.jar must be listed in the Class-Path header in ExtensionClient.jar's manifest, as follows:

Manifest-Version: 1.0
Class-Path: ExtensionDemo.jar
The value of the Class-Path header in this manifest is ExtensionDemo.jar with no path specified, indicating that ExtensionDemo.jar is located in the same directory as the ExtensionClient JAR file.

Security in JAR files

A JAR file can be signed by using the jarsigner tool or directly through the java.security API. A signed JAR file is exactly the same as the original JAR file, except that its manifest is updated, and two additional files are added to the META-INF directory, a signature file and a signature block file.

A JAR file is signed using a certificate stored in the Keystore database. Certificates stored in the keystore are protected with a password, which must be provided to the jarsigner tool to sign a JAR file.

Figure 1. Keystore database

Keystore Database

Each signer of a JAR is represented by a signature file with the extension .SF within the META-INF directory of the JAR file. The format of the file is similar to the manifest file -- a set of RFC-822 headers. As shown below, it consists of a main section, which includes information supplied by the signer but not specific to any particular JAR file entry, followed by a list of individual entries which also must be present in the manifest file. To validate a file from a signed JAR, a digest value in the signature file is compared against a digest calculated against the corresponding entry in the JAR file.

Listing 1. Manifest and signature files in signed JARs

Contents of signature file META-INF/MANIFEST.MF

Manifest-Version: 1.0
Created-By: 1.3.0 (Sun Microsystems Inc.)

Name: Sample.java
SHA1-Digest: 3+DdYW8INICtyG8ZarHlFxX0W6g=

Name: Sample.class

Contents of signature file META-INF/JAMES.SF

Signature-Version: 1.0
SHA1-Digest-Manifest: HBstZOJBuuTJ6QMIdB90T8sjaOM=
Created-By: 1.3.0 (Sun Microsystems Inc.)

Name: Sample.java
SHA1-Digest: qipMDrkurQcKwnyIlI3Jtrnia8Q=

Name: Sample.class
SHA1-Digest: pT2DYby8QXPcCzv2NwpLxd8p4G4=
Digital signatures

A digital signature is a signed version of the .SF signature file. Digital signature files are binary files and have the same filename as the .SF file but a different extension. The extension varies depending on the type of digital signature -- RSA, DSA, or PGP -- and on the type of certificate used to sign the JAR.


To sign a JAR file, you must first have a private key. Private keys and their associated public-key certificates are stored in password-protected databases called keystores. The JDK contains tools for creating and modifying keystores. Each key in the keystore can be identified by an alias, which is typically the name of the signer who owns the key.

All keystore entries (key and trusted certificate entries) are accessed with unique aliases. An alias is specified when you add an entity to the keystore using the keytool -genkey command to generate a key pair (public and private key). Subsequent keytool commands must use this same alias to refer to the entity.

For example, to generate a new public/private key pair with the alias "james" and wrap the public key into a self-signed certificate, you would use with the following command:

keytool -genkey -alias james -keypass jamespass
-validity 80 -keystore jamesKeyStore
-storepass jamesKeyStorePass
This command sequence specifies an initial password of "jamespass" required by subsequent commands to access the private key associated with the alias "james" in the keystore "jamesKeyStore." If the keystore "jamesKeyStore" does not exist, keytool will automatically create it.

The jarsigner tool

The jarsigner tool uses keystore to generate or verify digital signatures for JAR files.

Assuming you've created the keystore "jamesKeyStore" as in the example above, and it contains a key with alias "james," you can sign a JAR file with the following command:

jarsigner -keystore jamesKeyStore -storepass jamesKeyStorePass
-keypass jamespass -signedjar SSample.jar Sample.jar james
This command fetches the key whose alias is "james" and whose password is "jamespass" from the keystore named "jamesKeyStore" with the password "jamesKeyStorePass," and signs the Sample.jar file, creating a signed JAR, SSample.jar.

The jarsigner tool can also verify a signed JAR file; this operation is considerably simpler than signing the JAR file. Just execute the following command:

jarsigner -verify SSample.jar
If the signed JAR file has not been tampered with, the jarsigner tool will tell you the JAR has been verified; otherwise, it will throw a SecurityException indicating which files could not be verified.

JARs can also be signed programmatically using the java.util.jar and java.security APIs. Alternatively, you can use tools such as Netscape Object Signing Tool.

You can also sign JARs programmatically using the java.util.jar and java.security APIs. (See Resources for details). Alternatively, you can use tools such as the Netscape Object Signing Tool.

JAR indexing

If an application or applet is bundled into multiple JAR files, the class loader uses a simple linear search algorithm to search each element of the class path, which may entail the class loader downloading and opening many JAR files until the class or resource is found. If the class loader tries to find a nonexistent resource, all the JAR files within the application or applet will have to be downloaded. For large network applications and applets this could result in slow start up, sluggish response, and wasted network bandwidth.

Since JDK 1.3, the JAR file format has supported indexing to optimize the process of searching for classes in network applications, especially applets. The JarIndex mechanism collects the contents of all the JAR files defined in an applet or application and stores the information in an index file in the first JAR file. After the first JAR file is downloaded, the applet class loader will use the collected content information for efficient downloading of JAR files. This directory information is stored in a simple text file named INDEX.LIST in the META-INF directory of the root JAR file.

Creating a JarIndex

You can create a JarIndex by specifying the -i option to the jar command. Suppose we have a directory structure as depicted in the following diagram:

Figure 2. JarIndex

JarIndex Demo

You would use the following command to create an index file for JarIndex_Main.jar, JarIndex_test.jar, and JarIndex_test1.jar:

jar -i JarIndex_Main.jar JarIndex_test.jar SampleDir/JarIndex_test1.jar
The INDEX.LIST file has a simple format, containing the names of the packages or classes contained in each JAR file indexed, as shown in Listing 2:

Listing 2. Example JarIndex INDEX.LIST file

JarIndex-Version: 1.0





The JAR format is much more than an archive format; it has many features for improving the efficiency, security, and organization of Java applications. Because these features are built into the core platform, including the compiler and classloader, developers can leverage the power of the JAR file format to simplify and improve their development and deployment processes.

Няма коментари: