Understanding MIDP 2.0's Security Architecture

   




One of the reasons MIDP is such a good platform for mobile devices is the security it offers. Part of this is inherent in the Java programming model: MIDlet code runs within the confines of a virtual machine, which means it is immune to some of the spectacular failures binary code may exhibit. Badly written or maliciously written binary code can render a device inoperable. At worst, badly written Java code will bring its Java environment to a halt, leaving the rest of the device unharmed.

The security support designed into MIDP 2.0 doesn't end with the JVM, however. This article describes other MIDP 2.0 features that protect users and their devices from malicious software. You'll use the J2ME Wireless Toolkit 2.0 (beta 2 or later) to learn how to work with MIDP 2.0's security architecture.

What Are Sensitive Operations?

The MIDP 2.0 specification defines an open-ended system of permissions. To make any type of network connection, a MIDlet must have an appropriate permission. For example, a MIDlet that uses HTTP to talk to a server must have permission to open an HTTP connection. The permissions defined in MIDP 2.0 correspond to network protocols, but the architecture allows optional APIs to define their own permissions.

Each permission has a unique name; the MIDP 2.0 permissions are:

  • javax.microedition.io.Connector.http
  • javax.microedition.io.Connector.socket
  • javax.microedition.io.Connector.https
  • javax.microedition.io.Connector.ssl
  • javax.microedition.io.Connector.datagram
  • javax.microedition.io.Connector.serversocket
  • javax.microedition.io.Connector.datagramreceiver
  • javax.microedition.io.Connector.comm
  • javax.microedition.io.PushRegistry

Permission names are not the same as class names, although they look similar. The javax.microedition.io.Connector.https, permission, for example, governs to a MIDlet's ability to make HTTPS connections using the javax.microedition.io.Connector class.

MIDlets don't acquire permissions explicitly in code, but rather through something called a protection domain. Before you slip into a code-security coma, though, I'll show you something you can play with.

Making an HTTP Connection

The following simple MIDlet, available in the source code for this article, makes an HTTP connection and retrieves some of the server's response:

                     
                        import
                     java.io.*;

                     
                        import
                     javax.microedition.io.*;
                     
                        import
                     javax.microedition.lcdui.*;
                     
                        import
                     javax.microedition.midlet.*;

                     
                        public
                      
                    
                        class
                     HTTPMIDlet
     
                    
                        extends
                     MIDlet
     
                    
                        implements
                     CommandListener, Runnable  
                    
                        {
                    
   
                    
                        private
                     Display mDisplay;
   
                    
                        private
                     Form mMainScreen;

   
                    
                        public
                      
                    
HTTPMIDlet()  
                    
                        {
                    
    mMainScreen  
                    
                        =
                      
                    
                        new
                      
                    
Form(
                    
"HTTPMIDlet");
    mMainScreen.
                    
append(
         
                    
"Press OK to create an HTTP connection.");

    Command exitCommand  
                    
                        =
                    
         
                    
                        new
                      
                    
Command(
                    
"Exit", Command.EXIT,  
                    
0);
    Command okCommand  
                    
                        =
                    
         
                    
                        new
                      
                    
Command(
                    
"OK", Command.OK,  
                    
0);
    mMainScreen.
                    
addCommand(exitCommand);
    mMainScreen.
                    
addCommand(okCommand);
    mMainScreen.
                    
setCommandListener(
                    
this);
   
                    
                        }
                    
  
   
                    
                        public
                      
                    
                        void
                      
                    
startApp()  
                    
                        {
                    
     
                    
                        if
                     (mDisplay  
                    
                        ==
                      
                    
null)
      mDisplay  
                    
                        =
                     Display.
                    
getDisplay(
                    
this);
    
    mDisplay.
                    
setCurrent(mMainScreen);
   
                    
                        }
                    
  
   
                    
                        public
                      
                    
                        void
                      
                    
pauseApp()  
                    
                        {
                    
   
                    
                        }
                    
  
   
                    
                        public
                      
                    
                        void
                      
                    
destroyApp(
                    
                        boolean
                     unconditional)  
                    
                        {}
                    
  
   
                    
                        // CommandListener method
                    
  
   
                    
                        public
                      
                    
                        void
                      
                    
commandAction(Command c, Displayable s)  
                    
                        {
                    
     
                    
                        if
                     (c.
                    
getCommandType()  
                    
                        ==
                     Command.EXIT)
       
                    
notifyDestroyed();
     
                    
                        else
                      
                    
                        if
                     (c.
                    
getCommandType()  
                    
                        ==
                     Command.BACK)
      mDisplay.
                    
setCurrent(mMainScreen);
     
                    
                        else
                      
                    
                        if
                     (c.
                    
getCommandType()  
                    
                        ==
                     Command.OK)  
                    
                        {
                    
       
                    
                        // Put up a wait screen.
                    
      Form waitForm  
                    
                        =
                      
                    
                        new
                      
                    
Form(
                    
"Connecting...");
      mDisplay.
                    
setCurrent(waitForm);
       
                    
                        // Make the connection
                    
      Thread t  
                    
                        =
                      
                    
                        new
                      
                    
Thread(
                    
this);
      t.
                    
start();
     
                    
                        }
                    
   
                    
                        }
                    
  
   
                    
                        // Runnable method
                    
  
   
                    
                        public
                      
                    
                        void
                      
                    
run()  
                    
                        {
                    
    String url  
                    
                        =
    
    Form resultsForm  
                    
                        =
                      
                    
                        new
                      
                    
Form(
                    
"Results");
    Command backCommand  
                    
                        =
                    
         
                    
                        new
                      
                    
Command(
                    
"Back", Command.BACK,  
                    
0);
    resultsForm.
                    
addCommand(backCommand);
    resultsForm.
                    
setCommandListener(
                    
this);
    
    HttpConnection hc  
                    
                        =
                      
                    
null;
    InputStream in  
                    
                        =
                      
                    
null;

     
                    
                        try
                      
                    
                        {
                    
       
                    
                        // Now make a connection to the server.
                    
      hc  
                    
                        =
                     (HttpConnection)Connector.
                    
open(url);
      
       
                    
                        // Retrieve the response.
                    
      in  
                    
                        =
                     hc.
                    
openInputStream();

       
                    
                        int
                     length  
                    
                        =
                      
                    
256;
       
                    
                        byte
                    [] raw  
                    
                        =
                      
                    
                        new
                      
                    
                        byte
                    [length];
       
                    
                        int
                     readLength  
                    
                        =
                     in.
                    
read(raw);
      
      String message  
                    
                        =
                      
                    
                        new
                      
                    
String(raw,  
                    
0, readLength);
      resultsForm.
                    
append(message);
     
                    
                        }
                    
     
                    
                        catch
                     (Exception e)  
                    
                        {
                    
      resultsForm.
                    
append(
           
                    
                        new
                      
                    
StringItem(
                    
"Exception: ", e.
                    
toString()));
     
                    
                        }
                    
     
                    
                        finally
                      
                    
                        {
                    
       
                    
                        if
                     (in  
                    
                        !=
                      
                    
null)  
                    
                        {
                    
         
                    
                        try
                      
                    
                        {
                     in.
                    
close();  
                    
                        }
                    
         
                    
                        catch
                     (IOException ioe)  
                    
                        {}
                    
       
                    
                        }
                    
       
                    
                        if
                     (hc  
                    
                        !=
                      
                    
null)  
                    
                        {
                    
         
                    
                        try
                      
                    
                        {
                     hc.
                    
close();  
                    
                        }
                    
         
                    
                        catch
                     (IOException ioe)  
                    
                        {}
                    
       
                    
                        }
                    
     
                    
                        }
                    
    mDisplay.
                    
setCurrent(resultsForm);
   
                    
                        }
                    
                     
                        }
                    
                  

