Java 2 Platform Security
Date: Jan 6, 2006
Sample Chapter is provided courtesy of Prentice Hall Professional.
Topics in This Chapter
- Java Security Architecture
- Java Applet Security
- Java Web Start Security
- Java Security Management Tools
- J2ME Security Architecture
- Java Card Security Architecture
- Securing the Java Code
Sun's Java philosophy of "Write Once, Run Anywhere" has been an evolving success story since its inception, and it has revolutionized the computing industry by delivering to us the most capable platform for building and running a wide range of applications and services. In general, the Java platform provides a general-purpose object-oriented programming language and a standard runtime environment for developing and delivering secure, cross-platform application solutions that can be accessed and dynamically loaded over the network or run locally.
With the release of the Java 2 Platform, Sun categorized the Java technologies under three key major editions in order to simplify software development and deployment. The Java 2 Standard Edition (J2SE) provides the runtime environment and API technologies for developing and executing basic Java applications, and it also serves as the secure foundation for running Java enterprise applications. The Java 2 Enterprise Edition (J2EE), or the J2EE Platform, is a set of standards and API technologies for developing and deploying multi-tier business applications. To support Java on microdevices and embedded systems, Java 2 Micro Edition (J2ME) provides the runtime environment and API technologies for addressing the needs of consumer electronics and devices. With its widespread adoption, today Java technology is enabled and executed from smart cards to microdevices, handhelds to desktops, workstations to enterprise servers, mainframes to supercomputers, and so on.
To facilitate end-to-end security of the Java platform-based application solutions, the Java runtime environment (JRE) and the Java language provide a solid security foundation from the ground up by imposing strong format and structural constraints on the code and its execution environment. This distinguishes the Java platform from other application programming languages—it has a well-defined security architectural model for programming Java-based solutions and their secure execution.
In this chapter, we will explore the various Java platforms and the intricate details of their security architecture that contribute to the end-to-end security of Java-based application solutions. In particular, we will study Java security and the inherent features of the following technologies:
- J2SE security
- Java applet security
- Java Web start security
- Java security management tools
- J2ME security
- Java Card security
- Java Code obfuscation
Java Security Architecture
Security has been an integral part of Java technology from day one. Security is also an evolving design goal of the Java community—building and running secure and robust Java-based network applications. The primary reason for Java's success today as a secure execution environment is the intrinsic security of its architectural foundation—the Java Virtual Machine (JVM) and the Java language. This foundation achieves the basic Java security goal and its definitive ways for extending security capabilities to ensure features such as confidentiality, integrity, trust, and so forth. A second reason for its success is its ability to deliver an interoperable and platform-neutral security infrastructure that can be integrated with the security of the underlying operating system and services.
The Java Virtual Machine (JVM)
The JVM is an abstract computing engine that resides on a host computer. It is the execution environment for the Java programming language and has the primary responsibility for executing the compiled code by interpreting it in a machine-independent and cross-platform fashion. The JVM is often referred to as the Java runtime environment. While executing a Java program running on top of the JVM, the JVM insulates the application from the underlying differences of the operating systems, networks, and system hardware, thus ensuring cross-platform compatibility among all of the implementations of the Java platform.
The Java language allows creation of general-purpose programs called Java classes that represent a Java program or an application. The Java classes compile into a format called Java's executable bytecodes, which are quite similar to the machine language that can run on top of a JVM. The JVM also allows users to download and execute untrusted programs and applications from remote resources or over a network. To support delivery of Java components over the network, the JVM controls the primary security layer by protecting users and the environment from malicious programs. To enable security, the JVM enforces stringent measures ensuring systems security on the host client machine and its target server environments.
Distributing the executable Java bytecode over a network or running automatically inside a Web browser or a client's machine leads to different security risks and attacks, such as disclosure of the target environment to the untrusted applications and damage or modification of the client's private information and data. For example, Java applets downloaded from a network are not allowed to have access to, read from, or write to a local file system. They are also not allowed to create network connections to any host system except the one where they are deployed. On the other hand, stand-alone Java applications that reside and run locally as trusted applications are not subjected to these security features. The key issue is that allowing untrusted applications such as Java applets to be downloaded from a network via a Web browser and letting them access certain resources on the host computer paves the way for security breaches and becomes a potential avenue for the spread of viruses. To prevent known security breaches and threats, the JVM provides a built-in Java security architecture model, configurable security policies, access control mechanisms, and security extensions. Because of the built-in JVM safety features, Java programs can run safely and are more securely protected from known vulnerabilities.
The Java Language
Java is a general-purpose object-oriented programming language similar to C++. It delivers platform-neutral compiled code that can be executed using a JVM and is intended for use in distributed application environments, heterogeneous systems, and diverse network environments. The Java language is also designed to provide for the security and integrity of the application and its underlying systems at all levels—from the Java language constructs to the JVM runtime and from the class library to the complete application.
The several inherent features of the Java language that provide for the secure Java platform are as follows:
- The language defines all primitives with a specific size and all operations are defined to be in a specific order of execution. Thus, the code executed in different JVMs will not differ from the specified order of execution.
- The language provides access-control functionality on variables and methods in the object by defining name space management for type and procedure names. This secures the program by restricting access to its critical objects from untrusted code. For example, access is restricted by qualifying the type members as public, protected, private, package, etc.
- The Java language does not allow defining or dereferencing pointers, which means that programmers cannot forge a pointer to the memory or create code defining offset points to memory. All references to methods and instance variables in the class file are done via symbolic names. The elimination of pointers helps to prevent malicious programs like computer viruses and misuse of pointers such as accessing private methods directly by using a pointer starting from the object's pointer, or running off the end of an array.
- The Java object encapsulation supports "programming by contract," which allows the reuse of code that has already been tested.
- The Java language is a strongly typed language. During compile time, the Java compiler does extensive type checking for type mismatches. This mechanism guarantees that the runtime data type variables are compatible and consistent with the compile time information.
- The language allows declaring classes or methods as final. Any classes or methods that are declared as final cannot be overridden. This helps to protect the code from malicious attacks such as creating a subclass and substituting it for the original class and override methods.
- The Java Garbage Collection mechanism contributes to secure Java programs by providing a transparent storage allocation and recovering unused memory instead of deallocating the memory using manual intervention. This ensures program integrity during execution and prevents programmatic access to accidental and incorrect freeing of memory resulting in a JVM crash.
With these features, Java fulfills the promise of providing a secure programming language that gives the programmer the freedom to write and execute code locally or distribute it over a network.
Java Built-in Security Model
In the previous two sections, we briefly looked at the basic security features provided by the JVM and the Java language. As part of its security architecture, Java has a built-in policy-driven, domain-based security model. This allows implementing security policies, protecting/controlling access to resources, rule-based class loading, signing code and assigning levels of capability, and maintaining content privacy.
In the first release of the Sun Java Platform, the Java Development Kit 1.0.x (JDK) introduced the notion of a sandbox-based security model. This primarily supports downloading and running Java applets securely and avoids any potential risks to the user's resources. With the JDK 1.0 sandbox security model, all Java applications (excluding Java applets) executed locally can have full access to the resources available to the JVM. Application code downloaded from remote resources, such as Java applets, will have access only to the restricted resources provided within its sandbox. This sandbox security protects the Java applet user from potential risks because the downloaded applet cannot access or alter the user's resources beyond the sandbox.
The release of JDK 1.1.x introduced the notion of signed applets, which allowed downloading and executing applets as trusted code after verifying the applet signer's information. To facilitate signed applets, JDK 1.1.x added support for cryptographic algorithms that provide digital signature capabilities. With this support, a Java applet class could be signed with digital signatures in the Java archive format (JAR file). The JDK runtime will use the trusted public keys to verify the signers of the downloaded applet and then treat it as a trusted local application, granting access to its resources. Figure 3-1 shows the representation of a sandbox in the JDK 1.1 security model.
Figure 3-1 JDK 1.1 security model
Java 2 Security Model
The release of J2SE [J2SE] introduced a number of significant enhancements to JDK 1.1 and added such features as security extensions providing cryptographic services, digital certificate management, PKI management, and related tools. Some of the major changes in the Java 2 security architecture are as follows:
- Policy-driven restricted access control to JVM resources.
- Rules-based class loading and verification of byte code.
- System for signing code and assigning levels of capability.
- Policy-driven access to Java applets downloaded by a Web browser.
In the Java 2 security architecture, all code—regardless of whether it is run locally or downloaded remotely—can be subjected to a security policy configured by a JVM user or administrator. All code is configured to use a particular domain (equivalent to a sandbox) and a security policy that dictates whether the code can be run on a particular domain or not. Figure 3-2 illustrates the J2SE security architecture and its basic elements.
Figure 3-2 Java 2 Security architecture and basic elements
Let's take a more detailed look at those core elements of the Java 2 security architecture.
Protection Domains ( java.security.ProtectionDomain ): In J2SE, all local Java applications run unrestricted as trusted applications by default, but they can also be configured with access-control policies similar to what is defined in applets and remote applications. This is done by configuring a ProtectionDomain, which allows grouping of classes and instances and then associating them with a set of permissions between the resources. Protection domains are generally categorized as two domains: "system domain" and "application domain." All protected external resources, such as the file systems, networks, and so forth, are accessible only via system domains. The resources that are part of the single execution thread are considered an application domain. So in reality, an application that requires access to an external resource may have an application domain as well as a system domain. While executing code, the Java runtime maintains a mapping from code to protection domain and then to its permissions.
Protection domains are determined by the current security policy defined for a Java runtime environment. The domains are characterized using a set of permissions associated with a code source and location. The java.security.ProtectionDomain class encapsulates the characteristics of a protected domain, which encloses a set of classes and its granted set of permissions when being executed on behalf of a user.
Permissions ( java.security.Permission ): In essence, permissions determine whether access to a resource of the JVM is granted or denied. To be more precise, they give specified resources or classes running in that instance of the JVM the ability to permit or deny certain runtime operations. An applet or an application using a security manager can obtain access to a system resource only if it has permission. The Java Security API defines a hierarchy for Permission classes that can be used to configure a security policy. At the root, java.security.Permission is the abstract class, which represents access to a target resource; it can also include a set of operations to construct access on a particular resource. The Permission class contains several subclasses that represent access to different types of resources. The subclasses belong to their own packages that represent the APIs for the particular resource. Some of the commonly used Permission classes are as follows:
For wildcard permissions |
-java.security.AllPermission |
For named permissions |
-java.security.BasicPermission |
For file system |
-java.io.FilePermission |
For network |
-java.net.SocketPermission |
For properties |
-java.lang.PropertyPermission |
For runtime resources |
-java.lang.RuntimePermission |
For authentication |
-java.security.NetPermission |
For graphical resources |
-java.awt.AWTPermission |
Example 3-1 shows how to protect access to an object using permissions. The code shows the caller application with the required permission to access an object.
Example 3-1. Using Java permissions to protect access to an object
// Create the object that requires protection String protectedObj = "For trusted eyes only"; // create the required permission that will // protect the object. // Guard, represents an object that is used to protect // access to another object. Guard myGuard = new PropertyPermission ("java.home", "read"); // Create the guard GuardedObject gobj = new GuardedObject(protectedObj, myGuard); // Get the guarded object try { Object o = gobj.getObject(); } catch (AccessControlException e) { // Cannot access the object }
Permissions can also be defined using security policy configuration files (java.policy). For example, to grant access to read a file in "c:\temp\" (on Windows), the FilePermission can be defined in a security policy file (see Example 3-2).
Example 3-2. Setting Java permissions in policy configuration file
grant{ permission java.io.FilePermission "c:\\temp\\testFile", "read"; };
Policy: The Java 2 security policy defines the protection domains for all running Java code with access privileges and a set of permissions such as read and write access or making a connection to a host. The policy for a Java application is represented by a Policy object, which provides a way to declare permissions for granting access to its required resources. In general, all JVMs have security mechanisms built in that allow you to define permissions through a Java security policy file. A JVM makes use of a policy-driven access-control mechanism by dynamically mapping a static set of permissions defined in one or more policy configuration files. These entries are often referred to as grant entries. A user or an administrator externally configures the policy file for a J2SE runtime environment using an ASCII text file or a serialized binary file representing a Policy class. In a J2SE environment, the default system-wide security policy file java.policy is located at <JRE_HOME>/lib/security/ directory. The policy file location is defined in the security properties file with a java.security setting, which is located at <JRE_HOME>/lib/security/java.security.
Example 3-3 is a policy configuration file that specifies the permission for a signed JAR file loaded from "http://coresecuritypatterns.com/*" and signed by "javaguy," and then grants read/write access to all files in /export/home/test.
Example 3-3. Setting codebase and permissions in policy configuration file
grant signedBy "javaguy", codebase "http://coresecuritypatterns.com/*" { permission java.io.FilePermission "/export/home/test/*", "read,write"; };
The J2SE environment also provides a GUI-based tool called "policytool" for editing a security policy file, which is located at "<JAVA_HOME>/bin/policytool."
By default, the Java runtime uses the policy files located in:
${java.home}/jre/lib/security/java.policy ${user.home}/.java.policy
These policy files are specified in the default security file:
${java.home}/jre/lib/security/java.security
The effective policy of the JVM runtime environment will be the union of all permissions in all policy files. To specify an additional policy file, you can set the java.security.policy system property at the command line:
java -Djava.security.manager -Djava.security.policy=myURL MyClass
To ignore the policies in the java.security file and only use the custom policy, use '==' instead of '=':
java -Djava.security.manager -Djava.security.policy==Mylocation/My.policy MyClass
SecurityManager ( java.lang.SecurityManager ): Each Java application can have its own security manager that acts as its primary security guard against malicious attacks. The security manager enforces the required security policy of an application by performing runtime checks and authorizing access, thereby protecting resources from malicious operations. Under the hood, it uses the Java security policy file to decide which set of permissions are granted to the classes. However, when untrusted classes and third-party applications use the JVM, the Java security manager applies the security policy associated with the JVM to identify malicious operations. In many cases, where the threat model does not include malicious code being run in the JVM, the Java security manager is unnecessary. In cases where the SecurityManager detects a security policy violation, the JVM will throw an AccessControlException or a SecurityException.
In a Java application, the security manager is set by the setSecurityManager method in class System. And the current security manager is obtained via the getSecurityManager method (see Example 3-4).
Example 3-4. Using SecurityManager
SecurityManager mySecurityMgr = System.getSecurityManager(); if (mySecurityMgr != null) { mySecurityMgr.checkWrite(name); }
The class java.lang.SecurityManager consists of a number of checkXXXX methods like checkRead (String file) to determine access privileges to a file. The check methods call the SecurityManager.checkPermission method to find whether the calling application has permissions to perform the requested operation, based on the security policy file. If not, it throws a SecurityException.
If you wish to have your applications use a SecurityManager and security policy, start up the JVM with the -Djava.security.manager option and you can also specify a security policy file using the policies in the -Djava.security.policy option as JVM arguments. If you enable the Java Security Manager in your application but do not specify a security policy file, then the Java Security Manager uses the default security policies defined in the java.policy file in the $JAVA_HOME/jre/lib/security directory. Example 3-5 programmatically enables the security manager.
Example 3-5. Using SecurityManager for restricting access control
// Before the security manager is enabled, // this call is possible System.setProperty("java.version","Malicious: Delete"); try { // Enable the security manager SecurityManager sm = new SecurityManager(); System.setSecurityManager(sm); } catch (SecurityException se) { // SecurityManager already set } // After the security manager is enabled: // This call is no longer possible; // an AccessControlException is thrown System.setProperty ("java.version", "Malicious: Delete");
The security manager can also be installed from the command-line interface:
java -Djava.security.manager <ClassName>
AccessController ( java.security.AccessController ): The access controller mechanism performs a dynamic inspection and decides whether the access to a particular resource can be allowed or denied. From a programmer's standpoint, the Java access controller encapsulates the location, code source, and permissions to perform the particular operation. In a typical process, when a program executes an operation, it calls through the security manager, which delegates the request to the access controller, and then finally it gets access or denial to the resources. In the java.security.AccessController class, the checkPermission method is used to determine whether the access to the required resource is granted or denied. If a requested access is granted, the checkPermission method returns true; otherwise, the method throws an AccessControlException.
For example, to check read and write permission for a directory in the file system, you would use the code shown in Example 3-6.
Example 3-6. Using AccessController
try { AccessController.checkPermission (new FilePermission("/var/temp/*", "read,write")); } catch (SecurityException e) { // Does not have permission to access the directory }
Codebase: A URL location of class or JAR files are specified using codebase. The URL may refer to a location of a directory in the local file system or on the Internet. Example 3-7 retrieves all the permissions granted to a particular class that's been loaded from a code base. The permissions are effective only if the security manager is installed. The loaded class uses those permissions by executing Class.getProtectionDomain() and Policy.getPermissions().
Example 3-7. Using codebase class
URL codebase = null; try { // Get permissions for a URL codebase = new url("https://coresecuritypatterns.com/"); } catch (MalformedURLException e) { } catch (IOException e) { } // Construct a code source with the code base CodeSource cs = new CodeSource(codebase, null); // Get all granted permissions PermissionCollection pcoll = Policy.getPolicy().getPermissions(cs); // View each permission in the permission collection Enumeration enum = pcoll.elements(); for (; enum.hasMoreElements(); ) { Permission p = (Permission)enum.nextElement(); System.out.println("Permission " + p); }
To test Example 3-7, Example 3-8 is the policy file (test.policy), which provides permission to read all system properties.
Example 3-8. Policy file for testing permissions to a codebase
grant codebase "http://coresecuritypatterns.com/-" { // Give permission to read all system properties permission java.util.PropertyPermission "*", "read"; };
To ignore the default policies in the java.security file, and only use the specified policy, use '==' instead of '='. With the policy just presented, you may run the following:
java -Djava.security.policy==test.policy TestClass
CodeSource: The CodeSource allows representation of a URL from which a class was loaded and the certificate keys that were used to sign that class. It provides the same notion as codebase, but it encapsulates the codebase (URL) of the code where it is loaded and also the certificate keys that were used to verify the signed code. The CodeSource class and its two arguments to specify the code location and its associated certificate keys are as follows:
CodeSource(URL url, java.security.cert.Certificate certs[]);
To construct a code source with the code base and without using certificates, you would use the following:
CodeSource cs = new CodeSource(codebase, null);
Bytecode verifier: The Java bytecode verifier is an integral part of the JVM that plays the important role of verifying the code prior to execution. It ensures that the code was produced consistent with specifications by a trustworthy compiler, confirms the format of the class file, and proves that the series of Java byte codes are legal. With bytecode verification, the code is proved to be internally consistent following many of the rules and constraints defined by the Java language compiler. The bytecode verifier may also detect inconsistencies related to certain cases of array bound-checking and object-casting through runtime enforcement.
To manually control the level of bytecode verification, the options to the Java command with the V1.2 JRE are as follows:
- -Xverify:remote runs verification process on classes loaded over network (default)
- -Xverify:all verifies all classes loaded
- -Xverify:none does no verification
ClassLoader: The ClassLoader plays a distinct role in Java security, because it is primarily responsible for loading the Java classes into the JVM and then converting the raw data of a class into an internal data structure representing the class. From a security standpoint, class loaders can be used to establish security policies before executing untrusted code, to verify digital signatures, and so on. To enforce security, the class loader coordinates with the security manager and access controller of the JVM to determine the security policies of a Java application. The class loader further enforces security by defining the namespace separation between classes that are loaded from different locations, including networks. This ensures that classes loaded from multiple hosts will not communicate within the same JVM space, thus making it impossible for untrusted code to get information from trusted code. The class loader finds out the Java application's access privileges using the security manager, which applies the required security policy based on the requesting context of the caller application.
With the Java 2 platform, all Java applications have the capability of loading bootstrap classes, system classes, and application classes initially using an internal class loader (also referred to as primordial class loader). The primordial class loader uses a special class loader SecureClassLoader to protect the JVM from loading malicious classes. This java.security.SecureClassLoader class has a protected constructor that associates a loaded class to a protection domain. The SecureClassLoader also makes use of permissions set for the codebase. For instance, URLClassLoader is a subclass of the SecureClassLoader. URLClassLoader allows loading a class or location specified with a URL.
Refer to Example 3-9, which shows how a URLClassLoader can be used to load classes from a directory.
Example 3-9. Using URLClassLoader
// Create a File object on the root of the // directory containing the class file File file = new File("c:\\myclasses\\"); try { // Convert File to a URL URL url = file.toURL(); URL[] urls = new URL[]{url}; // Create a new class loader with the directory ClassLoader myclassloader = new URLClassLoader(urls); // Load in the class; // MyClass.class should be located in // the directory file:/c:/myclasses/com/security Class myclass = myclassloader.loadClass("com.security.MySecureClass"); } catch (MalformedURLException e) { } catch (ClassNotFoundException e) { }
Keystore and Keytool: The Java 2 platform provides a password-protected database facility for storing trusted certificate entries and key entries. The keytool allows the users to create, manage, and administer their own public/private key pairs and associated certificates that are intended for use in authentication services and in representing digital signatures.
We will take a look in greater detail at the usage of the Java keystore and keytool and how these tools help Java security in the section entitled "Java Security Management Tools," later in this chapter.
Java Applet Security
A Java applet downloaded from the Web runs in either a Java-enabled Web browser or a Java appletviewer, which is provided in the J2SE bundle. From a security standpoint, Java applets downloaded from the Internet or from any remote sources are restricted from reading and writing files and making network connections on client host systems. They are also restricted from starting other programs, loading libraries, or making native calls on the client host system. In general, applets downloaded from a network or remote sources are considered untrusted. An applet can be considered trusted, based on the following factors:
- Applets installed on a local filesystem or executed on a localhost.
- Signed applets provide a way to verify that the applet is downloaded from a reliable source and can be trusted to run with the permissions granted in the policy file.
In a Web browser, a Java plug-in provides a common framework and enables secure deployment of applets in the browser using the JRE. While downloading an applet, the Java plug-in enables the browser to install all the class files and then render the applet. A security manager (SecurityManager implementation) will be automatically installed during startup whenever an applet starts running in a Java-enabled Web browser. No downloaded applets are allowed to access resources in the client host unless they are explicitly granted permission using an entry in a Java security policy file.
Example 3-10 is source code for an applet named WriteFileApplet that attempts to create and to write to a file named AppletGenrtdFile in the local directory.
Example 3-10. WriteFileApplet.java
import java.awt.*; import java.io.*; import java.lang.*; import java.applet.*; public class WriteFileApplet extends Applet { String myFile = "/tmp/AppletGenrtdFile"; File f = new File(myFile); DataOutputStream dos; public void init() { String osname = System.getProperty("os.name"); if (osname.indexof("Windows") != -1) { myFile="C:" + file.separator + "AppletGenrtdFile"; } } public void paint(Graphics g) { try { dos = new DataOutputStream(new BufferedOutputStream (new FileOutputStream(myFile),128)); dos.writeChars("This is an Applet generated file\n"); dos.flush(); g.drawString("Success: Writing file" + myFile, 10, 10); } catch (SecurityException se) { g.drawString("Write Failed: Security exception: " + se, 10, 10); } catch (IOException ioe) { g.drawString("Write Failed:I/O exception" + ioe, 10, 10); } } }
To run the applet, you need to compile the source code using javac and then you may choose to deploy this applet class along with an HTML page in a Web server. To do so, create an HTML file (see Example 3-11) called WriteFileApplet.html.
Example 3-11. WriteFileApplet.html
<html><head> <title> Core Security Patterns Example: Applet Security</title></head><body> <h1> WriteFileApplet: Writing Files in the Client host </h1> <hr> <APPLET CODE = WriteFileApplet.class WIDTH=400 HEIGHT=40> </APPLET> <hr></body> </html>
To execute this applet using an appletviewer, run the following :
appletviewer http://coresecuritypatterns.com/WriteFileApplet.html
When executing this applet, you should receive the SecurityException in the applet window. This applet shouldn't be able to write the file, because it does not have a security policy with a file permission to write in the user's home directory.
Now, let's use the following policy file WriteAppletPolicy, which grants a write permission. To do so, create a policy file (see Example 3-12) called WriteAppletPolicy.policy in the working directory:
Example 3-12. WriteAppletPolicy.policy
grant { permission java.io.FilePermission "<<ALL FILES>>","write"; };
To test the applet using an appletviewer, you may choose to use the -J-Djava.security.policy=WriteAppletPolicy.policy option on the JVM command line, or you can explicitly specify your policy file in the JVM security properties file in the <JAVA_HOME>/jre/lib/security directory:
policy.url.3=file:/export/xyz/WriteAppletpolicy.policy
Example 3-13 shows running the WriteFileApplet applet with the WriteAppletPolicy policy file from the command-line interface.
Example 3-13. Running appletviewer using a Java security policy
appletviewer -J-Djava.security.policy=WriteAppletPolicy.policy http://coresecuritypatterns.com/WriteFileApplet.html
You should be able to run the WriteFileApplet applet successfully without a SecurityException, and it should also be able to create and write the file AppletGenrtdFile in the client's local directory.
Now let's explore the concept of signed applets.
Signed Applets
The Java 2 platform introduced the notion of signed applets. Signing an applet ensures that an applet's origin and its integrity are guaranteed by a certificate authority (CA) and that it can be trusted to run with the permissions granted in the policy file. The J2SE bundle provides a set of security tools that allows the end users and administrators to sign applets and applications, and also to define local security policy. This is done by attaching a digital signature to the applet that indicates who developed the applet and by specifying a local security policy in a policy file mentioning the required access to local system resources.
The Java 2 platform requires an executable applet class to be packaged into a JAR file before it is signed. The JAR file is signed using the private key of the applet creator. The signature is verified using its public key by the client user of the JAR file. The public key certificate is sent along with the JAR file to any client recipients who will use the applet. The client who receives the certificate uses it to authenticate the signature on the JAR file. To sign the applet, we need to obtain a certificate that is capable of code signing. For all production purposes, you must always obtain a certificate from a CA such as VeriSign, Thawte, or some other CA.
The Java 2 platform introduced new key management tools to facilitate support for creating signed applets:
- The keytool is used to create pairs of public and private keys, to import and display certificate chains, to export certificates, and to generate X.509 v1 self-signed certificates.
- The jarsigner tool is used to sign JAR files and also to verify the authenticity of the signature(s) of signed JAR files.
- The policytool is used to create and modify the security policy configuration files.
Let's take a look at the procedure involved in creating a signed applet using our previous WriteFileApplet applet example. The following steps are involved on the originating host environment responsible for developing and deploying the signed applet:
-
Compile the Applet source code to an executable class. Use the javac command to compile the WritefileApplet.java class. The output from the javac command is the WriteFileApplet.class.
javac WriteFileApplet.java
-
Package the compiled class into a JAR file. Use the jar utility with the cvf option to create a new JAR file with verbose mode (v), and specify the archive file name (f).
jar cvf WriteFileApplet.jar WriteFileApplet.class
-
Generate key pairs. Using the keytool utility, create the key pair and self-signed certificate (for testing purposes only). The JAR file is signed with the creator's private key and the signature is verified by the communicating peer of the JAR file with the public key in the pair.
keytool -genkey -alias signapplet -keystore mykeystore -keypass mykeypass -storepass mystorepass
This keytool -genkey command generates a key pair that is identified by the alias signapplet. Subsequent keytool commands are required to use this alias and the key password (-keypass mykeypass) to access the private key in the generated pair. The generated key pair is stored in a keystore database called mykeystore (-keystore mykeystore) in the current directory and is accessed with the mystorepass password (-storepass mystorepass). The command also prompts the signer to input information about the certificate, such as name, organization, location, and so forth. -
Sign the JAR file. Using the jarsigner utility (see Example 3-14), sign the JAR file and verify the signature on the JAR files.
Example 3-14. Signing an applet using jarsigner tool
jarsigner -keystore mykeystore -storepass mystorepass -keypass mykeypass -signedjar SignedWriteFileApplet.jar WriteFileApplet.jar signapplet
The -storepass mystorepass and -keystore mykeystore options specify the keystore database and password where the private key for signing the JAR file is stored. The -keypass mykeypass option is the password to the private key, SignedWriteFileApplet.jar is the name of the signed JAR file, and signapplet is the alias to the private key. jarsigner extracts the certificate from the keystore and attaches it to the generated signature of the signed JAR file. -
Export the public key certificate. The public key certificate will be sent with the JAR file to the end user who will use it to authenticate the signed applet. To have trusted interactions, the end user must have a copy of those public keys in its keystore. This is accomplished by exporting the public key certificate from the originating JAR signer keystore as a binary certificate file and then importing it into the client's keystore as a trusted certificate.
Using the keytool, export the certificate from mykeystore to a file named mycertificate.cer as follows:
keytool -export -keystore mykeystore -storepass mystorepass -alias signapplet -file mycertificate.cer
-
Deploy the JAR and certificate files. They should be deployed to a distribution directory on a Web server. Additionally, create a Web page embedding the applet and the JAR. As shown in Example 3-15, the applet tag must use the following syntax.
Example 3-15. Deploying a signedapplet Jar
<applet code=WriteFileApplet.class archive="SignedWriteFileApplet.jar" codebase="/export/home/ws/" width=400 height=40> </applet>
In addition to the previous steps, the following steps are involved in the client's environment:
-
Import certificate as a trusted certificate. To download and execute the signed applet, you must import the trusted public key certificate (provided by the issuer) into a keystore database. The Java runtime will use this client-side keystore to store its trusted certificates and to authenticate the signed applet. Using the Keytool utility, import the trusted certificate provided by the issuer (see Example 3-16).
Example 3-16. Importing a certificate
keytool -import -alias clientcer –file mycertificate.cer -keystore clientstore -storepass clientpass
-
Create the policy file. Create a policy file client.policy, which grants the applet to have permission for creating and writing to the file "AppletGenrtdFile" in the client's local directory (see Example 3-17).
Example 3-17. Sample policy file (Client.policy)
keystore "/export/home/clientstore"; grant SignedBy "clientcer" { permission java.io.FilePermission "<<ALL FILES>>", "write"; };
-
Run and test the applet using appletviewer. The appletviewer tool runs the HTML document specified in the URL, which displays the applet in its own window. To run the applet using the client policy file, enter the following at the command line (see Example 3-18).
Example 3-18. Testing the applet using the client policy
appletviewer -J-Djava.security.policy=client.policy http://coresecuritypatterns.com/SignedWriteFileApplet.html
Java Web Start Security
Java Web Start (JWS) is a full-fledged Java application that allows Java client applications to be deployed, launched, and updated from a Web server. It provides a mechanism for application distribution through a Web server and facilitates Java rich-client access to applications over a network. The underlying technology of JWS is the Java Network Launch protocol (JNLP), which provides a standard way for packaging and provisioning the Java programs (as JAR files) and then launching Java programs over a network. The JNLP-packaged applications are typically started from a Web browser that launches the client-side JWS software, which downloads, caches, and then executes the application locally. Once the application is downloaded, it does not need to be downloaded again unless newer updates are made available in the server. These updates are done automatically in an incremental fashion during the client application startup. Applications launched using JWS are typically cached on the user's machine and can also be run offline. Since the release of J2SE 1.4, JWS has been an integral part of the J2SE bundle, and it does not require a separate download [JWS].
JWS Security Model
Typical to a stand-alone Java application, JWS applications run outside a Web browser using the sandbox features of the underlying Java platform. JWS also allows defining security attributes for client-side Java applications and their access to local resources, such as file system access, making network connections, and so on. These security attributes are specified using XML tags in the JNLP descriptor file. The JNLP descriptor defines the application access privileges to the local and network resources. In addition, JWS allows the use of digital signatures for signing JAR files in order to verify the application origin and its integrity so that it can be trusted before it is downloaded to a client machine. The certificate used to sign the JAR files is verified using the trusted certificates in the client keystore. This helps users avoid starting malicious applications and inadvertent downloads without knowing the originating source of the application.
When downloading signed JARs, JWS displays a dialog box that mentions the source of the application and the signer's information before the application is executed. This allows users to make decisions regarding whether to grant additional privileges to the application or not. When downloading unsigned applications (unsigned JARs) that require access to local resources, JWS throws a "Security Advisory" dialog box notifying the user that an application requires access to the local resources and prompts the user with a question "Do you want to allow this action?" JWS will allow the user to grant the client application access to the local resources by clicking the "Yes" button in the Security Advisory dialog box.
Signing a JWS application is quite similar to the steps involved in signing an applet, as we saw in the previous section. To sign a JWS application for production, you must obtain a certificate from a certificate authority such as VeriSign and Thawte. For testing purposes, you may choose to use the key management tools provided with the J2SE bundle.
JNLP Settings for Security
To deploy a JWS application, in addition to JAR files, adding a .jnlp file is required. The JNLP file is an XML-based document that describes the application classes (JAR files), their location in a Web server, JRE version, and how to launch in the client environment. The client user downloads the JNLP file from the server, which automatically launches the JWS application on the client side. The JNLP file uses XML elements to describe a JWS application. The root element is tagged as <jnlp>, which contains the four core sub-elements: information, security, resources, and application-desc.
To enforce security, the <security> element is used to specify the required permissions. The security element provides two permission options: <all-permissions/> to provide an application with full access to the client's local computing resources, and <j2ee-application-client-permissions/> to provide a selected set of permissions that includes socket permissions, clipboard access permission, printing permission, and so forth. Example 3-19 is a JNLP file that shows putting all the elements including a <security> element setting with all permissions.
Example 3-19. JNLP file showing <security> elements
<?xml version="1.0" encoding="UTF-8"?> <jnlp spec="1.0+" codebase="file:///c:/testarea/jnlp/"> <information> <title>My Signed Jar</title> <vendor>Core Security Patterns</vendor> <homepage href="http://www.sec-patterns.com/signed" /> <description>Java Web start example</description> </information> <offline-allowed/> <security> <all-permission/> </security> <resources> <j2se version="1.2+" /> <jar href="SignedClientApp.jar"/> </resources> <application-desc main-class="SignedClientApp" /> </jnlp>
Java Security Management Tools
As part of the J2SE bundle, the Java 2 platform provides a set of tools that helps Java developers and security administrators to administer security policies, create keys (for testing purposes only), manage keys and certificates, sign JAR files, verify signatures, and support other functions related to key management.
The following tools and utilities are provided as part of the J2SE bundle.
Java Keystore
The keystore is a protected database that stores keys and trusted certificate entries for those keys. A keystore stores all the certificate information related to verifying and proving an identity of a person or an application. It contains a private key and a chain of certificates that allows establishing authentication with corresponding public keys. Each entry in the keystore is identified by a unique alias. All stored key entries can also be protected using a password. The Java keystore follows the RSA cryptographic standard known as PKCS#12, which provides a way to securely store multiple keys and certificates in a password-protected file. Unless specified by default, the key entries are stored in a .keystore file and the trusted CA certificate entries are stored in a cacerts file, which resides in the JRE security directory.
Keytool
Keytool is a key and certificate management tool that allows users and administrators to administer their own private/public key pairs and associated certificates. This tool is intended for use with authentication services and verifying data integrity using digital signatures. The keytool is provided with the J2SE bundle as a command-line utility, which can be used to create JKS (Java keystore) and JCEKS (Java Cryptographic Extensions Keystore) keystores, generate and store keys and their associated X.509v1 certificates, generate Certificate Signing Requests (CSR), import and store trusted certificates, and perform maintenance on keystore entries.
The keytool utility uses the X.509 certificate standard, which is encoded using the Abstract Syntax Notation 1 (ASN.1) standard to describe data and the Definite Encoding Rules (DER) standard to identify how the information is to be stored and transmitted. The X.509 certificate takes the values of subject and issuer fields from the X.500 Distinguished Name (DN) standard.
Let's take a look at the most common operations performed using the keytool utility:
Creating a keystore database. A keystore is created whenever you use keytool with options to add entries to a non-existent keystore. The following options automatically create a keystore when the specified keystore does not exist in the user's directory:
- -genkey option is used to generate private/public key pairs.
- -import option is used to import a trusted certificate.
- -identitydb is used to import data from a legacy JDK 1.1.
By default, the keytool creates a keystore as a file named .keystore in the user's home directory, but a name can be specified using the –keystore option.
Generating private/public key pairs. When a keytool is used to generate a private/public key pair, each entry contains a private key and an associated certificate "chain." The first certificate in the chain contains the public key corresponding to the private key.
A pair of public and private keys can be generated and added to the keystore using the keytool -genkey command. The -genkey option creates a public/private key pair and then wraps the public key in a self-signed certificate. The following example will generate a key pair wrapped in a X.509 self-signed certificate and stored in a single-element certificate chain. In this command, we also need to specify passwords for the keys and the keystore, the algorithm to use (RSA), and the alias (see Example 3-20).
Example 3-20. Generating key pairs using keytool
keytool -genkey -alias myalias -keyalg RSA -keypass mykeypass -keystore mykeystore -storepass mystorepass
In the command shown in Example 3-20, the -genkey option is used to generate the key pair, and all other options are used in support of this command. The key pair is identified with an alias myalias with a password of mykeypass. Both the alias and the keypass are required for all the subsequent commands and operations when we access the particular key pair in the keystore. The other options that can be used are as follows:
- -keyalg -- specifies the encryption algorithm used for the key (example: RSA). An additional "–keysize" option would allow us to specify the bit size for the key; if not specified, the keytool uses the default value of 1024 bits.
- -keypass -- specifies the password for the key generated.
- -keystore -- specifies the name of the keystore to store the keys, which is a binary file. If it is not specified, a new file will be created and saved as a .keystore file.
- -storepass -- specifies the password used to control access to the keystore. After keystore creation, all modifications to the keystore will require you to use the password whenever accessing the keystore.
When you execute these command and options, you will also be prompted with questions to supply the following names for creating subcomponents of the X.500 Distinguished Name standard:
CN - First and Last name OU - Organizational unit O - Organization L - City or Locality ST - State or Province C - Country code
Example 3-21 is a sample output generated using these command and options.
Example 3-21. Generating keypairs using Keytool
$ keytool -genkey -alias myalias -keyalg RSA -keystore mykeystore -keypass mykeypass -storepass mystorepass What is your first and last name? [Unknown]: Roger R What is the name of your organizational unit? [Unknown]: Java developer What is the name of your organization? [Unknown]: Java Day care What is the name of your City or Locality? [Unknown]: Boston What is the name of your State or Province? [Unknown]: Massachusetts What is the two-letter country code for this unit? [Unknown]: US Is CN=Roger R, OU=Java developer, O=Java Day care, L=Boston, ST=Massachusetts, C=US correct? [no]: yes
With all the questions answered, the keytool generates the keys and the certificate and stores them in the specified keystore file.
Listing the entries of a keystore. The keytool with the -list option is used to list all the entries of a keystore, and also to look at the contents of an entry associated with an alias. Example 3-22 is a command that lists all the entries of a keystore named mykeystore. This command also requires us to enter the keystore password.
Example 3-22. Using keytool to list entries of a Java keystore
$ keytool -list -keystore mykeystore Enter keystore password: mystorepass Keystore type: jks Keystore provider: SUN Your keystore contains 1 entry myalias, Sep 5, 2003, keyEntry, Certificate fingerprint (MD5): 68:A2:CA:0C:D5:C6:D2:96:D5:DC:EA:8D:E3:A1:AB:9B
To display the contents of a keystore entry when identified with an alias, the list command prints the MD5 fingerprint of a certificate. If the -v option is specified, the certificate is printed in a human-readable format. If the -rfc option is specified, the certificate is output in the Base 64 encoding format. The following command (see Example 3-23) lists the contents of a keystore entry in a human-readable format for alias "myalias."
Example 3-23. Using keytool to list contents of a Java keystore
$ keytool -list -alias myalias -keystore mykeystore -v Enter keystore password: mystorepass Alias name: myalias Creation date: Sep 5, 2003 Entry type: keyEntry Certificate chain length: 1 Certificate[1]: Owner: CN=Roger R, OU=Java developer, O=Java Day care, L=Boston, ST=MA, C=US Issuer: CN=Roger R, OU=Java developer, O=Java Day care, L=Boston, ST=MA, C=US Serial number: 3f58edda Valid from: Fri Sep 05 16:11:06 EDT 2003 until: Thu Dec 04 15:11:06 EST 2003 Certificate fingerprints: MD5: 68:A2:CA:0C:D5:C6:D2:96:D5:DC:EA:8D:E3:A1:AB:9B SHA1: 2E:E1:36:ED:D0:E8:EF:85:E5:6B:92:AD:9D:AE:28:82:25:8C:CC:9F
Exporting a certificate entry from a keystore. To have trusted interactions, the communicating client peer needs to have a copy of the public keys from the original signer in the keystore. This is done by exporting the certificate (containing the public key and signer information) to a binary certificate file and then importing them as a trusted certificate into the client peer's keystore.
To export the certificate to a binary certificate file (see Example 3-24), the keytool -export and -file options are used. The following command exports a certificate entry identified with alias myalias in keystore mykeystore to a file mycertificate.cer. This command requires entering the keystore password.
Example 3-24. Exporting a certificate from a Java keystore
$ keytool -export -alias myalias -file mycertificate.cer -keystore mykeystore Enter keystore password: mystorepass Certificate stored in file <mycertificate.cer>
Importing a trusted certificate. The keytool -import option is used to import a trusted certificate into a keystore database and to associate it with a unique alias. This is executed in the environment of a client who wishes to trust this certificate and to have trusted client interactions with that communicating peer.
When a new certificate is imported into the keystore, the keytool utility verifies that the certificate has integrity and authenticity. The keytool utility attempts this verification by building a chain of trust from that certificate to the self-signed certificate that belongs to the issuer. The lists of trusted certificates are stored in the cacerts file.
To execute the import in a keystore (see Example 3-25), you need to provide the certificate entry with a unique alias and key password. For example, the following command imports a certificate entry from a file mycertificate.cer and identifies the entry with myclientalias and key password clientkeypass in keystore clientkeystore with keystore password clientpass. As a last step, the command displays the owner and issuer information of the certificate and prompts the user to trust the certificate:
Example 3-25. Importing a trusted certificate to a Java keystore
$ keytool -import -alias myclientalias -file mycertificate.cer -keypass clientkeypass -keystore clientstore -storepass clientpass Owner: CN=Roger R, OU=Java developer, O=Java Day care, L=Boston, ST=MA, C=US Issuer: CN=Roger R, OU=Java developer, O=Java Day care, L=Boston, ST=MA, C=US Serial number: 3f58edda Valid from: Fri Sep 05 16:11:06 EDT 2003 until: Thu Dec 04 15:11:06 EST 2003 Certificate fingerprints: MD5: 68:A2:CA:0C:D5:C6:D2:96:D5:DC:EA:8D:E3:A1:AB:9B SHA1: 2E:E1:36:ED:D0:E8:EF:85:E5:6B:92:AD:9D:AE:28:82:25:8C:CC:9F Trust this certificate? [no]: yes Certificate was added to keystore
Printing certificate information. The keytool –printcert option is used to display the contents of a certificate that has been exported from a keystore and made available as a file. To execute this command, no associated keystore database or password is required, because the certificate contains the information as a certificate file (.cer), which is not imported into the keystore.
You would use Example 3-26 to display the contents of a binary certificate file.
Example 3-26. Displaying contents of a certificate
$ keytool -printcert -file mycertificate.cer Owner: CN=Roger R, OU=Java developer, O=Java Day care, L=Boston, ST=MA, C=US Issuer: CN=Roger R, OU=Java developer, O=Java Day care, L=Boston, ST=MA, C=US Serial number: 3f58edda Valid from: Fri Sep 05 16:11:06 EDT 2003 until: Thu Dec 04 15:11:06 EST 2003 Certificate fingerprints: MD5: 68:A2:CA:0C:D5:C6:D2:96:D5:DC:EA:8D:E3:A1:AB:9B SHA1: 2E:E1:36:ED:D0:E8:EF:85:E5:6B:92:AD:9D:AE:28:82:25:8C:CC:9F
Creating a Certificate Signing Request (CSR). The Keytool -certreq option allows you to generate a certificate authentication request for a certificate from a Certificate Authority (CA). The -certreq option (see Example 3-27) creates a CSR for the certificate and places the CSR in a file named certreq_file.csr, where certreq_file.csr is the name of the file that is to be sent to the CA for authentication. If a CA considers the certificate to be valid, it issues a certificate reply and places the reply in a file named cert_reply.cer, where cert_reply.cer is the file returned by the CA that holds the results of the CSR authorizations that were submitted in the certreq_file.csr file.
Example 3-27. Creating a CSR
keytool -certReq -keystore mykeystore -file myCSR.csr -alias mycsralias
Deleting a keystore. To delete a keystore, use an operating system delete command to delete the keystore files.
Changing password in the keystore. To change the keystore password, use keytool -storepassword -new options to set a new password (see Example 3-28).
Example 3-28. Changing keystore password
keytool -storepasswd -new newstorepass -keystore mykeystore -storepass mystorepass
Example 3-28 shows how the password for the keystore mykeystore is changed from mystorepass to newstorepass.
Smart Cards and Cryptographic Devices Based Keystores
With the release of J2SE 5.0 [J2SE5], Java provides support for using smart cards and cryptographic devices as keystores. J2SE 5.0 introduced the support for RSA Cryptographic Token Interface Standard (referred to as PKCS#11) which defines native programming interfaces to cryptographic tokens, such as hardware cryptographic accelerators and smart cards. Integrating smart card based keystores is accomplished by configuring the smart card PKCS#11 module as a security provider. To facilitate the integration using PKCS#11 interfaces, J2SE 5.0 provides a new cryptographic provider (SunPKCS11). The SunPKCS11 provider enables existing applications written to the JCA and JCE APIs to access native PKCS#11-based smart cards and cryptographic devices. To enable this, the JRE must be configured with the PKCS#11 provider in the java.security file located at $JAVA_HOME/jre/lib/security/java.security.
For a detailed example about how to configure the smart card as Java keystore, refer to the section "Using Smart Cards as Keystores" in Chapter 4, "Java Extensible Security Architecture and APIs."
Policytool
The policytool is a utility that provides a menu-driven user-friendly interface for creating and viewing Java security policy configuration files. The policytool menu options enable you to read and edit policy files by adding policy and permission entries, assigning a keystore, and creating a new policy configuration file.
To start the Policy Tool utility, simply type the following at the command line:
policytool
For more information about using policytool menu options, refer to the policytool documentation provided with the J2SE bundle.
Jarsigner
The jarsigner tool is used to digitally sign the Java archives (JAR files) and to verify the signature and its integrity. The jarsigner can sign and verify only the JAR file created by the JAR utility provided with the J2SE bundle.
Signing a JAR file
The jarsigner uses the private key information from the signer's keystore to generate digital signatures for signing the JAR files. After signing, the signed JAR file includes a copy of the public key from the keystore that corresponds to the private key used to sign the file. The jarsigner can also verify the signer's information in the signed JAR file.
Example 3-29 shows what you would use to sign a JAR file named myJar.jar and then name the signed JAR file as mySignedJar.jar.
Example 3-29. Using jarsigner to sign a jar file
jarsigner -keystore /home/nr/mykeystore -storepass mykeystorepass -keypass mykeypass -signedjar mySignedJar.jar myJar.jar myPrivateKeyalias
Verifying a Signed JAR
To verify a signed JAR file, and to verify that the signature is valid and the JAR file has not been tampered with, you would use the command shown in Example 3-30.
Example 3-30. Using jarsigner to verify signature a signed jar
jarsigner –keystore /home/nr/mykeystore -verify –certs mySignedJar.jar
The J2SE environment also provides support for cryptographic services, secure communication services using SSL and TLS protocols, and certificate management services. The J2SE 5.0 is the newest release of the Java platform. It includes numerous feature and security updates to the Java language and the JVM. J2SE 5.0 also offers significant security enhancements compared to the previous release of J2SE 1.4.x. These will be discussed in Chapter 4, "Java Extensible Security Architecture and APIs."
J2ME Security Architecture
The Java 2 Micro Edition (J2ME) is designed to deliver the benefits of Java technology on microdevices and embedded systems with limited constraints on their resources, such as memory size, display size, processing power, network bandwidth, and battery life. The J2ME platform provides the runtime environment and API technologies to support a broad range of consumer electronics, embedded devices, and personal mobile devices such as cellular phones, personal digital assistants (PDAs), TV set-top boxes, telematic systems, electronic appliances, and so forth. J2ME offers a rich graphical user interface, storage, networking, security, and other capabilities that include browsing Web applications and running mobile gaming applications. J2ME is also designed to produce portable code and to establish portability of applications across device groups such as PDAs and smartphones. With its security and portability benefits, today J2ME is widely adopted as a core platform for building customized services on a variety of devices and for running mobile applications.
In general, J2ME is a slimmed-down version of J2SE. To enable J2ME applications to meet the device resource constraints and to run efficiently, many API components have been removed from the core Java platform. Figure 3-3 illustrates the J2ME platform architecture and its elements.
Figure 3-3 J2ME platform architecture and core elements
J2ME defines the notion of configurations and profiles to represent the characteristics of supported devices. These configurations and profiles are developed by the industry groups participating in the Java community process.
J2ME Configurations
A J2ME configuration defines Java runtime and API technologies that satisfy the needs of a broad range of devices. Configurations are defined based on the device limitations and the characteristics of memory, display, processing power, network connectivity, and so forth.
The current J2ME specification defines two types of configurations: Connected Device Configuration (CDC) and Connected Limited Device Configuration (CLDC).
CDC
CDC targets high-end consumer devices with TCP/IP network connectivity and higher bandwidth. It requires at least 2Mb memory available for the Java platform. It defines a full-featured JVM that includes all the functionality of a Java runtime environment residing on a standard desktop system. The low-level interfaces for calling native code (JNI), connecting to debuggers (JVMDI), and profiling code (JVMPI) are optional. Vendors may adopt them based on device requirements.
Figure 3-4 J2ME platform configuration and profiles
CDC provides a full-featured Java security model and associated mechanisms of a J2SE environment:
- All code runs in a sandbox without exposing the user's system to risk. All classes are loaded with full byte-code verification and Java language features.
- Signed classes verify the integrity and originating source of the Java classes when the JVM attempts to load it.
- Security policy provides fine-grained access control over the resources using a user-defined set of permissions and policies.
- Support for Java cryptography to secure programs, data, communication, and retrieval is provided.
In short, CDC offers all the security benefits leveraging a standard J2SE environment and gives architects and developers the flexibility to use different Java security API capabilities for building secure applications. J2ME runtime implementations built on the CDC may utilize the standard JVM bundled with the J2SE or the Compact Virtual Machine (CVM) depending on the size of the device for which the implementation is being developed.
CLDC
CLDC targets low-end consumer devices, with only 128–512 kilobytes of memory required for the Java platform and running applications. It features a subset of a standard JVM with limited API and supporting libraries.
When compared to the J2SE implementation, J2ME differs as follows:
- Limited security model
- New class verification mechanism
- No user-defined class loaders
- No support for thread groups or daemon threads
- No support for weak references
- Limited error handling
- No finalization
- No reflection support
- New connection framework for networking
CLDC runs on top of Sun's K Virtual Machine (KVM), which is a JVM designed specifically for supporting resource-limited devices. KVM is the core component of the J2ME platform for CLDC devices. CLDC defines two levels of security: Low-level KVM security and application-level security.
Low-level KVM security: An application running in the KVM must not be able to harm the device in any way. Such security is guaranteed by a pre-verification process that rejects invalid class files and ensures that a class does not contain any references to invalid memory locations. The preverify tool is responsible for the verification process, and it inserts some special attributes into the Java class file. After pre-verification, the KVM does an in-device verification process, which ensures that the class is pre-verified. Figure 3-5 illustrates a CLDC verification process.
Figure 3-5 Pre-verification process–KVM low-level security
Application-level security: The KVM defines a sandbox model that is quite different from the J2SE sandbox model. The sandbox requires that all Java classes are verified and guaranteed to be valid Java applications. It limits all but a predefined set of APIs from becoming available to the application as required by the CLDC specifications and supporting profiles. The downloading and management of applications take place at the native code level, and application programmers cannot define their own classloader or override the classloader or system classes and associated packages of the KVM. Application programmers also cannot download or add any native libraries that contain code and functionality that are not part of the CLDC supported libraries.
J2ME Profiles
J2ME profiles define a broader set of Java API technologies that are suited to a specific device class or a targeted class of devices. Profiles are built on top of J2ME configurations and define API libraries that enable specific types of applications that are suitable for the target devices. Each J2ME configuration supports one or more J2ME profiles. For the smallest of devices, the Mobile Information Device Profile (MIDP) is one such profile that is built on top of the CLDC configuration. The other class of devices based on CDC configuration use the Foundation Profile. The Foundation profile and its API libraries are based on J2SE 1.3.
Understanding MIDP and MIDlets
MIDP is designed for the family of limited computing devices that have wireless network connectivity and mechanisms for user input and display. The MIDP combined with the CLDC provides the execution environment and application functionality, which includes the user interface, application management, network connectivity, local data storage, and application life-cycle management. The CLDC and MIDP are packaged as a standardized J2ME runtime environment along with the API libraries and are deployed on the native operating system of the device. The OEM-specific classes are provided by the device manufacturer.
A MIDlet is a J2ME application designed to run on a mobile device. In simple terms, a MIDlet is a Java applet, except that it runs on a mobile device rather than in a Web browser. The mobile device provides an Application Management utility that is responsible for installing, executing, and removing MIDlet suites. The Java 2 Micro Edition Wireless Toolkit (J2ME WTK) provides a MIDlet development environment for creating and testing MIDP applications. Aside from the basics of building and packaging MIDlet suites, it includes tools for setting MIDlet security permission attributes, cryptographically signing MIDlet suites, and working with MIDP protection domains. The J2ME toolkit download is available for free from http://java.sun.com/j2me.
A MIDlet suite consists of one or more MIDlets packaged together as a JAR file. A packaged MIDlet will generally consist of compiled and pre-verified Java classes and other files, including images and application-related data. In addition to those files, a manifest file (manifest.mf) is also stored as part of the JAR file. The manifest file stores the MIDlet name, version, and MIDlet vendor-specific attributes. When running the JAR utility, it is important to use the -m option along with the manifest file in the arguments. Example 3-31 shows a manifest file.
Example 3-31. MIDlet JAR–manifest file
MIDlet-1: SecMidlet, /icons/csp.png, com.csp.SecMidlet MMIDlet-Description: Example Manifest file illustration MIDlet-Name: Example Secure Midlet MIDlet-Permissions: MIDlet-Vendor: Coresecuritypatterns.com MIDlet-Version: 2.0 MicroEdition-Configuration: CLDC-1.0 MicroEdition-Profile: MIDP-2.0
In addition to a JAR file, a Java Application Descriptor (JAD) file is also required to be available as part of the MIDlet suite to provide information about the MIDlet(s) bundled in the JAR file. The JAD file provides information to the mobile device's application manager about the contents of the JAR file and also provides a way to pass parameters required by the MIDlet(s). The application manager requires that the JAD file have an extension of .jad. Example 3-32 shows an example of a JAD file.
Example 3-32. JAD file
MIDlet-1: SecMidlet,/icons/csp.png, com.csp.SecureMidlet MMIDlet-Description: Example Manifest file illustration MIDlet-Jar-URL: SecureMidlet.jar MIDlet-Name: Example Secure Midlet MIDlet-Permissions: MIDlet-Vendor: Coresecuritypatterns.com MIDlet-Version: 2.0 MicroEdition-Configuration: CLDC-1.0 MicroEdition-Profile: MIDP-2.0 MIDlet-Jar-Size: 19250
Use the ktoolbar GUI tool provided with the J2ME WTK to load the JAD file and the JAR file containing the MIDlet suite from a local filesystem.
MIDlet Security
The MIDP 1.0 specification introduced the basic security feature that restricts all MIDlet suites to operate within a sandbox-based security model. This was primarily done to ensure that MIDlets prevent access to sensitive APIs and functions of devices, and to avoid any risks to the device resources.
The MIDP 2.0 introduced the notion of trusted MIDlets to provide a flexible and consistent security model with access-control mechanisms defined by a domain policy. The device enforces access control on a MIDlet suite as trusted MIDlets in accordance with the defined domain policy. A MIDlet suite is identified as untrusted when the origin and integrity of the MIDlet suite's JAR file cannot be verified and trusted by the device. The MIDP 2.0 also specifies how MIDlet suites can be cryptographically signed so that their authenticity and originating source can be validated.
Trusted MIDlets
The MIDP defines a security model for trusted MIDlets based on a notion referred to as Protection Domains. Each protection domain associates a MIDlet with a set of permissions and related interaction modes, which allows a MIDlet to access the domain based on the permissions granted. A protection domain contains allowed and user permissions.
- The allowed permissions define a set of actions that should be allowed without any user interaction.
- The user permissions define a set of permissions that require explicit user approval. The MIDlet is bound by the protection domain and will allow or deny access to functions after prompting the user and obtaining their permissions.
In the case of user permissions, a MIDlet needs permission at the first time of access and asks the user whether the permission should be granted or denied. The user permission is defined to grant allow or deny permissions to specific API functions with the following three interaction modes.
- Blanket: The MIDlet is valid for every invocation until its permission is revoked by the user or the MIDlet is deleted from the device.
- Session: The MIDlet is valid for every invocation of the MIDlet suite until it terminates. It prompts the user for every initiated session on or before its first invocation.
- Oneshot: The MIDlet is valid for a single invocation of a restricted method and prompts the user on each such invocation.
All user permission has a default interaction mode with an optional set of available interaction modes. These interaction modes are determined by the security policy. A policy consists of the definitions of domains and aliases. Each domain consists of the definition of granted permissions and user permissions. Aliases permit groups of named permissions to be reused in more than one domain and help keep the policy compact. Aliases may only be defined and used within a single policy file.
A domain is defined with a domain identifier and a sequence of permissions. The domain identifier is implementation-specific. Each permission line begins with allowing or denying user permissions and indicates the interaction modes, such as blanket, session, and oneshot for the specified list of permissions that follow. Example 3-33 shows a policy file.
Example 3-33. MIDlet policy file
domain: O="MIDlet Underwriters, Inc.", C=US allow: javax.microedition.io.HttpConnection oneshot(oneshot): javax.microedition.io.CommConnection alias: client_connections javax.microedition.io.SocketConnection, javax.microedition.io.SecureConnection, javax.microedition.io.HttpConnection, javax.microedition.io.HttpsConnection domain: O=Acme Wireless, OU=Software Assurance allow: client_connections allow: javax.microedition.io.ServerSocketConnection, javax.microedition.io.UDPDatagramConnection oneshot(oneshot): javax.microedition.io.CommConnection domain: allnet blanket(session): client_connections oneshot: javax.microedition.io.CommConnection
To request permissions for executing a MIDlet suite, we will make use of attributes such as MIDlet-Permissions and MIDlet-Permissions-opt in a JAD descriptor file that signals a MIDlet suite's dependence on requiring certain permissions. These special JAD attributes represent the MIDlet suite and provide a device at installation time with access control information about which particular operations the MIDlet suite will be attempting. For example, suppose the MIDlet suite will attempt to make an HTTP connection and optionally make socket connections. The attributes in the JAD descriptor file would look like this:
MIDlet-Permissions: javax.microedition.io.Connector.http MIDlet-Permissions-opt: javax.microedition.io.Connector.socket
If a device attempts to install a MIDlet suite into a protection domain that doesn't allow the required permissions specified in the MIDlet suite's JAD file, the installation fails automatically. Thus, the trusted MIDlet provides the mechanisms that protect the device from poorly or maliciously written Java code that can render a device inoperable.
Signed MIDlet Suite
Since MIDP 2.0, a MIDlet suite can be cryptographically signed and its authenticity and originating source can be validated as trusted. This is an enhanced security model for MIDP applications, which is handled via digital signatures and PKI support. As with any PKI authentication, it requires zero or more root CA certificates that are used to verify other certificates. Signing a MIDlet suite is quite similar to signing applets that involve a signer and public key certificates. The signer of the MIDlet is responsible for distributing and supporting the MIDlets. The signer needs to have a public key certificate that can be validated by one of the protection domain root certificates on the device. A signed MIDlet suite usually includes a certificate issued by a well-known certificate authority. It is important to note that signing a MIDlet suite isn't useful unless client devices can verify the signature.
From a developer standpoint, signing a MIDlet suite is the process of adding the signer certificates and the digital signature of the JAR file to a JAD file. Signing a MIDlet suite adds new attributes to the JAD file, represented using Base 64 encoded values.
MIDlet-Certificate: <Base64 encoded value of certificate> MIDlet-Jar-RSA-SHA1: <Base64 encoded value of signatures>
The J2ME Wireless Toolkit enables a signer to either sign a MIDlet suite with an existing public and private key pair obtained from a certificate authority or with a new key pair that can be generated as a self-signed certificate for testing purposes only. Each key pair is associated with a certificate. Assigning a security domain to the certificate designates the level of trust that the certificate holder has to access protected APIs and the level of access to those APIs.
The JADTool is a command-line interface provided with the J2ME toolkit for signing MIDlet suites. The JADTool only uses certificates and keys from J2SE keystores, discussed earlier in this chapter. As we discussed previously, the J2SE bundle provides key management tools for managing keystores.
The JADTool utility is packaged in a JAR file. To run it from the command line interface, change your current directory to {j2metoolkit-dir}\bin, and execute the following command:
java –jar JADTool.jar <command-options> <JAD file>
Example 3-34 would add the certificate of the key pair from the given J2SE keystore to the specified JAD file.
Example 3-34. Adding a certificate to a JAD file
java –jar JADTool.jar -addcert -keystore <mykeystore> -storepass <storepassword> -alias <alias> -inputjad <input_jadfile> -outputjad <output_jadfile>
Example 3-35 adds the digital signature of the given JAR file to the specified JAD file.
Example 3-35. Adding a digital signature to a JAD file
java –jar JADTool.jar -addjarsig -jarfile <jarfile> -keystore <mykeystore> -storepass <storepassword> -alias <alias> -inputjad <input_jadfile> -outputjad <output_jadfile>
Example 3-36 displays a list of certificates in a given JAD file.
Example 3-36. Displaying list of certificates for a given JAD file
java –jar JADTool.jar -showcert -inputJAD <input_jadfile>
If you don't wish to use the JADTool command-line utility, the J2ME toolkit also provides a GUI-based utility (Ktoolbar) that enables you to complete the entire signing process without having to use the command-line options. Finally, you may also choose to use the Ktoolbar tool to publish and then directly install the MIDlet suite in the device using Over-The-Air (OTA) provisioning techniques commonly adopted in wireless service deployment. The OTA provisioning mechanisms allow deploying applications wirelessly, the same way they send and receive messages or browse the Internet.
The J2ME platform [J2ME] also provides mechanisms for using cryptographic algorithms and securing network communication using SSL/TLS protocols as well as ensuring source authentication, integrity, and confidentiality. This will be discussed in Chapter 4. "Java Extensible Security Architecture and APIs."
Java Card Security Architecture
The Java Card technology enables smart cards and other devices with limited memory to run Java-based applications. Java Card technology brings a whole set of advantages to smart cards by offering a secure execution environment, platform-independence, the ability to store and update multiple applications, and compatibility with existing smart-card standards. It is also important to note that the Java Card technology [JavaCard] was developed specifically to enhance the security of smart cards.
Understanding Smart Cards
A smart card looks like a typical credit card but is much more powerful than the traditional magnetic stripe card. Smart cards make use of an embedded microprocessor chip. One of the primary advantages of smart cards over magnetic stripe cards is their ability to run small applications that perform computations. They are, in a sense, small computers. Smart cards are also compatible with a variety of portable electronic devices, such as cellular phones, personal digital assistants (PDAs), and other consumer devices.
Figure 3-6 The Java Card (Source: Sun Microsystems)
Smart card technology is quickly becoming a replacement for several current technologies—from ID badges to credit cards. Financial companies are considering smart cards as a mechanism for delivering services at a lower cost to businesses and consumers. A common service would be an electronic purse service that allows bank customers to transfer money from their bank accounts to their smart cards in the form of secure electronic cash. The electronic cash can then be used to purchase products, pay bills, or pay bridge tolls. Smart cards can be used as a form of ID in a variety of industries. Smart cards can hold information commonly found on an ID card, driver's license, or in a patient's medical records. For example, a doctor can create a record of a patient's treatment history by writing information to his or her smart card. This allows the patient, or another doctor, to have medical information available at any time. A smart card can also act as an employee access badge (by containing encrypted security information such as user names and passwords) that allows an employee access into a company's building or computer network.
Smart Card Components
Each smart card solution requires a number of hardware and software components (operating systems, APIs, and applications) and protocols (for communication between tiers and between components on a tier). There can be four or more hardware components in an enterprise smart card solution. The hardware components are the smart card, the card reader or card acceptance device (CAD), the terminal, and the back-end business applications. All smart cards contain some amount of memory—ROM, RAM, and EEPROM (that is, electrically erasable programmable read-only memory). Both memory cards and processor cards can store much more information than traditional magnetic stripe cards. ROM is used to hold the operating system and other software that you cannot modify. RAM is a volatile memory and any information in RAM is lost when the card is disconnected from the reader. Like ROM, EEPROM is used to store information that persists after power is disconnected. The CAD is a card reader that enables access to and communication with a smart card. It can also enable developers to build applications that are smart card capable. The card reader provides a "path" for your application to send and receive commands from the card. The card reader is connected to a terminal that is usually an electronic device—from common computer workstations and desktop computers (PCs) to small embedded devices, such as screen phones, set-top boxes, and cellular phones. The back-end technologies provide the information, database, or processing system to facilitate any number of smart-card enterprise solutions. Smart cards solve many of the security problems that occur in a network environment. At the center of the solution is the promising fact that a smart card requires a user PIN (Personal Identification Number) to get access to a system.
Java Card Technology in Smart Cards
Java Card technology makes it possible to create and download new applications and services to smart-card consumers. In general, a Java Card is a smart card that is capable of running Java applets. From a security standpoint, the Java Card technology maintains the built-in security of the Java platform and makes the smart card a safe way to conduct consumer transactions over such insecure networks as the Internet and public telephone systems. Thus, Java Card technology preserves many of the existing benefits of the Java programming language, such as security, robustness, and portability.
Java Card technology was designed based on the smart-card specification standard, ISO7816. This standard specifies that communication between a host application and a smart card occur through Application Protocol Data Units (APDUs). An APDU is a packet of data that conforms to a specific format. There are two types of APDUs: command APDUs and response APDUs. In Java Card technology, the host application sends a command APDU, and a Java Card applet responds with a response APDU. The Java Card technology defines a Java Card runtime environment (JCRE) on top of the hardware and the smart-card native system.
The JCRE acts as an intermediary between the native smart card system and the Java Card applet. The command APDU is transmitted to the JCRE, which sends it to the appropriate Java Card applet for processing. After processing the APDU, the Java Card applet transmits a response APDU to the JCRE, which sends it to the host application.
Java Card Runtime Environment and APIs
The JCRE provides a high-level standard interface to smart-card applications. As a result, it is much easier to develop applications for a Java smart card. The JCRE provides a secure execution environment with a virtual firewall between different applications in the same card. This allows different applications on the same card to function separately and independently from each other, as if they were on separate cards.
The Java Card API is a subset of the Java platform that allows development using object-oriented programming to create secure smart applications. By contrast, traditional smart-card application programming uses assembly language or the C programming language, which forces security evaluation of the application and also requires looking at the entire application as a unit to verify the behavior.
The Java Card API is a standard set of APIs and software classes that will run on any existing smart card. It is ISO7816-4 compliant, and compatible with formal international standards such as ISO7816 and industry-specific standards such as Europay/MasterCard/Visa (EMV). It also provides Java Card issuers with interoperable services through logical channel support defined by standards organizations, such as the European Telecommunications Standards Institute (ETSI), Third Generation Partner Project (3GPP), and Wireless Access Protocol (WAP). The Java Card provides the issuers with security options by supporting cryptographic algorithms, including Advanced Encryption Standard (AES) and Elliptic Curve Cryptography (ECC).
Let's look at the Java Card platform security features in greater detail.
Java Card Platform Security Model
The Java Card platform provides a number of security features that can be enforced at every level at the inception of application development. They are characterized as follows:
- The Java Card technology supports a subset of the Java programming language and JVM specifications, inheriting the security features built into the supported subset.
- The Java Card platform stores the objects and data in memory. During a power loss or unexpected failure, the platform makes sure that the objects and data are stored to its previous state before such failures.
- The Java Card applets are verified to ensure their integrity, because they can be downloaded over an unsecured network. A trusted third party can also cryptographically sign Java Card applets. The digital signature of an applet can be verified during on-card installation to further ensure the safety of the code.
- In the Java Card runtime, the notion of a sandbox is implemented via the applet firewall mechanism. The firewall essentially assigns an object space, called a context, to each applet on the card. Data access within a context is allowed, but the access to an applet in a different context is prohibited by the firewall. Applets residing in different contexts can share objects using secure object-sharing mechanisms by implementing a shareable interface.
- The Java Card applets are not allowed to execute native methods except for the card's vendor-issued applets. This means that applets installed after issuance of the card are restricted from running native methods.
- The Java Card technology embraces techniques using compressed archive files with cryptographic signatures to provide tamperproof distribution and installation procedures for Java class files and Java Card applets.
Java Card Applets
A Java Card applet is a smart card application written using the Java Card APIs and is able to run within a JCRE. It is important to note that Java Card applets are not intended to run in a Web browser environment. Multiple applets can be loaded and coexist on a Java Card and can have multiple instances. Each applet instance is uniquely identified by an application identifier (AID). Similar to any persistent objects, applets installed on a Java Card live throughout the entire lifetime of the Java Card as long as the card is usable. The applets will terminate only if they are uninstalled. The process of applet installation to a card (also referred to as masking) is done through the proprietary tools provided by the Java Card manufacturer.
Java Card Applet Development and Installation
To study Java Card security, it is very important to understand the process of developing and installing Java Card applets. Figure 3-7 illustrates the steps involved in developing and installing a Java Card applet.
Figure 3-7 Java Card applet development and installation
The development of a Java Card applet typically starts like developing any other Java program and compiling the source file to produce Java class files. The resulting class files are tested and debugged using a Java Card simulator environment that simulates the applet using the Java Card runtime environment running on a development workstation. The simulator helps the developer to study the behavior and results prior to deploying on a Java Card. Then the class files that make up the applet are converted to a Converted Applet (CAP) file using a Java Card CAP converter tool. As a package, the resulting CAP files represent a Java Card applet. These CAP files are further tested using a Java Card emulator tool in the development environment to identify the expected behavior of the applet in a real Java Card. Finally, the tested applet comprised of all the CAP files is downloaded into the Java Card using a proprietary tool provided by the Java Card vendor. To secure the applets (using vendor-provided tools), it is possible to sign the applet code and allow the Java Card to verify the signatures.
Java Card Applet Security
The Java Card provides a multi-application smart card environment that allows multiple applets to coexist on a Java Card and also provides the flexibility to download applets after manufacture or issuance. The Java Card provides a virtual applet firewall protection mechanism that isolates an applet package to its designated firewall partition (referred to as context). The context mechanism disallows object access from another applet located in a different context. To support cooperative applications running on a single card, it provides a secure object sharing mechanism. The sharing mechanism under specific conditions enables one context to access objects belonging to another context by performing a context switch. When an object is accessed, the JCRE enforces access control, and if the contexts do not match, the access is denied and results in a SecurityException.
Java Card Development Kit
Sun Microsystems provides a Java Card development kit that contains components and tools that you need to develop Java Card applets. The development kit provides a set of tools that support Java Card applet development and deployment onto a smart card. It provides a Java Card Workstation Development Environment (JCWDE) that simulates a Java Card runtime environment using a JVM. It provides a Converter utility, which does off-card verification processes, including class loading, linking, name resolution, bytecode verification, optimization, and conversion of an applet to a smart-card installation format. For more information about the Java Card Development Kit, refer to the Web site at http://java.sun.com/products/javacard.
In summary, a Java Card is a portable computing device about the size of a credit card that is often used as a secure storage device; beyond that, it can be used for a variety of security and identity-management solutions. Some common examples are as follows:
- Secure storage for personal and confidential information
- Prepaid GSM phone SIM cards offering a cash-free and anti-fraud mechanism
- Secure identity cards for personal identification, physical access, user authentication, and access control
- Bank credit cards that cannot be copied and misused like magnetic stripe cards
We will take a look at Java Card strategies for secure personal identification in Chapter 15, "Secure Personal Identification Using Smart Cards and Biometrics."
Securing the Java Code
The threat of reverse engineering is a well-known security problem for Java applications. By default, the byte code generated by the Java compiler contains much symbolic information, including the actual Java source of the executable and debugging information. Using reverse engineering mechanisms, it is possible to disassemble and decompile the executable Java bytecode into actual Java source code. This fact highlights the vulnerabilities of Java applications and makes clear the risks of someone having the ability to do the following:
- Modify code and data
- Determine the flow of program execution
- Determine algorithms
- Construct a fraudulent application
- Steal intellectual property
- Apply code-level security breaches
Reverse Engineering: Disassembling and Decompiling
The process of reverse engineering the Java program is done by disassembling the executable classes to an intermediate assembly code and then decompiling the assembly code to obtain the higher-level abstractions of the byte code. This higher-level abstraction contains significant source code, including variables, methods, and so forth. When compared with the actual source code, the noticeable difference is the absence of comments. It is also noted that the reverse engineering process does not provide source code with accuracy. There are many commercial and freeware tools available that provide disassembling and decompiling capabilities.
From a security standpoint, it is very important to realize the threats posed by the use of disassembling and decompiling techniques to reverse engineer the Java executable. With these techniques, a hacker can reconstruct an application with modified code and disrupt the original application by attacking its underlying resources. To counter these issues, there are several tools and techniques that allow protecting the source code from prying eyes by making it difficult to apply any reverse engineering mechanisms.
The possible ways to prevent reverse engineering of Java executables and to protect the source code are as follows:
- Code authentication: This approach adopts evaluation and verification of executable code for trusted sources, runtime checks, predictable behavior, and output. This ensures that code is not executed from an unauthorized host, not modified to produce any unpredicted output, and not duplicated to act as an original application.
- Encryption and decryption: Using encryption and decryption of executable code in transmission ensures that the code is not accessible or tampered with during its transit. This approach limits portability of the application but works well in scenarios where applications are made available via server-side invocations.
- Code obfuscation: This approach uses a transformation mechanism that changes the program and generates Java code with obscure references. The obfuscated code is understood by compilers but difficult to read by humans. This is the most popular way to prevent the success of reverse engineering capabilities on executable code.
Code Obfuscation
Code obfuscation is the process of transforming the executable in a manner that affects reverse engineering mechanisms by making the generated code more complex and harder to understand. It decouples the relationship between the executable code and its original source, which ultimately makes the decompiled code ineffective. With all those changes, the obfuscated program still works in a functionally identical way compared to the original executable. There are several transformation mechanisms that allow obfuscation of Java code, and the most common techniques [Obfuscation] are as follows:
- Structural or layout transformation: This transforms the lexical structure of the code by scrambling and renaming the identifiers of methods and variables.
- Data transformation: This transformation affects the data structures represented in the program. For example, it changes the data represented in the memory from a local to a global variable, converting a two-dimensional array into a one-dimensional array and vice-versa, changing the order of data in a list, and so forth.
- Control transformation: This transformation affects the flow control represented in the program. For example, it changes the grouping of statements as inline procedures, order of execution, and so forth.
- Tamper-proofing and preventive transformation: This transformation makes the decompiler fail to extract the actual program, and the generated code is unusable. Refer to [PrevTransform] for more detailed information.
- String encryption: This mechanism encrypts all string literals within the executable code, and during runtime invocation it decrypts them for use.
- Watermarking: This mechanism embeds a secret message in the executable that identifies the copy of the executable and allows you to trace the hacker who exploited the generated code.
With little performance overhead, the code obfuscation process restricts the abuse of decompilation mechanisms and offers portability without affecting the deployment platforms. Adopting code obfuscators is a good choice to make in the attempt to reduce the risks of reverse engineering. They prevent loss of intellectual property and offer protection of Java code from malicious attacks. The Java code obfuscators are publicly available in the form of freeware, shareware, and commercial applications.
Summary
This chapter explained the Java 2 platform architecture and its security features as they apply to building Java applications. In particular, it described the various Java platforms and the core security features that contribute to the end-to-end security of Java-based applications running on various systems—from servers to stand-alone computers, computers to devices, and devices to smart cards. It discussed securing Java applets, JNLP-based Java Web start applications and code obfuscation strategies.
The chapter also described how to use the different security mechanisms, tools, and strategies for implementing the following:
- Java application security
- Java applet security
- Java Web start security
- J2ME Platform security
- Java Card Platform security
- Java code obfuscation
In the next chapter, we will explore the Java extensible security architecture and API mechanisms that allow preserving confidentiality, integrity, authentication, and nonrepudiation in Java-based applications.
References
"Java Security Architecture."
Java™ 2 SDK, Standard Edition Documentation Version 1.4.2. Sun Microsystems, 2003. http://java.sun.com/j2se/1.4.2/docs/guide/security/spec/security-spec.doc1.html and http://java.sun.com/j2se/1.4.2/docs/guide/security/spec/security-spec.doc2.html."J2SE 5.0 Platform Specifications,"
Sun Microsystems, 2004. http://java.sun.com/j2se/1.5.0/docs/api/"J2ME Platform Specifications,"
Sun Microsystems, 2003. http://java.sun.com/j2me/"Java Card Platform Specifications,"
Sun Microsystems, 2003. http://java.sun.com/products/javacard/"Java Web Start Technology Specifications,"
Sun Microsystems, 2003. http://java.sun.com/products/javawebstart/.[Obfuscation] Christian Collberg and Clark Thomborson.
"Watermarking, Tamperproofing, and Obfuscation—Tools for Software Protection."
IEEE Transactions on Software Engineering 28:8, 735-746, August 2002[PrevTransform] Christian Collberg, Clark Thomborson, and Douglas Low.