Bạn đang xem bản rút gọn của tài liệu. Xem và tải ngay bản đầy đủ của tài liệu tại đây (5.59 MB, 702 trang )
First of all, let’s consider the command below, which creates an archive for
the PointlessButton applet (see Figure 17 on page 37):
jar cvf pbutton.jar PointlessButton.class jamjar\examples\Button.class
Figure 181 shows the format of the pbutton.jar file that the command above
creates:
pbutton.jar
META-INF
MANIFEST.MF
PointlessButton.class
JAR Payload
jamjar
examples
Button.class
Figure 181. The pbutton Archive
The files that make up the payload of the JAR are packed into a copy of the
original directory structure. The MANIFEST.MF file, also known as the
manifest file, contains details of the payload of the JAR. The manifest file is
created under a directory META-INF. This is what the manifest looks like in
this case:
Manifest-Version: 1.0
Created-By: 1.2 (Sun Microsystems Inc.)
Figure 182. Manifest File Created by the jar cvf Command
JAR files can be digitally signed. A digital signature on a JAR file guarantees
the sender’s identity to the receiver, but it also vouches for the integrity of the
JAR file itself – that is, the JAR file was not altered after signing. JAR signing
allows you to generate digital signatures for any of the files in the archive. In
fact, files can be signed by more than one signer. So, for example, an applet
386
Java 2 Network Security
could be signed by the developer who created it and then also signed by the
IT department of the company that uses it. When the user loads the applet,
he or she not only knows that the applet comes from a trustworthy source, but
also knows that it has been approved for corporate use.
As we can see in Figure 181 on page 386, the manifest file is created by the
jar command in the META-INF directory. However, when you sign a file in a
JAR archive with the Java 2 SDK jarsigner tool, two new files are added to
the META-INF directory; we will call them the signature file and signature
block file.
Let’s discuss in detail these files one by one.
12.1.1 Manifest File
A manifest file, MANIFEST.MF, is created by default in the META-INF
directory whenever a new JAR file is created. According to the specifications
(see http://java.sun.com/products/jdk/1.2/docs/guide/jar/manifest.html), the
manifest file must include as a minimum the following line:
Manifest Version: 1.0
Figure 182 on page 386 shows the sample manifest file created by using the
jar command with the option cvf. However, you have the possibility to include
your own manifest information from a specified text file.
A customized manifest file can be manually edited, but this is a risky
operation, because you must be sure that you respect the syntax. Another
option you have is to let the jar tool create a default manifest file while
compressing the files. Then you should extract the JAR file, modify the
manifest, customizing it according to your needs, and then compress the JAR
file again including the manifest file you modified. This operation also requires
editing the manifest file, but at least you can use part of the manifest
information produced by default by the jar tool. A customized manifest can be
packed with a JAR file by using the M or m option provided with the jar utility:
• The M option does not create the manifest file at all. So the command:
jar cvfM jarFile file1 file2 ... fileN
compresses all the files in a single JAR file, without adding any manifest
information file. This can be useful if you wish to include your own manifest
file. In this case, in fact, you can use the M option and your predefined
manifest file will appear as one of the regular files that must be
compressed, as shown:
jar cvfM jarFile file1 file2 ... fileN META-INF\MANIFEST.MF
Java Gets Out of Its Box
387
where META-INF\MANIFEST.MF is the manifest file you previously created.
Remember that there can be only one manifest file in the archive. It must
be called MANIFEST.MF and it is required to be in the directory META-INF,
otherwise it will not be recognized as the manifest file during signing,
updating, verifying, etc. and will be treated as a normal file in the JAR. The
names META-INF and MANIFEST.MF should be generated as uppercase,
but they will be recognized in any case. Also, if you manually edited the
manifest file, be sure you respect the syntax.
• The m option is probably the most useful one. It can be applied as follows:
jar cvfm jarFile manifestInput file1 file2 ... fileN
or:
jar cvmf manifestInput jarFile file1 file2 ... fileN
Using the m option, a new manifest file is created taking the information
contained in an existing manifest input text file, specified on the command
prompt.
Note the order of the files to be specified on the command prompt. If the
option f is specified before m, then jarFile must come before
manifestInput; otherwise the order will have to be manifestInput
jarFile. The files to be compressed, file1 file2 ... fileN , are
always specified last.
Another important thing to notice is that, with the m option, the file you pass
on the command line as the manifest file does not need to be called
MANIFEST.MF and does not need to reside in the directory META-INF.
The jar utility will create a file called MANIFEST.MF and will place it in a
directory called META-INF, as you can see by extracting the resulting JAR
file.
There are several reasons why you might want to create a JAR file with a
specific manifest. These reasons depend on what role you want your JAR file
to play. If you're interested only in the ZIP-like features of JAR files, such as
compression and archiving, you do not have to worry about the manifest file.
The manifest doesn't really play any role in those situations. However, for
other purposes, you will need to change the default manifest file. For
example, you can add special-purpose name-value attribute headers to the
manifest file that are not contained in the default manifest. Examples of such
headers would be those for vendor information, package sealing, downloaded
extensions, and headers to make JAR-bundled applications executable.
For applications bundled in a JAR file, you have to add the following line to
your manifest file:
388
Java 2 Network Security
Main-Class: ClassName
An example of this can be found in 1.4.2.4, “Packing the Application Class in
a JAR File” on page 30.
For downloaded extensions, which are JAR files referenced by other JAR files
(see 3.4.2, “Extensions Framework” on page 86), you need to add the
following line to your manifest file:
Class-Path: extensionJarName
A package within a JAR file can be optionally sealed, which means that all
classes defined in that package must be archived in the same JAR file.
Package sealing is a new feature introduced for the first time with Java 2
SDK, Standard Edition, V1.2. You might want to seal a package, for example,
to ensure version consistency among the classes in your software or as a
security measure. To seal a package, you need to add a Name header for the
package, followed by a Sealed header, similar to this:
Name: myCompany/myPackage/
Sealed: true
The Name header’s value is the package's relative path name. Note that it
ends with a forward slash (/) to distinguish it from a file name. Any headers
following a Name header, without any intervening blank lines, apply to the file
or package specified in the Name header. In the above example, because the
Sealed header occurs after the Name header, with no blank lines between,
the Sealed header will be interpreted as applying (only) to the package
myCompany/myPackage/.
Another new feature introduced only with Java 2 SDK, Standard Edition, V1.2
is package versioning. The package versioning specification defines several
manifest headers to hold versioning information. One set of such headers can
be assigned to each package. The versioning headers should appear directly
beneath the Name header for the package. This example shows all the
versioning headers:
Name: java/util/
Specification-Title: "Java Utility Classes"
Specification-Version: "1.2"
Specification-Vendor: "Sun Microsystems, Inc.".
Implementation-Title: "java.util"
Implementation-Version: "build57"
Implementation-Vendor: "Sun Microsystems, Inc."
Java Gets Out of Its Box
389
Header information, such as vendor information, package sealing,
downloaded extensions, and headers to make JAR-bundled applications
executable, is not inserted in the default manifest file created by the jar utility.
Therefore you must provide those headers in a manifest input file and then
use the m option, or in alternative you have to edit a manifest file with the
information you need and include it in the JAR file using the M option, to
prevent jar from creating the default manifest.
Notice that the default manifest has the Created-By and Manifest-Version
information (see Figure 182 on page 386). If you use the m option and either
or both of these two pieces of information are also present in the manifest
input file you pass on the command line, the same values will be present in
the new manifest file, although the order of the entries might be rearranged.
For example if your manifest input file is:
Manifest-Version: 1.0
Created-By: DEEPAK GUPTA
Main-Class: GetProps
then the manifest file created is:
Manifest-Version: 1.0
Main-Class: GetProps
Created-By: DEEPAK GUPTA
As you can see, the jar utility has rearranged the order of the entries in the
manifest file.
On the other hand, if your original manifest file contained only the line:
Main-Class: GetProps
then the manifest file that is created is:
Manifest-Version: 1.0
Main-Class: GetProps
Created-By: 1.2 (Sun Microsystems Inc.)
So, in this case, the jar utility has provided the missing manifest information.
Also, note that the manifest file entries must have the syntax:
Name: value
390
Java 2 Network Security
When the jar utility encounters incorrect syntax, the following error is
returned in the Command Prompt window:
java.io.IOException: invalid header field
at java.util.jar.Attributes.read(Compiled Code)
at java.util.jar.Manifest.read(Compiled Code)
at java.util.jar.Manifest.
at sun.tools.jar.Main.run(Main.java:87)
at sun.tools.jar.Main.main(Main.java:760)
As we said also in Step 1 on page 274, the last line of the manifest input file
must be empty. That is, there should be a new line character at the end of the
file. If this is missing, the jar utility simply ignores the manifest file. Therefore,
when you are manually editing the manifest file, make sure to press the Enter
key after the last line.
When a manifest file is signed, the digest values of the files in the JAR are
added to the manifest file. Note that this behavior is different from what
happened with the JDK 1.1 jar utility, which always computed the digests,
regardless of whether or not the JAR file was signed. In other words, in JDK
1.1, the digests were calculated and added to the manifest file when the JAR
file was created. In Java 2 SDK, Standard Edition, V1.2, this operation is
done only when the JAR file is signed for the first time. This is to speed up the
creation of unsigned JAR files, for which you do not need any digests.
The following lines are present in the manifest of a signed JAR file:
Name: dirpath/whatever.class
Algorithm-Digest: base-64_representation_of_digest
So after a JAR file is signed, the manifest should look like the following:
Manifest-Version: 1.0
Created-By: 1.2 (Sun Microsystems Inc.)
Name: PointlessButton.class
SHA1-Digest: Sj15dptWhrZhiIFRNU27WRY1brc=
Name: jamjar/examples/Button.class
SHA1-Digest: Fo6pYkn6ZR17eessxEiN7fK5xpE=
The digest values recorded in the manifest are calculated from the contents
of the payload files they refer to. They are used to validate the payload files
when they are verified.
Java Gets Out of Its Box
391
Notice that by default only the SHA-1 digest is present.
12.1.2 Signature File
A signature file is automatically generated and placed in the META-INF
directory each time a JAR file is signed. This file looks very similar to the
manifest file shown above, except that the digests in it are calculated from the
manifest file entries, not from the actual contents of the payload files.
The name of this file is signerID.SF, where signerID is an arbitrary name for
the creator of the signature. If the JAR has been signed by more than one
signer, each signer will generate a separate SF file. The signature file looks
like the following:
Signature-Version: 1.0
SHA1-Digest-Manifest: 3jdG5UfTfZHcBQxGCBWSnCRb0p4=
Created-By: 1.2 (Sun Microsystems Inc.)
Name: jamjar/examples/Button.class
SHA1-Digest: WuhnnW3v9MiVHl0zlT8qnwFDY0o=
Name: PointlessButton.class
SHA1-Digest: L1S9Bcrbn4ZGAOflam1Cwn9qDFw=
The SHA1-Digest-Manifest header gives the digest of the complete manifest
file. The SHA1-Digest header for the different file entries in the SF file give
the digests of the entries of the respective files in the manifest file. By default,
the digest is calculated using the SHA-1 algorithm.
12.1.3 Signature Block File
In addition to the signature file, a signature block file is automatically placed
in the META-INF directory each time a JAR file is signed. Unlike the manifest
file or the signature file, which are ASCII files, signature block files are binary,
so they are not human-readable.
The signature block file is in PKCS#7 format1. It contains two elements
essential for verification:
1. The digital signature for the JAR file, generated with the signer’s private
key
2. The certificate containing the signer’s public key, to be used by anyone
wanting to verify the signed JAR file
1 Public Key Cryptography Standards (PKCS) is a set of rules for encoding various cryptographic structures. PKCS#7
defines a general-purpose signature format, including the signed digest, the certificate of the signer and the certification
authority (CA) certificates that support it.
392
Java 2 Network Security
Signature block file names typically will have a .DSA extension indicating that
they were created by the default Digital Signature Algorithm (DSA). Other file
name extensions are possible if keys associated with some other standard
algorithm are used for signing. For example, .RSA is the extension if the
signature is obtained from an algorithm that uses RSA encryption, and .PGP
is the extension with a Pretty Good Privacy (PGP) signature.
12.2 Signed Code Scenario in JDK 1.1 and Sun HotJava
In this section we show how to use the commands to create three key
databases:
1. A certificate authority database
2. A database for a Web server
3. A database for a Web client
We then use these keys to sign a JAR file containing an applet that attempts
to read a file on the browser system.
12.2.1 Creating the CA Key Database
The certificate authority is a principal in its own key database, with a
self-signed certificate. We create it as follows:
1. The first thing to do is to create a new key database. The key database is
created implicitly when you add the first principal to it:
D:\work\sun_signed_jar>javakey -cs "JamJar CA" true
Created identity [Signer]JamJar CA[identitydb.obj][trusted]
D:\work\sun_signed_jar>
This creates the key database identitydb.obj in your home directory.
2. Next, generate a key pair for the CA principal. We choose to use a
1024-bit key:
D:\work\sun_signed_jar>javakey -gk "JamJar CA" DSA 1024
Generated DSA keys for JamJar CA (strength: 1024).
D:\work\sun_signed_jar>
This can take a while to do. We ran it on a 75 MHz 486 machine and the
command ran for 2 minutes and 40 seconds (the time is related to the key
size).
Java Gets Out of Its Box
393
3. We use the list option of javakey to check the results so far:
D:\work\sun_signed_jar>javakey -ld
Scope: sun.security.IdentityDatabase, source file: C:\users\default\identitydb.obj
[Signer]JamJar CA[identitydb.obj][trusted]
public and private keys initialized
certificates:
No further information available.
D:\work\sun_signed_jar>
4. The key pair allows the CA to sign certificates, but we also need to
generate a certificate for the CA itself, so that others can accept the CA’s
signatures. The first thing to do is to create a certificate information file,
containing the distinguished name information for the CA and the
certificate issuer. In this case, the certificate is self-signed, so the issuer
and the subject are the same:
issuer.name=JamJar CA
issuer.cert=1
subject.name=JamJar CA
subject.real.name=Project JamJar Certificate Authority
subject.org.unit=ISL
subject.org=IBM
subject.country=UK
start.date=12 Sep 1997
end.date=12 Sep 1998
serial.number=1
out.file=cert.jamjar
We save this file as certinfo.jamjar.
5. Finally we can generate the CA’s certificate:
D:\work\sun_signed_jar>javakey -gc certinfo.jamjar
Generated certificate from directive file certinfo.jamjar.
D:\work\sun_signed_jar>javakey -ld
Scope: sun.security.IdentityDatabase, source file: C:\users\default\identitydb.o
bj
[Signer]JamJar CA[identitydb.obj][trusted]
public and private keys initialized
certificates:
certificate 1 for : CN=Project JamJar Certificate Authority, OU=ISL,O=IBM, C=UK
from : CN=Project JamJar Certificate Authority, OU=ISL,O=IBM, C=UK
No further information available.
D:\work\sun_signed_jar>
394
Java 2 Network Security
12.2.2 Creating the Server Key Database
Now we want to create a key database for our server:
1. If we use javakey to create the principal for the server, it will add it to the
CA database. So first we must choose to use a different key database, by
setting the identity.database directive in the main security properties file,
${java.home}\lib\security\java.security, where ${java.home} in this case is
the directory where JDK 1.1 was installed. We add the following line:
identity.database=D:/work/sun_signed_jar/serverdb.obj
2. The server has to know about the CA that signed its own certificate, so
first we add the CA principal to the key database and import the CA
certificate:
D:\work\sun_signed_jar>javakey -cs "JamJar CA" true
Created identity [Signer]JamJar CA[D:/work/sun_signed_jar/serverdb.obj][trusted]
D:\work\sun_signed_jar>javakey -ic "JamJar CA" cert.jamjar
Imported certificate from cert.jamjar for JamJar CA.
D:\work\sun_signed_jar>javakey -ld
Scope: sun.security.IdentityDatabase, source file: D:/work/sun_signed_jar/serverdb.obj
Signer]JamJar CA[D:/work/sun_signed_jar/serverdb.obj][trusted]
no keys
certificates:
certificate 1 for : CN=Project JamJar Certificate Authority, OU=ISL,O=IBM, C=UK
from : CN=Project JamJar Certificate Authority, OU=ISL,O=IBM, C=UK
D:\work\sun_signed_jar>
Notice that in this case the list command shows a key database with no
keys in it, just a public key certificate. This is slightly misleading, because
the certificate contains the public key; the display should really say that
there are no key pairs.
3. We create the principal and generate a key pair for our server:
D:\work\sun_signed_jar>javakey -cs "Robusta"
Created identity [Signer]Robusta[D:/work/sun_signed_jar/serverdb.obj][not trusted]
D:\work\sun_signed_jar>javakey -gk "Robusta" DSA 512
Generated DSA keys for Robusta (strength: 512).
D:\work\sun_signed_jar>
4. Next we want to use the CA key pair to sign the server’s public key. First
we export the public key to a file:
Java Gets Out of Its Box
395
D:\work\sun_signed_jar>javakey -ek Robusta pubkey.robusta
Public key exported to pubkey.robusta.
D:\work\sun_signed_jar>
5. We need to import this key into the CA’s key database. To do this we
comment out the identity.database entry that we added to java.security
(see Step 1 on page 395), create the server’s principal in the CA database
and import the public key:
D:\work\sun_signed_jar>javakey -cs "Robusta"
Created identity [Signer]Robusta[D:/work/sun_signed_jar/serverdb.obj][not trusted]
D:\work\sun_signed_jar>javakey -ik Robusta pubkey.robusta
Set public key from pubkey.robusta for Robusta.
D:\work\sun_signed_jar>
6. Now we can sign the server’s certificate. The process is the same as for
the CA certificate. First we create the certificate information file:
issuer.name=JamJar CA
issuer.cert=1
subject.name=Robusta
subject.real.name=All Java is secure but signed Java is Robusta
subject.org.unit=ISL
subject.org=IBM
subject.country=UK
start.date=12 Sep 1997
end.date=12 Sep 1998
serial.number=2
out.file=cert.robusta
7. Then we sign the certificate:
D:\work\sun_signed_jar>javakey -gc certinfo.robusta
Generated certificate from directive file certinfo.robusta.
D:\work\sun_signed_jar>
8. To use the certificate, we have to import it into the server’s key database,
which means that we first have to find out the number assigned to the
certificate in the CA database and export the certificate to a file:
396
Java 2 Network Security