HTTPMIDlet's run() method connects to a server, retrieves some data, and shows it on the screen. You can build and run HTTPMIDlet without thinking about protection domains. When you choose the OK command to make the connection, HTTPMIDlet calls Connector.open(). If you're using the J2ME Wireless Toolkit, the emulator prompts you for permission:

The Emulator Asks for Permission
The Emulator Asks for Permission

You have the opportunity to grant or deny permission for the connection. You can also choose to make a grant or denial of a permission permanent, although these options really only make sense in the context of MIDlet suite installation, which I'll explain later.

If you allow the connection, the application continues normally. The MIDlet displays data retrieved from the HTTP connection:

Data from the Server
Data from the Server

On the other hand, if you're feeling grumpy and deny the connection, Connector.open() throws a SecurityException. HTTPMIDlet catches the exception and displays a message about it. All MIDlets should catch any SecurityExceptions thrown from network connections and handle them gracefully.

Permission Denied
Permission Denied

How did the emulator know it should ask your permission to make the HTTP connection? Why wasn't permission just granted or denied? The answer has to do with the protection domain that contains HTTPMIDlet.

Understanding Protection Domains

A protection comprises two parts:

  1. Permissions which are allowed (granted to contained MIDlet suites), and permissions for which the user must be consulted
  2. Criteria for entry into the protection domain

When you click on the Run button in the J2ME Wireless Toolkit, the current MIDlet suite is run in a protection domain called Untrusted. In this domain, the user is consulted for all permissions. That's why the emulator prompts you for permission when HTTPMIDlet attempts its HTTP connection.

You can change the MIDlet suite's runtime protection domain by choosing Edit -> Preferences, then clicking on the Security tab:

Selecting a Protection Domain
Selecting a Protection Domain
(Click for the full image.)

The J2ME Wireless Toolkit includes four protection domains. MIDlets in the Minimum domain are denied all permissions. The Untrusted domain prompts the user for every permission. The Trusted domain is a kind of MIDlet security nirvana where all permissions are granted; it's equivalent to Maximum.

Try selecting the Minimum domain and running HTTPMIDlet again. This time, when you select OK to make the connection, permission is denied immediately and HTTPMIDlet shows a SecurityException.

The J2ME Wireless Toolkit demonstrates only one way to implement protection domains. Manufacturers, carriers, and other MIDP implementors are free to create whatever domains suit their purposes.

Understanding Permission Types

A protection domain contains allowed permissions and user permissions, the ones for which the user will be consulted. User permissions come in three varieties (called "interaction modes" in the specification). The first time a MIDlet needs a permission, the implementation asks the user whether the permission should be granted or denied. The three modes refer to how long the implementation remembers the user's decision.

For oneshot permissions, the MIDP implementation retains no memory of the user's decision and will ask for a permission every time it is needed. For session permissions, the implementation remembers the user's decision until the MIDlet is terminated. Finally, for blanket permissions, the user's decision sticks until the MIDlet suite is uninstalled.

A protection domain must deny permissions that are not explicitly allowed or user.

For example, a protection domain called HTTPOnly contains only this single permission:

allow: javax.microedition.io.Connector.http

This domain will deny all other permissions. A more lenient domain might defer to the user's judgment for other permissions, thus:

allow: javax.microedition.io.Connector.http
blanket: javax.microedition.io.Connector.https
blanket: javax.microedition.io.Connector.socket

In this case, the user will be prompted to allow or deny HTTPS and socket connections.

Remember, the definition and configuration of protection domains are entirely the province of MIDP implementors. It's your job as an application developer to work effectively within the security architecture defined by the MIDP 2.0 specification.

Requesting Permissions for MIDlet Suites

Special attributes in a JAD signal a MIDlet suite's dependence on certain permissions. You can use these attributes to say things like, "This MIDlet suite needs to make an HTTP connection and may also make socket connections." In this case, the attributes in the descriptor would look like this:

MIDlet-Permissions: javax.microedition.io.Connector.http
MIDlet-Permissions-opt: javax.microedition.io.Connector.socket

Fortunately, you don't have to add these attributes manually. The J2ME Wireless Toolkit 2.0 makes it easy to add them. Just click on Settings..., choose the Permissions tab, and add required and optional permissions as needed. The toolkit will write them into the MIDlet suite JAD automatically.

What's the good of putting the required and optional permissions in the JAD? It's a handy way to advise a device at installation time that your MIDlet suite will be attempting particular operations. If the device is about to install the MIDlet suite into a protection domain that doesn't allow some of the permissions the MIDlet suite requires, the installation can fail gracefully. Without this mechanism, the user could install a MIDlet suite only to find later that it doesn't work.

Code Signing

You've just learned that a protection domain contains allowed and user permissions. The other part of a protection domain is a set of rules that describe how MIDlet suites get into the domain.

Downloaded code can be pretty dangerous, especially when you don't know who wrote the code or where it came from. Even though MIDlets, running on a virtual machine, are intrinsically a lot safer than binary code, there are still risks. A downloaded MIDlet could make unauthorized network connections, connections that cost the user money. A rogue MIDlet could try to collect information and send it to a server for unauthorized use. A MIDP device gives its owner a lot of flexibility in choosing games and applications, but with that flexibility goes the risk of running code whose origins and motivations may be dubious.

Protection domains can be configured to make it safer for users to run downloaded MIDlets. The MIDP specification is deliberately vague about how protection domains are to be defined. It leaves a lot to the implementor's discretion: the number of protection domains that will be present and how they will be defined. The specification does, however, suggest a protection domain based on cryptographic signatures and certificates. The idea is that the carrier, or the software developer, creates a signing-key pair and obtains a certificate from a recognized certificate authority. The carrier or developer computes a signature of the MIDlet suite JAR, then places that signature and the corresponding certificate in the application descriptor.

A signed MIDlet suite assures the user that the contents of the MIDlet suite have not been tampered with and that the MIDlet suite comes from an identifiable (and thus legally accountable) source. The MIDP implementation on the device can verify the identity of the developer by verifying the developer's signature. It can use the developer's public key to verify the integrity of the MIDlet suite itself.

Version 2.0 of the J2ME Wireless Toolkit includes easy-to-use tools for managing keys and signing MIDlet suites. Suppose, for example, that you want to sign the HTTPMIDlet project. After you have used Project -> Package -> Create Package to package the MIDlet suite, begin the signing process by choosing Project -> Sign.

Signing a MIDlet Suite
Signing a MIDlet Suite

This window shows a list of all the signing key pairs that the toolkit knows about. If you have a key pair in a Java keystore that you'd like to use to sign your MIDlet suite, you can import it to the J2ME Wireless Toolkit by clicking Import Key Pair.

If you'd like to create a brand new key pair to use for signing, click New Key Pair. Fill in appropriate values for a key alias, domain name, and company name:

Creating a Key Pair
Creating a Key Pair

The toolkit creates the new key pair and adds it to the list of available signing key pairs. At this point the toolkit asks you which protection domain should be associated with the new key pair. The emulator will install MIDlet suites signed with the new key pair into whatever protection domain you choose. In this example, choose Trusted.

Choosing a Protection Domain
Choosing a Protection Domain

Once you've created the key pair and chosen a protection domain, signing a MIDlet suite is very simple. Just select the alias of the key pair you'd like to use and click on Sign MIDlet Suite. If you've arrived at the Sign MIDlet Suite window via File -> Utilities, the toolkit will ask you to locate the descriptor for the MIDlet suite you'd like to sign. Select the descriptor for the project. If you're using the HTTPMIDlet project, the descriptor will be apps/HTTPMIDlet/bin/HTTPMIDlet.jad in the J2ME Wireless Toolkit directory. Alternately, if you chose Project -> Sign from the KToolbar menu, the toolkit will use the descriptor from the currently active project.

