Introduction

In another paper I have published critics about some basic weaknesses of the present Java runtime system (the one of JDK 1.02 as well as the one of JDK 1.1). Here are some complementary and partially independent remarks on the new security API proposed with JDK 1.1 (beta2 pre-release).

The main problem I have found is related to the interaction of serialization with the management of identity and public/private key database as proposed by javakey. This database is stored in a permanent serial object file. At init time, this persistent object is loaded and stored in a static variable of the class IdentityScope of the package java.security. This object (let me call it "systemscope") is publicly available via the request: IdentityScope.getSystemScope(). An untrusted applet can then get the systemscope of the client site and send to the server it comes from a serialized version of this object . No security check is made to forbid this and thus no hack is necessary to program such an applet. While the applet generally cannot access and use the private keys of the signers recorded in the systemscope of the client, it is perfectly possible for the server to access and use them once it has received the serialized version of this object.


Identity Databases and Signers

An "identity database" is an object of (abstract) type java.security.IdentityScope where "identities" are stored along with with their public keys and certificates. It is also possible to store in an identity database "signers": these are special identities with a private/public pair of keys, the private key being used for signing data.

The java.security API provides almost only interfaces and abstract classes. To make all the security stuff work, an implementation of these abstract classes is needed. This the role of "security providers". JDK 1.1 comes with a default security provider, the package sun.security.provider. With the classes SystemIdentity, SystemSigner and IdentityDatabase this package provides a basic implementation of the abstract classes Identity, Signer and IdentityScope defined in java.security. It provides also basic implementations of cryptographic algorithms.

A third-party program has no need to know what is the particular security provider used on a site: a static class, java.security.Security, is used as an interface between such a program and the security provider. For example, to get a signature algorithm object, a program has just to request it from this static class. The class java.security.Security is aware of the security provider in use (thanks to an information file that it reads at init time), and can provide to the requester an instance of the concrete class implementing in its methods the required algorithm. The requester has no need to know the concrete type of the object it receives.

Generally, all communications between application programs and the security provider in use is done through the static class java.security.Security. However, there is an important exception. At init time, this class reads from the security information file what is the particular class used for the default identity database. With the basic security provider of Sun, this class is sun.security.provider.IdentityDatabase. When initialized, this last class reads a file "identitydb.obj" where is stored a serialized instance (of itself). This serialized instance can be created and modified thanks to the tool javakey provided by Sun. By deserializing this object, IdentityDatabase creates a runnable instance assigned to a static variable of the abstract class java.security.IdentityScope. It is this instance (that I call "systemscope") that can be obtained by the request: IdentityScope.getSystemScope() with the abstract type IdentityScope. Any application program or applet is allowed to access the identities strored inside systemscope and use their public keys and certificates to verify the authenticity of incoming data. But, generally, the private key of a signer stored in systemscope cannot be known nor used.

Let me explain this last point. In the sequel, I suppose that only the basic security provider of Sun is used. The public key of an identity can be known thanks to a public method of the abstract class Identity. The private key of a signer can be divulgated only by the method getPrivateKey() of the abstract class Signer defined in the package java.security and by the method getSignerPrivateKey() defined in the concrete subclass SystemSigner of the package sun.security.provider. These methods are protected. So, it is not possible for an object belonging to a class external to these packages to get the private key of a signer nor to use it.

There are only two cases where the private key of a (Sun) signer can be used. First, by javakey itself to sign jar files (note that javakey is a small script to launch a java program of main class: java.security.provider.Main). Secondly, by programs that have classes declared to be members of the package java.security or sun.security.provider. This last case is possible because, in the present implementation of the java runtime system, nothing is made to guarantee the integrity of packages: an application installed in the classpath can define new classes or hide already installed classes of any package (provided by others) it wants to modify or extend (an untrusted applet can also define new classes in any local package except those that are explicitely protected from such extensions by the applet viewer: by default only the packages of name prefixed by "java", "sun" and "netscape").

Here is an example of a small java program that can sign a file by using the private key of a signer previously defined with javakey:

I consider the possibility illustrated by this example quite dangerous: any java program you install in your classpath can access and use all the private keys stored in the identity database. In my other paper about the weaknesses of the present Java runtime system, I already designated the possibility to modify or extend packages provided by others as a major problem from a security viewpoint.



How Untrusted Applets Can Steal Private Keys

Generally, an untrusted applet cannot define new classes in the packages java.security and sun.security.provider. So it cannot get the private key of a signer recorded in systemscope.

The object systemscope is computed at init time from the information stored in the file named "identitydb.obj" created and maintained by javakey. In order to make the computation of systemscope easy, the file "identitydb.obj" contains just a serialized representation of the object systemscope. To compute this object at init time is then quite straightforward:

        IdentityScope systemscope;
        try
        {FileInputStream fileInputStream = new FileInputStream(new File("identitydb.obj"));
        ObjectInputStream objectInputStream = new ObjectInputStream(inputStream);
        systemscope = (IdentityScope)objectInputStream.readObject();
        IdentityScope.setSystemScope(systemscope);
        }
        catch (Exception e){...}
        

But in order to recursively serialize systemscope and all the objects it records in its fields, all the involved classes (Identity, Signer, Key, PrivateKey, etc) must be "serializable". Presently, the methods readObj() and writeObj() used by these classes do not invoke any security check. Thus, an untrusted applet, while it cannot use directly the private keys recorded in systemscope, can send, via socket, a serialized version of systemscope to the server it comes from. On the server where locally the packages java.security and sun.security.provider can be freely extended, the private keys in the object systemscope of the client site, when deserialized, can be used by programs defining new classes in these packages (as illustrated before). On the server site, javakey can also be used with the stollen systemscope to sign jar files with the stollen private keys.

I give below the Java source of an applet that can steal systemscope:

The class BasicApplet being defined as:

On the server side the serialized version of the client's systemscope stollen by the above applet can be received and stored by the following program:

This example illustrates a big security hole: no hack is needed to make an untrusted applet able to steal, and send to the server it comes from, secret ressources of the client site that it is not allowed to access or use on the client site itself (but that can be exploited on the sever site).


Conclusion

The apparent cause of the problem is that the readObject() and writeObject() methods presently used by the involved classes never perform any security check. However, the security risks brought by serializable objects are well known and well explained in JDK 1.1 documentation. But Java API architecture may become so subtil, and this is especially true of the new security API, that it is not obvious to detect security breaches when using serializable classes.

To correct this main security breach there are several solutions: