In an article entitled Implementing a Security Policy (Java Developer's Journal, Vol. 2., Issue 8), Qusay Mahmoud wrote on the practical uses of the Java SecurityManager class.
Starting with JDK 1.1, Java provides a number of classes and interfaces for implementing a comprehensive security policy. These include cryptographic interfaces for signature production and verification, cryptographic checksums as well interfaces for access control. This article focuses on practical uses of the cryptographic interfaces of JDK 1.1, including their use in creating and verifying the origin of trusted applets.
Many organizations are attempting to harness the connectivity power of the Internet to conduct real business on a global scale and earn more money. In order for electronic business to flourish, the infrastructure must provide a basic framework of trust. The elements of trust include provisions for:
- Identification and Authentication - The process of ascertaining the identity of one party to another
- Authorization and Access Control - The process of granting and denying the rights to a party to perform a certain operation
- Privacy-protection - The assurance that the details of the transaction can not be revealed to a third party.
- Integrity-protection - the assurance that a receiving party would reject a transaction that is accidentally or maliciously corrupted
- Non-repudiation - The assurance that an entity cannot deny being a party to a transaction after it voluntarily engages in that transaction
- Audit - A means of securely keeping a detailed record of events for possible future examination
Java is rapidly becoming the language of choice for developing and deploying Internet-based applications. The longevity of Java depends on many factors, including the availability of built-in tools to implement a security policy that reflects the needs of individual organizations.
Trust Model before JDK 1.1
From its very inception, the Java language has implemented a simple and somewhat effective trust model. This model - known to Java developers as the sandbox - provides a binary choice within the runtime environment: trust everything that is local, but do not trust anything that is downloaded. In other words, the Java runtime completely trusts each and every Java application and completely restricts each and every downloaded Java applet. The Java runtime restricts applets from accessing local files. This restriction manifests itself in several ways, including the restriction to load a local class, to link with a local library and to read and write local files. Implementations of the JDK within each browser modify the SecurityManager class to provide different degrees of access to downloaded applets. However, the trust model remains binary.
The Sandbox Dilemma: To
Use or Not to Use
Experience shows that from a security policy standpoint, enforcing the sandbox model is very important. The sandbox protects you from malicious attacks and misbehaving applications that can delete your files, misuse private information or consume your system resources. Simultaneously, most applications need to interact with users in a personalized way. This means that in order for an applet to provide a useful function - especially in the context of electronic business - it does need access to some local resources.
Solutions to the Sandbox Dilemma
JavaSoft addresses the sandbox dilemma in two releases of the JDK. JDK 1.1 allows the originator of an applet to digitally sign the applet (see the sidebar for a primer on digital signatures). The digital signature provides a highly tamper resistant fingerprint of the applet. The client who downloads the applet can choose to trust the originator and allow it to access local resources (files, network connections, etc.). Therefore, a client can treat each applet differently based on its digital signature. However, the trust model for a given applet remains binary. JavaSoft addresses this problem in JDK 1.2 with fine grain access control. JDK 1.2 introduces the concept of protection domains, which allows a client to specify exactly which resources a given applet may access and for what reason (read, write, connect, etc.).
JDK 1.1 Tools for Applet Signing
JDK 1.1 provides two tools for applet signing: javakey and jar. Use javakey to create identities and load them into your identity database. Once you create an identity, you can designate it as a trusted identity. Applets that are signed by a trusted identity are trusted applets. JDK 1.1 treats trusted applets as local applications: a trusted applet has access to everything a local application can access. Using javakey you can also create a signer identity. A signer is an identity with a private key. You create a signer on the server side before signing the applet. Table 1 shows the various options of javakey.
Even though jar is not a security tool, it is very useful for packaging a series of files, including Java classes, into a single downloadable entity. The jar command provides the same function as the UNIX tar command. Table 2 shows the various options of jar.
Steps in Creating a Signed Applet
In order to create a signed applet, you need to take the following steps. Listings 1 and 2 show an example of each step. The string ambrosia is the prompt of the system that was used to generate these examples.
Step 1: Create a signer identity and generate key pair.
Using javakey. you would create a signer and trusted identity. In listing 1, with the command  we create a signer identity called OpenHorizon. After creating a signer identity, you need to generate a public/private key pair for that identity. The algorithm you choose for the signing and verifying signatures and length of the key are important parameters contributing to the overall security of your environment. In listing 1, with command  we choose the Digital Signature Algorithm (DSA) with a key length of 1024 bits. The public and private keys are stored in two files called oh.pub and oh.priv respectively. Note that it is extremely important to protect the private key file.
Step 2: Generate a certificate for the signer
Every signer must have its public key certified. In a real Public Key Infrastructure (PKI) environment, a Certification Authority (CA) issues a certificate which binds a signer's identity to the signer's public key. The CA itself may have a certificate issued to it by another, higher authority CA. This is called chain-of-trust and is not supported by JDK 1.1. In listing 1, with command  we create a self-issued certificate: a certificate issued by the identity OpenHorizon for itself. Note that certificates are issued according to a certificate directive file. The certificate directive is shown in listing 1, with command . It specifies, among other information, a 1 year validity period, a serial number (1100) and the name of the file where the certificate will be stored (oh.cert).
Step 3: Create and sign the archive
You would follow the procedures in steps 1 and 2 to prepare your system for signing applets. Follow the procedures in steps 3 and 4 every time you need to create and deploy a signed applet. In listing 1, with command  we first create a jar file of all classes that comprise a sample of a software called Ambrosia. We call the target jar file AmbrosiaSamples.jar. Using command , we sign the jar file with the private key of an identity whose name appears in a signature directive file (listed with command ). The signature file name that appears at the end of the signature directive file is used to create the signature as an individual file (OHSig.DSA). The signature file is included in the jar file.
Step 4: Deploy signed archive on the web server.
Javakey creates a file with the same name (AmbrosiaSamples.jar) and the extension .sig. We rename Ambrosia.jar.sig to Ambrosia.jar, which is a more suitable name for deploying the signed classes on the web server. In listing 1, with command  we rename the jar file. Listing 2 shows the HTML file for one applet (ohpub.class) embedded in the signed jar file.
Configuring the Client
The client must be configured to recognize and validate the signer of the applet. Listing 3 shows the javakey commands you can use to configure the client. You may have to use a different set of commands depending on your browser. The client must obtain the certificate file (oh.cert in this example) before loading it to the identity database (listing 3, command ).
The Java Cryptographic
In this section of the article we focus on the Java Cryptographic Architecture (JCA). Figure 1 shows the basic hierarchy of the JCA. There are three classes that form the foundation of the JCA: KeyPairGenerator, MessageDigest and Signature. You may notice that the JCA does not include any classes for encryption. The reason is that including encryption classes or interfaces would make the JDK non-exportable. Therefore, JavaSoft has elected to provide an adjunct package called the Java Cryptographic Extensions (JCE), which is only available to customers in the United States and Canada.
The JCA is based on the notion of Cryptographic Package Providers (CPP). As such, the JDK itself simply provides engine classes and interfaces. Engine classes and interfaces may be implemented by one or more CPP's. JDK 1.1 comes with a default implementation for the DSA, MD5 and SHA algorithms. Other CPPs can provide implementations for the same or new algorithms. CPPs may be installed statically at the time of JDK 1.1 installation. You can also use methods of the java.security.Security class to dynamically manage providers.
Getting an Instance of an Engine Class
All three engine classes have a factory method that allows you to instantiate an object with a specified algorithm and a specified provider. For example, in listing 4 line 9 we obtain an instance of the DSA signature algorithm provided by the default CPP (in this case, SUN). If desired, one can use another version of the getInstance method to name a specific provider.
Signing Data and Verifying
The Signature class can be used to both sign data and verify the digital signature of the data. Consequently, when you instantiate a Signature object you must initialize it to be either in a SIGN state or a VERIFY state. Figure 2 illustrates the states of the Signature class. Once in a given state, you can supply the Signature class with as much data as you wish and with as many update method calls as you wish. After you have supplied all the data, you would invoke either the sign or the verify method to perform the corresponding operation. After the data is signed or verified, the Signature object returns to an uninitialized state.
Digitally Signing Data
Listing 4 shows a partial code segment for digitally signing data. Lines 1 and 2 import the security packages needed for signing data. Lines 3 and 4 define two byte arrays: one is the input data to be signed, and the other is the digital signature. At line 9 we instantiate a Signature object which implements the DSA algorithm by the default provider (i.e., SUN). Line 10 places the object in the SIGN state. Line 11 feeds the data to the object and line 12 produces the digital signature. Note that if we were receiving the data in chunks, say from a user, we could call the update method as many times as required until there was no more data. Also note that the private key that is used to sign the data must be loaded (not shown in the code) prior to calling the initSig method. Use methods of KeyPair and Key interfaces to set the private key.
Verifying the Digital Signature
Listing 5 shows a partial code segment for verifying the digital signature of some arbitrary data. Lines 1 and 2 import the security packages needed for verifying the digital signature on the data. Lines 3 and 4 define two byte arrays: one is the input data whose digital signature is to be verified and one is the actual digital signature. Line 6 defines a boolean that will be set by the verify method to true or false depending on whether the signature is valid or not. At Line 10 we instantiate a Signature object which implements the DSA algorithm by the default provider (i.e. SUN). Line 11 places the object in the VERIFY state. Line 12 feeds the data to the object and Line 13 verifies the digital signature. Again, note that if we were receiving the data in chunks, we could call the update method as many times as required until there was no more data. Also, note that the public key that is used to verify the digital signature must be loaded (not shown in the code) prior to calling the initVerify method. Use methods of KeyPair and Key interfaces to set the public key.
JDK 1.1 provides an excellent starting point for building a secure and trustworthy infrastructure. Java security is much more than code safety and sandboxing. With JavaSoft's plan to provide more security classes and interfaces, either as part of the JDK or as adjunct packages, Java will solidify its position as the language of choice for developing and deploying Internet-based applications.
About the Author
Jahan Moreh is the chief security architect at Open Horizon, Inc. (www.openhorizon.com). He is a frequent speaker on the topic of Java security at various conferences. Additionally, he is a senior member of the teaching staff at UCLA's department of Information Science where he teaches classes in distributed system security and CORBA. You can reach him at [email protected]
Listing 1: Creating a signed applet.
ambrosia -> javakey -cs OpenHorizon true
created identity [Signer]OpenHorizon[uninitialized][trusted]
ambrosia-> javakey -gk OpenHorizon DSA 1024 oh.pub oh.priv
Generated DSA keys for OpenHorizon (strength: 1024).
Saved public key to oh.pub.
Saved private key to oh.priv.
ambrosia-> cat oh.certDir
subject.real.name=Open Horizon, Inc.
start.date=10 April 1997
end.date= 10 April 1998
ambrosia-> javakey -gc oh.certDir
Generated certificate from directive file oh.certDir
ambrosia-> jar cf AmbrosiaSamples.jar *.class
ambrosia-> cat oh.signDir
ambrosia-> javakey -gs oh.signDir AmbrosiaSamples.jar
Adding entry: META-INF/MANIFEST.MF
Creating entry: META-INF/OHSIG.SF
Creating entry: META-INF/OHSIG.DSA
Adding entry: ohsub.class
Adding entry: ohpub.class
Signed JAR file AmbrosiaSamples.jar using directive file oh.signDir
ambrosia-> mv AmbrosiaSamples.jar.sig AmbrosiaSamples.jar
Listing 2: HTML file for deploying on the Web server.
<TITLE> Open Horizon Publishing Application</TITLE>
<APPLET code = ohpub.class
archive = AmbrosiaSamples.jar
width = 200
height = 300>
Listing 3: Setting up the client to verify a signed applet.
ambrosia-> javakey -c OpenHorizon true
Created identity OpenHorizon[uninitialized][trusted]
ambrosia-> javakey -ic OpenHorizon oh.cert
Imported certificate from oh.cert for OpenHorizon
Listing 4: Producing a Digital Signature.
1 import java.security
2 import java.security.interfaces
3 byte toBeSigned ;
4 byte signedData ;
5 . . . . . . .
6 // priv must be initialized with a copy of the
7 // private key. Not shown here.
8 PrivateKey priv;
9 Signature sig = Signature.getInstance(“DSA”);
12 signedData = sig.sign();
Listing 5: Verifying the Digital Signature.
1 import java.security
2 import java.security.interfaces
3 byte toBeVerified ;
4 byte theSignature ;
5 boolean isValid;
6 . . . . . . .
7 // pub must be initialized with a copy of the
8 // public key. Not shown here.
9 PublicKey pub;
10 Signature sig = Signature.getInstance(“DSA”);
13 isValid = sig.verify(theSignature);