The toolkit tells you when it finishes signing the MIDlet suite. If you don't believe it, you can open up the descriptor file in a text editor. You'll see two new attributes, MIDlet-Certificate-1-1 and MIDlet-Jar-RSA-SHA1. Both attributes appear in base64, a method for encoding binary data as text.

Installing a Signed MIDlet Suite

Now that you've got a signed MIDlet suite, what do you do with it? With the J2ME Wireless Toolkit, you can install the MIDlet suite in the emulator to see if it ends up in the right protection domain. Any MIDP implementation (including the toolkit emulator) features software that manages MIDlets and MIDlet suites, called the Application Management Software (AMS). It's the AMS that manages the installation of MIDlet suites, and launches and cleans up after MIDlets. (Did you ever wonder what calls startApp() in your MIDlet? It's the AMS.)

The toolkit emulator includes an AMS that can handle Over The Air (OTA) provisioning. All you have to do is point the emulator's AMS to a web page that contains a link for a MIDlet suite descriptor; the AMS can download and install the MIDlet suite using the OTA protocol described in the MIDP specification.

Luckily, you don't have to set up your own server to use this feature. The J2ME Wireless Toolkit 2.0 (beta 2 and later) includes a little emulation OTA server that makes it very easy to test OTA installation of a signed MIDlet suite.

Assuming you've already packaged and signed a MIDlet suite, just choose Project -> Run via OTA from the KToolbar menu. The emulator pops up, showing the AMS splash screen:

AMS Splash Screen
AMS Splash Screen

Choose Apps to display a list of installed MIDlet suites:

AMS Application List
AMS Application List

To install the MIDlet suite you've just signed, select Install Application and click the select button. The AMS prompts you for a URL, but it's already filled in with the address of the toolkit's local OTA server:

Entering a URL
Entering a URL

All you need to do is choose the Go command. The AMS connects to the toolkit's OTA server and prompts you to choose which MIDlet suite to install. There will be only one:

Choosing a MIDlet Suite
Choosing a MIDlet Suite

Choose Install to begin installation. The AMS presents some basic information about the MIDlet suite and asks if you'd like to continue:

Confirming Installation
Confirming Installation

Choose Install again and the AMS installes the MIDlet suite and adds it to the list of MIDlet suites you can run. When you run the HTTPMIDlet example, you'll be able to make the HTTP connection without the emulator asking you for permission. Why? When you created the new key pair, you associated it with the Trusted protection domain. The emulator installed the MIDlet suite in the Trusted domain when you worked through Run via OTA. Finally, when HTTPMIDlet attempted the HTTP connection, it was able to get permission from its containing protection domain, Trusted.

Summary

This article is a hands-on overview of the security architecture of MIDP 2.0. You learned about the motivations for a security architecture on MIDP devices and the network operations that are protected in MIDP 2.0. The J2ME Wireless Toolkit makes it easy to specify the permissions that a MIDlet suite needs. MIDlets are sorted into buckets called protection domains based on their credentials. A protection domain is a collection of permissions and a set of criteria for entry into the domain. The MIDP 2.0 specification includes suggestions for signing MIDlet suites cryptographically; this scheme is implemented in the J2ME Wireless Toolkit, which includes simple tools for creating a key pair and signing a MIDlet suite. Finally, the toolkit's Run via OTA feature makes it easy to test the installation of signed MIDlet suites.

MIDP 2.0's security architecture is flexible and strong; the J2ME Wireless Toolkit makes it easy to work with the new security architecture to produce software that users can run safely.




Reader Feedback
Excellent   Good   Fair   Poor  

If you have other comments or ideas for future technical tips, please type them here:

Comments:
If you would like a reply to your comment, please submit your email address:
Note: We may not respond to all submitted comments.


Back To Top