The Java programming language is one of the fastest-growing technologies in use on the Internet today. The principal reason why Java has scored over other languages is the promise that an application written once in Java can be run from any machine that has a JVM. From the early stages of Java development, it was realized that this feature poses the greatest challenge to Java security because code distribution is risky.
The JDK 1.0 Sandbox Security Model
The entire focus of the initial security model provided by Version 1.0 of the Java platform (known as the sandbox model ) was to treat code downloaded from a remote location as untrusworthy and provide a restricted environment (the sandbox) to limit the resources that could be accessed by the alien code. At the same time local code was considered trustworthy and was allowed full access to all the system resources, as illustrated in the figure :
Figure. JDK 1.0 Sandbox Security Model
This was achieved by using the three components, “The Need for Java Security” namely the class loader, the class file verifier and the security manager. However with the actions of the remote code constrained to a bare minimum, the Write Once, Run Anywhere benefit of Java could not be fully exploited.
Remote applets, though a powerful concept, were shackled by having to run inside a sandbox, and by not being able to perform several operations. They could not read local files and could not write to the disk. They had absolutely no access to the system resources. Moreover they could establish a network connection only with their servicing Web server. This heavily restricted the use of remote applets for all but cosmetic functions to decorate a Web page.
The Concept of Trusted Code in JDK 1.1
The next phase of evolution of Java security was based on an effort to increase the breathing space for remote code at the client location without compromising the safety of the client. The security architecture in JDK 1.1 introduced the concept of signed remote code. Remote codes, signed by a trusted entity, were permitted access to several of the system resources that were off limits for those remote programs without a trusted signature on them, as shown in the following figure:
Figure. Trusted and Untrusted Code in JDK 1.1
A remote code (a remote applet or servlet, for example) with an appropriate digital signature was treated with the same respect as local code, and so it could be considered trusted. An appropriate digital signature was one that was recognized as trusted by the client.
On the other hand, unsigned remote code or remote code signed with a digital signature not recognized as trusted by the client, was still confined to the sandbox.
Though this opened up interesting possibilities, the system was still rather crude, with all local Java applications enjoying full access to the system resources and all remotely loaded code running inside a sandbox, unless signed by a trusted entity.
The jar and javakey Tools
Starting with JDK 1.1, the Java platform has offered the jar command line tool to pack and deliver remote codes together with their signatures, if any, in the Java Archive (JAR) format. The JAR file format is based on the ZIP file format and is used for aggregating and compressing many files into one. Although the jar utility can be used as a general archiving tool, the primary motivation for its development was so that Java applets and their requisite components (class files, images, sounds, etc.) could be downloaded to a browser in a single HTTP transaction, rather than opening a new connection for each piece. This greatly improves the speed with which an applet can be loaded onto a Web page and begin functioning. The JAR format, like a ZIP format, also supports compression, which reduces the size of the file and improves download time still further. Additionally, a JAR file may be digitally signed by the applet authors to authenticate the origin of the code.
The JAR format is cross-platform, handles audio and image files as well as class files and is backward-compatible with existing applet code. JAR consists of a ZIP archive, as defined by PKWARE, containing a manifest file and potentially signature files (see http://www.pkware.com). The jar tool is basically a Java application that combines multiple files into a single JAR file. JDK 1.1 offered the javakey tool to sign JAR files.
JDK 1.1 Security API
The Java security API was built around the java.security package and its subpackages java.security.acl and java.security.interfaces. The first release for Java security, available in JDK 1.1, included primarily cryptography functions, which could be incorporated into Java-based applications. The cryptography framework in the Java security API was designed so that a new algorithm could be added later on without much difficulty and could be used in the same fashion as existing algorithms. For example, even if Digital Signature Algorithm (DSA) was the only built-in algorithm in this release, it was possible to use software from providers to help generate RSA signatures and key pairs for encryption.
The first release of Java security in JDK 1.1 included APIs for digital signatures, message digests, key management and access control lists (ACLs). APIs for data encryption and other functionalities, together with their implementations, were released separately in the Java Cryptography Extension (JCE) 1.1 as an add-on package to JDK, in accordance with United States export control regulations. The JCE APIs included block and stream cipher, symmetric and asymmetric encryption and support for multiple modes of operation and multiple encryption.
The Fine-Grained Access Control of Java 2
An obvious handicap with the JDK 1.1 security architecture was no easy way of achieving fine-grained access control, with all local code enjoying unrestricted access to all the system resources and all remote code subjected to sandbox constraints unless signed in a way recognizable to the client as trusted. By fine-grained access control , we mean the ability to grant specific permissions to a particular piece of code about accessing specific resources of the client (say read and write permission on file x , but only read permissions on file y and no permissions on file z ) depending on the signers of the code and/or the URL location from which the code was loaded. Thus, existence of a fine-grained access control would allow a user to specify access permissions on a case-by-case basis rather than a rigid classification of local code being fully trusted and remote code being untrusted and restricted to a sandbox, unless signed in a way recognizable to the client as fully trusted.
The new security architecture developed in Java 2 allows easy fine tuning of the access controls. The concept of signed code can now be extended to local code as well. With the new security model, all code, whether remotely downloaded or local, signed or unsigned, will have access to system resources based on what is defined in a policy file . This allows the client to explicitly specify the permissions to be granted to different signatories of code and different sources. This way the end user can download, install and run applications from the Web by granting them permissions for only those actions that are necessary. This will eliminate codes that have a hidden agenda, such as letting you play a nice game while sending your credit card information or your password file to a particular server at the same time.
Consider for example the following scenario, based on the JDK 1.1 security model. You download a little tic-tac-toe program from the Web. It is signed by an entity you trust, and you are sure that it will not crash your system. For this reason, you accept to run it. Nonetheless, this code reads your address book,nand sends all the e-mail addresses you have to the database of the nearest junk mailer. Though not very malicious, this is something we all would like to avoid. This is a very likely situation, since more and more software is just being brought off the net, and this trend is likely to continue for a long time. This might lead to fly-by-night software vendors, some of whom might come up with very innovative software, but some of whom you cannot really trust. With JDK 1.1, you do not have an option to restrict access to code to do only certain things. You either install the software, or you make do without it.
However, if you are running Java 2-enabled software, you can instruct the JVM, through modifications in a policy file, that code loaded from a particular URL (local or remote) and/or signed by a particular entity is restricted to specific local resources. For example, you may specify in the policy file that the code in question may read files in one particular directory and can do nothing else – cannot open sockets, cannot write or delete any files, etc. This is the fine-grained control mechanism offered by Java 2.
In versions of Java prior to Java 2, the JVM resource access was enforced by the sandbox security model, which was a function of the security manager. Extensions were usually limited to features implemented by the platform providers such as Web browsers and Web servers. When using Java 2, you can have full control over what each of your programs and applications is permitted to do – this was never possible until now. Similarly, you can now define the exact things an applet coming from a particular URL can do, or what any programs (applets, applications, servlets) signed by one or more particular entities can do. Further, in multi-user systems, the system administrator can define a default system policy, and each of the users of the system can have their own policy, which is combined with the system default.
Java programs now have the ability to define access restrictions on sensitive resources without having to write a new security manager or modify the underlying platform. For example, applets downloaded into a Java 2-enabled Web browser and servlets downloaded into a Java 2-enabled Web server can add resource access controls to a JVM without having to modify the underlying browser or server implementation. These new concepts of permission and policy enable the Java 2 platform to offer fine-grained, highly configurable, flexible, and extensible access control.
Figure . Fine-Grained Access Control Mechanism in Java 2 SDK
Lexical Scoping of Privilege Modifications
A new security feature implemented for the first time in Java 2 is the lexical scoping of privilege modification, which is a technique enforcing the least privileged mode. Using this technique, it is possible to enable only the execution of the piece of code that needs the privilege. All the sensitive code could therefore be added at one place and defined as privileged, by calling the doPrivileged() method, belonging to the java.security.Access Controller class.
Java 2 Security Tools
Java 2 provides four powerful security tools for ensuring confidentiality, integrity, authenticity of data and adequate control on access to various system and non-system resources. These are jar , keytool , jarsigner and Policy Tool.
The jar function is similar in Java 2 to what it was in JDK 1.1. JAR files acquire specific significance, since the old javakey , and its newer version jarsigner , can sign only JAR files.
The keytool command line utility creates key pairs – pairs of public and private keys – imports and exports X.509 V1, V2 and V3 certificates, generates self-signed X.509 V1 certificates and manages keystores. A keystore is a protected database that holds private keys as well as public keys and certificates. In the default implementation, a keystore is protected using a password and each private
key stored in the keystore is protected with a possibly different password. The private keys are used to digitally sign applications and applets whereas public keys are used to verify signed data, and certificates are used to verify whether a public key indeed belongs to the person it is supposed to belong.
The jarsigner command line tool signs JAR files and verifies the signature(s) of signed JAR files. It accesses the keystore when it needs to find:
• A private key when signing a JAR file
• A public key when verifying a signature
• A certificate when verifying a public key
In the Java 2 platform, the keytool and jarsigner command line utilities replace the JDK 1.1 tool javakey . The javakey tool had several shortcomings, the most significant of them being the fact that both the public and private keys were stored in the same, unprotected location (often called an identity database ). This allowed anyone with access to the identity database to determine all keys that were stored in the file. In contrast, private keys are now password protected in the keystore.
The Policy Tool utility, which is launched through the policytool command, creates and modifies the external policy configuration files that define the client’s security policy.
Java 2 Security API
In Java 2 two new subpackages have been added to the java.security package, and they are java.security.cert and java.security.spec. These packages offer more features to deal with X.509 certificates and to create certificate revocation lists (CRLs) and certificate signing requests (CSRs). In particular, java.security.Certificate, that in JDK 1.1 was an interface of abstract methods for managing an identity certificate, is completely deprecated in Java 2, which offers the entire package java.security.cert to handle certificates. Moreover, the package java.security.cert adds X.509 V3 support to certificates.
Java 2 also provides an additional certificate interface: the X509Extension interface in the java.security.cert package. This is an interface for X.509 extensions. The extensions defined for X.509 V3 certificates and V2 CRLs provide methods for associating additional attributes with users or public keys, for managing the certification hierarchy, and for managing CRL distribution.
A Comparison of the Three Java Security Models
Java security models based on seven parameters, which are:
• Resource access to local unsigned code
This refers to the options provided by the security architecture to a client to determine access to local resources for local unsigned code.
• Resource access to local signed code
This refers to the options provided by the security architecture to a client to determine access to local resources for local signed code.
• Resource access to remote unsigned code
This refers to the options provided by the security architecture to a client to determine access to local resources for remote unsigned code.
• Resource access to remote signed code
This refers to the options provided by the security architecture to a client to determine access to local resources for remote signed code.
• Lexical scoping of privilege modification
This refers to the availability of the option in the security architecture to temporarily grant more privileges to a specific piece of code in an execution thread, which are additional to the privileges the code would have enjoyed by itself. This facility is available only with Java 2 and achieved with the help of the doPrivileged() method.
• Cryptographic services for data confidentiality/integrity
This refers to the availability of cryptographic services for data confidentiality and integrity. Such services became available only with JDK 1.1.
• Digital signature services for code signing
This refers to the facility of digital signature services for signing code. Such services became available only with JDK 1.1.
Table 1. Evolution of the Java Security Model
This comparison shows the increasing flexibility and functionality provided by the evolving Java security model in determining a security policy.