Skip to main content

Securing Java Card applications, Part 1: Building a Kerberos-enabled J2ME application

A sample app illustrates secure programming techniques

Faheem Khan is an independent software consultant specializing in enterprise application integration (EAI) and B2B solutions. Contact Faheem at fkhan872@yahoo.com.

Summary:  Smart cards take the functions you've come to expect in magnetic swipe cards to a whole new level: they can contain a large amount of data and even executable programs. The Java™ Card spec brings the power of the Java language to smart cards. In this series, Faheem Kahn shows you how to use the Java Card's power to build Kerberos-based security to a mobile application.

Date:  16 Aug 2005
Level:  Introductory
Comments:  

This two-part series of articles will demonstrate the use of Java Card™ technology to enhance the security of Java 2 Platform, Micro Edition (J2ME) applications. This first article begins with an introduction to smart card and Java Card technologies. I'll examine the workings of a sample e-bank application in which Java Cards will serve J2ME clients. I'll also demonstrate how to load and install Java applications onto Java Cards. Next, I'll explain the exchange of messages that occurs when a J2ME client invokes the services of a Java Card. Finally, I demonstrate how the different classes of the sample Java Card application work.

Smart cards and Java Card technology

Let's start by introducing smart card technology and describing what it offers. Smart cards are plastic cards that comply with the ISO 7816 standard (see Resources for a link). They have a silicon chip embedded on the plastic. Possible uses of smart cards include credit cards and SIM cards in cellular devices. The silicon chip on a smart card contains memory to permanently hold data and, possibly, smart card applications. The chip may also contain the processing capability needed to run smart card applications.

Smart cards are generally used in conjunction with a smart card reader device. This device normally also provides the electrical input to power up the smart card, and may contain host applications that communicate with applications residing on the smart cards.

Smart cards are the highly enhanced successors to magnetic strip cards. The smart card has three main advantages over magnetic strip cards:

  • A smart card's storage capacity is several hundred times that of a magnetic strip card.
  • A smart card has processing capability that cannot be added to a magnetic strip card.
  • Because a smart card has processing capability, powerful authentication mechanisms can be implemented to access the data stored in that card. This means that your data is much more secure on a smart card than it is on a magnetic strip card.

Resources contains links to some interesting articles that describe smart card technology in detail.

All Java Cards are essentially smart cards with one extra feature. The Java Card technology allows different vendors to use the Java language to develop smart card applications that are hosted on an individual card. For example, if the SIM card in your cell phone is a Java Card, it could contain value-added Java Card applications that, for instance, would manage your insurance policy, your medical records, your electronic wallet, and so on.

In this series of articles, I will focus on the use of Java Card technology with J2ME devices, although Java Card technology is not limited to the J2ME platform. The Security and Trust Services API (SATSA) enables the use of Java Card technology in J2ME devices. I will demonstrate the use of SATSA in this series of articles.

To begin, let's see how such value-added applications would work on a J2ME-enabled mobile phone with a Java Card inside. (Note that such a mobile phone would itself serve as the Java Card reader device.)

The Java Card technology for wireless applications

Let's consider two use case scenarios from the financial services and health care industries to elaborate some potential uses of Java Card technology.

A Java Card might carry medical records, such as your blood group, allergies, medical history, emergency instructions, and so on. Your doctor could access the medical records in the card to learn your medical history and prescribe medication. Drug stores could access the Java Card to verify a prescription. Your hospital might access your Java Card to provide appropriate treatment and also update your medical history. Similarly, in case of an emergency, the hospital could access emergency instructions from your Java Card. If your insurance premium depended on your health condition, your insurance company might also access your Java Card to check your medical records. The Java Card would contain authentication and authorization logic to determine who is authorized to access the various pieces of information stored on it.

A Java Card application could also work as an authentication module in a J2ME-based e-bank application. The e-bank application would allow its users to access their bank accounts using their cell phones. The Java Card application on the Java Card would contain the authentication logic that ascertained who was trying to credit or debit an account. The J2ME device would contain a MIDlet that would present an easy-to-use GUI for account access.

In this series of articles, I will use the latter example -- an e-bank application -- to demonstrate the potential of Java Card technology. To get started, let's examine the architecture of a Java Card application, look at its components, and see how it serves J2ME-based client applications.

Java Card technology offers an open architecture for smart card application development. A Java Card contains a virtual machine (called a Java Card Virtual Machine, or JCVM for short) and a set of APIs (collectively known as the Java Card API). Some of the classes and interfaces in the Java Card API are exposed for use by J2ME MIDlets and other client applications. Throughout this series of articles, you will see examples of the classes and interfaces that are exposed to client applications. (One such example is a class named RMIService, which I will introduce later while examining Listing 4.) Such classes and interfaces, together with the JCVM, are collectively known as the Java Card Runtime Environment, or JCRE for short.

In this series of articles, I will demonstrate the use of Java Card APIs in J2ME applications using a sample application. Before you can start developing the sample application, you will need to download a few freely available software packages, which have been listed in a readme.txt file included in the source code download of this article.

Let's first briefly discuss what the sample application will do. This will help you understand Java Card technology's usage model.


The architecture of an e-bank application

The sample application in this series of articles is the same Kerberos-based J2ME application that I developed in my earlier series, entitled "Lock down J2ME applications with Kerberos" (see Resources for links to all three parts). I will add Java Card features to this Kerberos application and show how the combination of Java Card and Kerberos technologies strengthens the security of J2ME applications.

The sample application lets bank account holders use their J2ME-enabled cell phones to securely access their bank accounts, make and receive payments, and check their balances. Let's call this application an e-bank application. I will develop the e-bank application in three components. There will be a Java Card application called JavaCardKerberosKey. All Java Card applications work as applets, so JavaCardKerberosKey will actually be a Java Card applet. The second component will be a MIDlet called KerberosEBank. The main purpose of this MIDlet is to provide a graphical user interface to the e-bank application. The third part of the application is the e-bank's server-side component. (At this point, you may want to go through my "Lock down J2ME applications with Kerberos" series of articles to understand the terms for the Kerberos components in the application, such as a key distribution center (KDC), a ticket granting ticket (TGT), and a session key, as well as the use of secret keying in Kerberos-based security, if those concepts are unfamiliar to you.)

The JavaCardKerberosKey applet works as a secret key manager. It contains a Kerberos secret key that is used to secure communication between a J2ME cell phone user and the bank's business logic. The secret key is used to decrypt the encrypted portion of a TGT. (I described the structure of a TGT in the "Exchange of Kerberos messages" section of the first article of my earlier series.) The encrypted portion contains the session key, which an application can only extract using the Kerberos secret key. Therefore, knowing the secret key is essential for using a TGT.

The secret key consists of eight bytes, which the e-bank application will divide into two portions of four bytes each:

  • The first part is the user's key, which both the e-bank and the user will know. The e-bank will install this key on the account holder's Java Card at the same time that it installs the JavaCardKerberosKey applet. (I will demonstrate this installation process shortly.)

    The user's key will be a simple alphanumeric PIN code. The customer will provide the user's key every time she wants to access her e-bank account. Here, note that the customer needs to use both her key and the specific Java Card in which the e-bank installed the user's key to access her e-bank account. If someone stole the user's key, it would not be operative unless the same individual also stole the physical Java Card for that particular account. This is sometimes referred to as dual factor security, which is considered quite effective. In dual factor security, authentication is based both on something that a user knows (such as a PIN code) and on something that a user carries or possesses (such as a Java Card).

  • The second part of the key is the e-bank's key, which only the e-bank will know. The e-bank will install this key when it installs the Java Card applet. The Java Card technology ensures that the e-bank's key is never exposed to any client application accessing the Java Card application.

The e-bank will set the user's and e-bank's keys in the JavaCardKerberosKey applet when it installs the applet on the user's Java Card.

The KerberosEBank MIDlet is a client of the JavaCardKerberosKey applet, which means that the KerberosEBank MIDlet will use the JavaCardKerberosKey applet to decrypt the encrypted portion of the TGT and extract the session key. The KerberosEBank MIDlet will request a TGT from the e-bank's KDC server. On receipt of the TGT, the KerberosEBank MIDlet will extract the encrypted portion of the TGT and hand it over to the JavaCardKerberosKey applet. The KerberosEBank MIDlet will also provide the user's key to the JavaCardKerberosKey applet. The JavaCardKerberosKey applet will authenticate the user; if the authentication succeeds, it will decrypt the encrypted portion of the TGT, extract the session key, and return the session key to the KerberosEBank MIDlet. The KerberosEBank MIDlet will use the decrypted session key to secure communication with the e-bank server.

Now, let's see how the e-bank will load the JavaCardKerberosKey applet onto the user's Java Card.


Loading a Java Card application onto a Java Card

Java Card applications work as applets having a definite life cycle: they are compiled, copied onto a Java Card, installed, and executed. The top-level class in all Java Card applications has to extend the javacard.framework.Applet class, which is part of the Java Card framework.

Java Card applications are compiled as normal Java 2 Platform, Standard Edition (J2SE) applications (for example, using a Java IDE like Eclipse). Suppose the e-bank application vendor has successfully compiled its JavaCardKerberosKey applet and is now ready to install it in the JCRE of a Java Card. The e-bank will need to follow a few steps in order to do so.

After compiling the Java Card application code into a set of .class files, the e-bank will pack, or cap, the .class files into a single .cap file, which will contain the complete Java Card application. The e-bank can cap its Java Card application using the converter tool that comes with the Java Card Development Kit, called JCDK for short. If you want to follow along on your own machine, you can download JCDK as a single zip file named java_card_kit-2_2_1.zip (see the Resources section for download details). I will use JCDK extensively in this series of articles, so it's a good idea to do this now. When you unzip java_card_kit-2_2_1.zip, you will find a folder named bin. The converter and other utility tools that I will use in this article are in this folder.

The e-bank wraps the Java Card application data in the .cap file using the command-line statement shown in Listing 1.


Listing 1. The converter tool command-line statement
    converter -applet 0xa:0x0:0x0:0x0:0x62:0x3:0x1:0x1
                       KerberosCardApplet.JavaCardKerberosKey
              -classdir X:\KerberosCardApplet
              -exportpath X:\java_card_kit-2_2_1\api_export_files 
               KerberosCardApplet 0xa0:0x0:0x0:0x0:0x62:0x3:0x1:0x0 1.0

You can see from the command line provided above that the converter tool takes three options: applet, classdir, and exportpath.

  • The applet option specifies the application ID (or AID for short) and the qualified name of the JavaCardKerberosKey applet. All Java Card applets have AIDs. Client applications (such as the KerberosEBank MIDlet) identify the different applets in a Java Card through their AIDs. The first five bytes of an AID are a fixed value (0xa0:0x0:0x0:0x0:0x62). You can specify an additional 0 to 11 bytes to uniquely identify your application.
  • The classdir option specifies the path of the .class files that you want to cap.
  • The exportpath option specifies the location path to export files. The export files tell where to find the Java Card API implementation. JCDK comes with all the required export files. When you unzip the java_card_kit-2_2_1.zip file, you will find a folder named api_export_files, which contains all the export files you need. You just need to include the complete path of the api_export_files folder here.

The string KerberosCardApplet in Listing 1 specifies the applet's package name. The applet is identified by this package name in the JCRE. The byte sequence 0xa0:0x0:0x0:0x0:0x62:0x3:0x1:0x0 specifies the AID of the KerberosCardApplet package. The last number in Listing 1 (1.0) specifies the version number of the applet.

The converter tool generates two output files, named KerberosCardApplet.cap and KerberosCardApplet.exp. The two files are stored in a directory named javacard at the classpath location specified in the classdir option to the converter tool. The KerberosCardApplet.cap file is the required cap file that I needed to generate. The KerberosCardApplet.exp file is an export file that you will need only if you want to use some classes of your e-bank application in some other Java Card application. I won't be using this export file in this series.

Now the KerberosCardApplet.cap file needs to be loaded (or copied) onto the Java Card. All Java Cards come loaded with an installer applet, whose job is to load new Java Card applications onto the Java Card. The AID of the installer applet is 0xa0:0x00:0x00:0x00:0x62:0x03:0x01:0x08:0x01.

The KerberosCardApplet.cap file cannot be directly loaded onto a Java Card. The installer applet, like all other Java Card applets, communicates with the outside world through sequences of bytes called Application Protocol Data Units, or APDUs for short. APDUs are the communication format that all Java Card applications use to communicate with client applications running outside the Java Card.

A tool named scriptgen (which comes with the JCDK) transforms the KerberosCardApplet.cap file into a script file named KerberosCardApplet.src. KerberosCardApplet.scr will contain the KerberosCardApplet.cap file in the required form of a set of APDUs. The installer applet accepts the sequence of APDUs from the KerberosCardApplet.src file. The following command line generates a set of APDUs for KerberosCardApplet.cap and stores the output APDUs in KerberosCardApplet.scr.

scriptgen -o KerberosCardApplet.scr KerberosCardApplet.cap

The KerberosCardApplet.scr file generated by scriptgen is not complete; you'll need to edit it manually. Open the KerberosCardApplet.scr in a text editor and make the following changes in the file:

  1. Insert the string powerup in the first line of the file. As you will shortly see, I will use another tool named apdutool (which also comes with the JCDK) to upload the APDUs contained in the KerberosCardApplet.scr file to the installer applet. The powerup command prepares the two ends (the apdutool and the installer applet) for communication.
  2. I also need to add the AID of the installer applet. The AID will occur immediately after the powerup command. This specifies the applet (in this case, the installer applet) that the apdutool will ask the card reader device to invoke for APDU exchange.
  3. Insert the string powerdown at the end of the file. This command marks the end of APDU exchange between the apdutool and the installer applet.

The source code download in this article contains files named KerberosCardApplet_BeforeEditing.scr and KerberosCardApplet_AfterEditing.scr. These files illustrate what the KerberosCardApplet.scr file looks like before and after manual editing.

But where is the Java Card and its reader device to which you will upload the applet using apdutool? The JCDK provides a tool called cref that simulates a Java Card in a card reader. So, before you can use the apdutool, you must start the cref simulator by using the following command-line statement:

start cref -o KerberosCardApplet.eeprom

This command line starts the cref simulator in a separate window, listening at its default port, 9025. The -o option specifies the name of an output file with an .eeprom extension.

When you run the command-line statement above, a file named KerberosCardApplet.eeprom is created in the command line execution directory. This file simulates the electrically erasable programmable read only memory (EEPROM) portion of a Java Card. You don't need to understand the details of a Java Card's EEPROM to write Java Card applications; just note that the EEPROM portion of the card contains the Java Card applets.

Now run apdutool using the following command-line statement. The KerberosCardApplet.eeprom file will be populated with the .scr file.

apdutool X:/KerberosCardApplet/javacard/KerberosCardApplet.scr

After populating the KerberosCardApplet.eeprom file with the required data, the cref simulator will close the connection with apdutool. After that's happened, you should manually shut down the cref simulator by closing the simulator window.

Note that the cref simulator works according to the Java Card platform specification. Therefore, when you work with a real Java Card reader, you'll use apdutool just as you do here.

The JavaCardKerberosKey applet is now loaded in the Java Card simulator. Next, you'll learn how to install this applet.


Installing a Java Card application

In the previous section, you learned how to load a Java Card application onto a Java Card. Now the JavaCardKerberosKey applet resides inside the Java Card. The next step is to install the applet, so that the KerberosEBank MIDlet client can use its services.

Installing the JavaCardKerberosKey applet means instantiating the applet class in the JCRE. I need to write a new .scr file (install.scr) to install the JavaCardKerberosKey applet. You can see the contents of this file in Listing 2.


Listing 2. The install.scr file to install the JavaCardKerberosKey applet
powerup;
0x80 0xB0 0x00 0x00 0x00 0x7F;

// Command APDU for the AID of the installer applet.
0x00 0xA4 0x04 0x00 0x09 0xa0 0x00 0x00 0x00 0x62 0x03 0x01 0x08 0x01
0x7F;

// Command APDU for the AID of the JavaCardKerberosKey applet 
// along with user and e-bank keys.
0x80 0xB8 0x0 0x0 0x12 0x08 0xa0 0x0 0x0 0x0 0x62 0x03 0x0 0x01 
0x08 0x01 0x02 0x03 0x04 0x05 0x06 0x07 0x08 0x7F;

0x80 0xBA 0x00 0x00 0x00 0x7F;
powerdown;

The install.scr file in Listing 2 is very simple. It starts with the powerup command and ends with the powerdown command. In between, there are two AIDs wrapped inside their respective command APDUs. The first AID identifies the installer applet and the second identifies the applet to be installed (that is, the JavaCardKerberosKey applet).

The e-bank application will choose its own AID for its JavaCardKerberosKey applet. There are two other pieces of data that the e-bank application will need to include with the applet AID data: the user's key and the e-bank's key that I want to provide to the JavaCardKerberosKey applet at the time of installation. You need to include these two keys within the applet AID data in the install.scr file, because later on you will have no chance to send the keys to the JavaCardKerberosKey applet.

Now there are three bits of data -- the AID bytes, the user's key, and the e-bank's key -- wrapped in the command APDU for the AID of the JavaCardKerberosKey applet. If the AID for the JavaCardKerberosKey applet you supplied is already occupied by some other applet on the Java Card, the apdutool will receive an error from the installer applet.

After writing the install.scr file, you will need to launch the cref simulator, using the same command-line statement as before.

start cref -o KerberosCardApplet.eeprom

This starts the cref simulator in a separate window. The cref simulator is now waiting for apdutool to send applet installation instructions. Use the apdutool to send the install.scr file APDUs to the cref simulator and install the applet on the Java Card. The following command-line statement shows the use of apdutool to install the JavaCardKerberosKey applet.

apdutool install.scr

Here you can see that the AID bytes of the install.scr file that the apdutool sends to the installer applet are plain text bytes without any encryption. Therefore, you are assuming that the installation of the JavaCardKerberosKey applet takes place over a trusted secure network (the e-bank's private network within its premises, say, or perhaps a virtual private network). In the next article in this series, I will demonstrate how Java Card applications can also communicate securely with the outside world.

On receipt of the install command, the installer applet calls the install() method of the JavaCardKerberosKey applet.

The install() method

The install() method of all Java Card applets is static, which means that it can be called without instantiating the applet. You can see the install() method implementation of the JavaCardKerberosKey applet in Listing 3.


Listing 3. The install() method of JavaCardKerberosKey
public static void install(
              byte[] installationData, short offset, byte length) {
    new JavaCardKerberosKey(installationData, offset, length);
}//install()

As you can see, the install() method does just one job: it instantiates a new JavaCardKerberosKey object by calling its constructor. The install() method is called once at the time of installation, so only one instance of the JavaCardKerberosKey applet will exist in the course of the applet's lifetime.

Notice that the install() method receives three parameters from the installer applet:

  • The first parameter is a byte array named installationData, which contains the AID of the JavaCardKerberosKey applet, the user's key, and the e-bank's key.
  • The second parameter specifies the starting position of content in the installationData byte array.
  • The third parameter specifies how many bytes are for your use after the starting position.

The install() method passes the three parameters to the JavaCardKerberosKey applet constructor. The applet constructor processes the three parameters during the installation process. Let's see how.

The JavaCardKerberosKey constructor

This installation process provides the applet being installed with a chance to perform application-specific start-up processing. For example, the JavaCardKerberosKey applet wants to store the e-bank's and user's keys during the installation process for later use during user authentication.

You can see the JavaCardKerberosKey constructor in Listing 4.


Listing 4. The JavaCardKerberosKey applet constructor
public JavaCardKerberosKey(byte[] installationData, short offset, byte length) {
 //**** Step1 *****
 byte[] userKey = new byte[PIN_LENGTH];
 byte[] eBankKey = new byte[PIN_LENGTH];

 //getting offset of key data in installationData byte array.
 byte aidLength = installationData[offset];
 short kdOffset = (short)(offset+aidLength+1);

 byte[] appletAID = new byte[aidLength];
 Util.arrayCopy(appletAID, (short)0, installationData, (short)(offset+1),(short)aidLength);
 Util.arrayCopy(userKey, (short)0, installationData, (short)(kdOffset+1), PIN_LENGTH);
 Util.arrayCopy(eBankKey, (short)0, installationData, (short)(kdOffset+5),PIN_LENGTH);

 
 //**** Step2 *****
 UserPIN userPIN = new UserPIN (userKey);
 EBankPIN ebankPIN = new EBankPIN(userKey, eBankKey);

 //**** Step3 *****
 SecurityService secService = 
     new KerberosSecurityService(userPIN);

 //**** Step4 *****
 Remote keyManager = 
     new KerberosKeyManager(secService, ebankPIN);

 //**** Step5 *****
 RMIService rmiService = new RMIService(keyManager);

 //**** Step6 *****
 disp = new Dispatcher( (short)2);
 disp.addService(secService, Dispatcher.PROCESS_COMMAND);
 disp.addService(rmiService, Dispatcher.PROCESS_COMMAND);

 //**** Step7 *****
 register(appletAID, (short)0, aidLength);
}//JavaCardKerberosKey()

The JavaCardKerberosKey constructor performs seven steps (you can use the comments in the listing to follow along in the code):

  1. The installationData byte array contains the e-bank's key and the user's key appended to the AID bytes. So the first step is to extract the two keys and the AID bytes from the installationData byte array. In Step 1 of Listing 4, I have instantiated three byte arrays named eBankKey, userKey, and appletAID, and used a utility method named Util.arrayCopy() to copy the corresponding key data from the installationData byte array into their respective byte arrays.
  2. In the second step, I instantiate two PIN classes named UserPIN and EBankPIN. You'll see how these classes work in the next article of this series; for now, just note that the UserPIN class wraps the user's key bytes that you got in Step 1, and that EBankPIN class wraps both the user's and e-bank's keys. The two classes will use the keys for authentication and decryption later on.
  3. In Step 3, I instantiate a KerberosSecurityService object and pass the UserPIN object (from Step 2) to its constructor. The KerberosSecurityService class provides an authentication service to the e-bank application. It uses the UserPIN class for data decryption and user authentication purposes.
  4. In Step 4, I have instantiated a KerberosKeyManager object. The KerberosKeyManager class implements the business logic of the e-bank application. The business logic here takes an encrypted Kerberos structure and extracts a Kerberos session key from that structure. A client application (such as the KerberosEBank MIDlet) will use the business logic of the e-bank application by calling the methods of the KerberosKeyManager class. Later, in Using a Java Card applet with a J2ME client application, I'll explain how I'll expose the methods of the KerberosKeyManager class to the client for remote access, how the remote client will gain access to a reference of the KerberosKeyManager object, and how the JCRE will route method invocation requests from remote clients to the KerberosKeyManager class.
  5. In Step 5, I instantiate an RMIService class. The RMIService class is used to service remote method invocation requests from remote clients. For example, the KerberosEBank MIDlet client will first get the reference to the KerberosKeyManager class and then make a remote method invocation request using the remote reference. The RMIService class will handle all such remote requests. In other words, you can say that the RMIService class will service the remote clients on behalf of the KerberosKeyManager class. Therefore, while instantiating the RMIService class, I will pass the KerberosKeyManager object as a parameter to the RMIService constructor. This identifies the class that the RMIService class should use for serving RMI requests from remote clients.
  6. The Java Card framework provides a well-defined mechanism for all service provider classes. In Java Card terminology, a service is defined as an object that processes a command from a client. Commands from clients come as APDUs.

    All service provider classes are actually command APDU processing classes. Both KerberosSecurityService and RMIService classes are service provider classes; therefore, I must follow Java Card's service provision (or APDU processing) mechanism in order to use these two classes in the JavaCardKerberosKey applet. Later in this section, I will explain how the different service provider classes work in the Java Card framework. For now, just note that I must register all service provider classes with a class named Dispatcher.

    In Step 6, I instantiate a Dispatcher object and then call its addService() method. The Dispatcher constructor takes just one parameter: the maximum number of services I want to register with the Dispatcher object.

    The addService() method takes two parameters. The first is the instance of the service provider class that will handle a client's request for a service; the second is the identifier for a phase. The Java Card platform specification defines various APDU processing phases; you'll learn more about these in APDU processing in a Java Card application. For now, just note in Step 6 in Listing 4 that both the service provider classes (KerberosSecurityService and KerberosKeyManager) are registered in the PROCESS_COMMAND phase.

  7. Now I am all set. The applet is ready to serve its J2ME clients. Therefore, I will register the applet with the JCRE by calling the register() method of the javacard.framework.Applet class. The register() method takes three parameters. The first is a byte array containing the AID of the applet, the one I got in Step 1, which the client uses to access this applet. If the AID for the applet is not unique, the register() method raises an illegal AID exception and the applet registration with JCRE fails. The second parameter is an offset value specifying where the AID bytes begin in the byte array. The third parameter specifies the length of the AID bytes in the byte array.

APDU processing in a Java Card application

Now let's discuss the different phases of APDU processing in a Java Card application.

Recall that while discussing Steps 3 and 6 of the JavaCardKerberosKey constructor in Listing 4, I instantiated a KerberosSecurityService object and registered it with the Dispatcher object. The KerberosSecurityService class has a method named processCommand(), which you'll see in more detail later in Major classes in the e-bank application. When the Dispatcher receives some APDU from a client application, it will forward that APDU to the processCommand() method of the KerberosSecurityService class.

Any class that wants to act as a service provider (such as the KerberosSecurityService class, which acts as an authentication service provider) should implement an interface named Service, which is part of the javacard.framework.service package. The Java Card framework contains a convenience class named BasicService that implements the Service interface along with many helper methods to process incoming APDUs from requesting client applications. Therefore, instead of implementing the Service interface directly, it is better to extend BasicService. That's what I'll do while implementing the KerberosSecurityService class.

The Service interface contains methods named processDataIn(), processCommand(), and processDataOut(). Recall that I registered the KerberosSecurityService class for the PROCESS_COMMAND phase in step 6 of Listing 4. The PROCESS_COMMAND phase is actually one of the three available APDU processing phases; the other two are PROCESS_INPUT_DATA and PROCESS_OUTPUT_DATA.

The Java Card framework allows you to register as many services in any of the three phases as you like. The three phases work in the following sequence:

  1. When a Java Card applet receives an APDU from a client application, it hands over the APDU to the Dispatcher, and the PROCESS_INPUT_DATA processing phase begins. The Dispatcher first checks to see how many service provider classes are registered with it for the PROCESS_INPUT_DATA phase. It will then call the processDataIn() methods of the classes one by one. The sequence of processDataIn() method calls will be the same as the sequence in which the classes were registered with the Dispatcher.

    You should keep in mind that any processDataIn() method can stop further processing in the PROCESS_INPUT_DATA phase by returning true. If this happens, the JCRE will think that the PROCESS_INPUT_DATA phase is over, and will, therefore, not call the processDataIn() methods of the remaining service classes registered for the PROCESS_INPUT_DATA phase.

    In the JavaCardKerberosKey applet, you don't need to do any processing in the PROCESS_INPUT_DATA phase. That's why I have not registered any service class with the Dispatcher for this phase.

  2. After the PROCESS_INPUT_DATA phase, the PROCESS_COMMAND phase takes place. The JCRE will call the processCommand() method of the service provider classes registered for the PROCESS_COMMAND phase one by one. If any of the processCommand() methods return true, the PROCESS_COMMAND phase will be terminated and no further calls to the remaining processCommand() methods will be made.

    Recall from Step 6 of the JavaCardKerberosKey constructor of Listing 4 that the JavaCardKerberosKey constructor registered two service provider classes (first KerberosSecurityService and then RMIService) for the PROCESS_COMMAND phase. The JCRE will first call the processCommand() method of the KerberosSecurityService class, which will provide authentication service to the application. If the requesting client is successfully authenticated, the processCommand() method of the KerberosSecurityService class will return false, so that the processCommand() method of the RMIService class will be called. This method will use the KerberosKeyManager class to provide the actual Kerberos session key to the requesting client.

  3. After the PROCESS_COMMAND phase is over, the PROCESS_OUTPUT_DATA phase begins. It is similar to the first two phases. The processDataOut() methods of the service provider classes are called in this phase. I have not used this phase in the sample application.

Now it should be obvious why every service provider class needs to implement the Service interface: this interface contains the three APDU processing methods (processDataIn(), processCommand(), and processDataOut()).

The e-bank is now finished installing the Java Card application, and you have seen how to install and register the JavaCardKerberosKey applet in a Java Card. You have also seen the different APDU processing phases of a Java Card application. Now let's see how a J2ME application will select and execute a JavaCardKerberosKey to perform certain operations. Let's start by looking at the sequence of events that occurs in a communication session between the client application (that is, the KerberosEBank MIDlet) and the JavaCardKerberosKey applet.


Using a Java Card applet with a J2ME client application

There are two ways in which client applications will communicate with Java Card applets: synchronously or asynchronously. Synchronous communication is like calling a method of a class, and is blocking -- in other words, the application is blocked after making a method call until the method returns. Synchronous communication in Java Card applications uses the Remote Method Invocation (RMI) framework. On the other hand, asynchronous communication is like sending and receiving messages. Asynchronous communication is always non-blocking: you send a message and then start doing something else until you receive a response from the recipient.

The e-bank application will use the synchronous (RMI) method of communicating with Java Card applications. Let's see the sequence of events that occurs when the KerberosEBank MIDlet communicates synchronously with the JavaCardKerberosKey applet.

Suppose a J2ME cell phone user wants to check the balance in her e-bank account. The sequence of events shown in Figure 1 allows her to check the balance.


Figure 1. The sequence of events that occur when a J2ME mobile phone user checks her e-bank account
The sequence of events that occur when a J2ME mobile phone user checks her e-bank account

You can map the sequence numbers in Figure 1 to the following steps:

  1. The J2ME mobile phone user invokes the KerberosEBank MIDlet in her J2ME cell phone and provides the user's key to the KerberosEBank MIDlet.
  2. The KerberosEBank MIDlet fetches a Kerberos ticket granting ticket (TGT) from a Kerberos distribution center (KDC). I discussed the details of fetching a Kerberos TGT from a KDC in the "Authoring a TGT request" section of the second article in my "Lock down J2ME applications with Kerberos" series. You can also refer to the "The request for a TGT" section of the same article, where I explained the structure of a TGT, which contains an encrypted portion named enc-part. The KerberosEBank MIDlet will extract this encrypted portion from the TGT.
  3. Now the KerberosEBank MIDlet wants to extract the session key from the encrypted portion. The session key will allow the KerberosEBank MIDlet to further communicate with the e-bank's server-side implementation. To do this, the MIDlet needs to communicate with the JavaCardKerberosKey applet. That's where you need a Security and Trust Services API (SATSA) implementation on the J2ME mobile phone. Without a SATSA implementation a J2ME device cannot communicate with a Java Card applet. SATSA provides a standard API for MIDlet applications to communicate with Java Card applets.

    The KerberosEBank MIDlet will ask SATSA to select the JavaCardKerberosKey applet. The MIDlet will provide the AID of the JavaCardKerberosKey applet to SATSA and ask it to create a synchronous connection with the applet. Here you should recall that a J2ME mobile phone can itself act as a card reader device (if, for example, the SIM card in your phone is itself the Java Card that hosts the JavaCardKerberosKey applet).

  4. SATSA sends an APDU to the JavaCardKerberosKey applet. The APDU wraps the AID of the JavaCardKerberosKey applet (the applet that you want to select). The APDU sent at this stage is called the SELECT APDU, the structure of which is clearly defined by the Java Card platform specification. I don't need to go into the details of the different types of APDUs, as SATSA manages the authoring and processing of all the types of APDUs that you need to communicate with Java Card applications.
  5. A host application inside the Java Card reader device routes the SELECT APDU to the JCRE inside the Java Card.
  6. The JCRE receives the SELECT APDU from the host application and identifies the Java Card applet by the AID wrapped inside the SELECT APDU.
  7. Now the JCRE finds the reference to an object that will be exposed to remote clients. In this case, the KerberosKeyManager object exposes methods for the KerberosEBank MIDlet. Therefore, the JCRE will wrap a reference to the KerberosKeyManager object in a SELECT response APDU.
  8. The JCRE returns the SELECT response APDU to the host application. Note that the SELECT response APDU wraps a reference to the KerberosKeyManager object.
  9. The host application returns the SELECT response APDU to the KerberosEBank MIDlet using SATSA.

  10. The J2ME application will process the response APDU using SATSA and extract the reference to the KerberosKeyManager object from the APDU.
  11. The KerberosEBank MIDlet can remotely invoke the different methods of the KerberosKeyManager object. Suppose the MIDlet invokes a method named getKey() on the KerberosKeyManager object. The MIDlet passes the user's key (from Step 1) and the encrypted portion of the TGT (from Step 2) in encrypted form to the getKey() method.
  12. The control goes back to SATSA. The SATSA framework prepares an INVOKE APDU containing the getKey() method call and sends the APDU to the host application inside the Java Card reader device.
  13. The host application sends the INVOKE APDU to the JCRE.
  14. The JCRE hands over the INVOKE APDU to the JavaCardKerberosKey applet.
  15. The JavaCardKerberosKey applet receives the INVOKE APDU and hands over the APDU to the Dispatcher.
  16. The Dispatcher checks to see which service provider should get control of the INVOKE APDU. It finds that the PROCESS_COMMAND phase requires that the KerberosSecurityService.processCommand() method gain control. (If the KerberosSecurityService.processCommand() method had returned false, the RMIService class would have gain control.)
  17. Therefore, the Dispatcher forwards the INVOKE APDU to the KerberosSecurityService.processCommand() method.
  18. The KerberosSecurityService.processCommand() method performs data decryption and authentication steps. If the decryption of the data fails, it raises an invalid data exception; otherwise, it proceeds with user authentication. If authentication is successful, the result is stored for future use, and the method returns false.
  19. If the KerberosSecurityService.processCommand() method returns false, the Dispatcher transfers control to the RMIService class and hands over the INVOKE APDU to the RMIService.
  20. The RMIService class now has the INVOKE APDU. It processes the APDU to confirm that it is an INVOKE APDU, meaning that it actually contains an RMI call. Next, the RMIService class extracts the name of the Remote object and its method that the INVOKE APDU is asking to invoke. In my case, the name of the remote object is KerberosKeyManager and the method that should be invoked is getKey(). Therefore, the RMIService class invokes the KerberosKeyManager.getKey() method. While invoking the method, it also parses the INVOKE APDU to extract the data that goes along with the method call as a parameter. In my case, the getKey() method needs two pieces of information: the user's key (from Step 1) and the encrypted portion of the TGT (from Step 2).
  21. The KerberosKeyManager.getKey() method decrypts the encrypted portion of the TGT, extracts the session key from the decrypted data, and returns the session key to RMIService.
  22. The RMIService returns the session key to the Dispatcher.
  23. The Dispatcher hands over the session key to the JCRE.
  24. The JCRE authors a response APDU and sends it to the host application.
  25. The host application routes the response APDU to the KerberosEBank MIDlet. The MIDlet uses SATSA to extract the session key from the APDU and then uses the session key for further communication with the e-bank.

Notice from the sequence of events that if the user either forgets her PIN code or does not have her Java Card, she will not be able to use her e-bank account.

I've finished discussing the sequence of events that result in the working of Java Card applications. I have already introduced the two important Java Card classes of the e-bank application (KerberosSecurityService and KerberosKeyManager). Now let's see how those classes work.


Major classes in the e-bank application

In the previous two sections, I introduced the following Java Card classes, which perform different tasks in the e-bank application:

  • KerberosSecurityService
  • KerberosKeyManager

Now it's time to demonstrate how these classes work.

The KerberosSecurityService class

The KerberosSecurityService class shown in Listing 5 provides an authentication service to the e-bank application. The KerberosSecurityService class authenticates the user who tries to access the e-bank application.


Listing 5. The KerberosSecurityService class
public class KerberosSecurityService 
    extends BasicService implements SecurityService {
    
    private UserPIN userPIN = null;
    private byte userAuthenticated = 0;
    private static final byte INS_INVOKE = (byte)0x38;
	
    public KerberosSecurityService (UserPIN userPin) {
        this.userPIN = userPin;
        userAuthenticated = 0;
    }//KerberosSecurityService
    
    public boolean isAuthenticated(short principal) 
        throws ServiceException {
        return (userAuthenticated == principal);
    }
    
    public boolean isCommandSecure(byte properties) 
        throws ServiceException {
        return true;
    }
    
    public boolean isChannelSecure(byte prop) {
        return true;
    }
  
    public boolean processCommand(APDU apdu) {
       //**** Step 1 ****
       if (isInvokeAPDU(apdu)) {
           receiveInData(apdu);

          //**** Step 2 ****
          if (userPIN.check(apdu.getBuffer(), (short)5, (byte)4)) {
              //**** Step 3 ****
              userAuthenticated = PRINCIPAL_CARDHOLDER; 
              return false;					
          } else {
              fail(apdu, ISO7816.SW_DATA_INVALID);
          }
      }
      return false;
   }//processCommand
	
   private boolean isInvokeAPDU (APDU apdu) {
       return (getINS(apdu) == INS_INVOKE);
   }
}

Now let's see how the different methods of the KerberosSecurityService class shown in Listing 5 work.

The KerberosSecurityService constructor
Recall that the KerberosSecurityService constructor was called during the installation phase in Step 3 of Listing 4. Look at the KerberosSecurityService constructor in Listing 5, which takes just one parameter, named userPIN; this is a UserPIN object. The UserPIN class keeps the user's key stored with it for authentication later on. I'll explain the UserPIN class in the next article of this series.

The KerberosSecurityService constructor stores the UserPIN object for later use and also initializes an authentication flag. This flag is set after authentication.

The most important method of the KerberosSecurityService class is processCommand(), which I introduced earlier in APDU processing in a Java Card application. Let's now see how this method works.

The processCommand() method
The processCommand() method receives an APDU object as a parameter. This APDU contains two things: an encrypted Kerberos structure (that contains the Kerberos session key in encrypted form) and the user's key; I'll use the latter to authenticate the user. I am not interested in processing the Kerberos structure in the KerberosSecurityService class; I'll do this after authenticating the user. (You'll see this happen when I discuss the KerberosKeyManager class in the next section.)

The processCommand() method performs the following authentication steps, as shown in Listing 5:

  1. In Step 1, I check to see if the incoming APDU is an INVOKE APDU. I have used a helper method named isInvokeAPDU() that checks the APDU and returns true if it is an INVOKE APDU and false otherwise. If it is not an INVOKE APDU, I don't need to do anything, and return false.
  2. Next, I check the validity of the user's key. The processCommand() method passes the APDU object to the UserPIN.check() method, which extracts the user's key from the APDU and returns true only if the user's key matches the one I installed in the UserPIN object in Step 2 of the JavaCardKerberosKey applet constructor (Listing 4).
  3. If the comparison is successful, the processCommand() method sets an authentication flag and returns false.

The KerberosSecurityService class also implements the SecurityService interface, which is part of the javacard.framework.service package. Every class that wants to act as an authentication service provider should implement the SecurityService interface. This interface defines methods that provide services for user authentication and data integrity checking. For example, a method named isAuthenticated() will use the authentication flag set in Step 3 of the processCommand() method to indicate whether the user has successfully authenticated or not. The KerberosKeyManager class will call the isAuthenticated() method.

Now, let's see how the KerberosKeyManager class will process a user's request to extract the Kerberos session key from a Kerberos structure.

The KerberosKeyManager class

In the Java Card framework, any class that wants to process RMI requests must implement the java.rmi.Remote interface. This is an empty interface, and Java Card applications define their own Remote interface by extending java.rmi.Remote. I have extended the Remote interface in an interface named KeyManager, shown in Listing 6.


Listing 6. The KeyManager interface
public interface KeyManager extends Remote {
    public static final short INVALID_DATA = (short)0x6002;
    public static final short INVALID_PIN = (short)0x6003;
    public static final short CORRUPTED_DATA = (short)0x6004;

    public byte[] getKey(byte[] encKeyData) 
        throws RemoteException, UserException;
}

The KeyManager interface contains just one method, named getKey(). This method contains the business logic of the e-bank application. The KerberosKeyManager class will implement the KeyManager interface as shown in Listing 7.

As I mentioned earlier in Installing a Java Card application, the e-bank application's business logic uses RMI-based synchronous communications with client applications. That's why in Step 4 of the JavaCardKerberosKey constructor (Listing 4), I instantiated the KerberosKeyManager object, passed it to RMIService, and registered RMIService with the Dispatcher for the PROCESS_COMMAND phase.

As a result of the SELECT APDU exchange in the previous section, the e-bank application sent (or exported) a reference to the same KerberosKeyManager object to the KerberosEBank MIDlet. The MIDlet can now call the methods exposed by the KerberosKeyManager class. The KerberosKeyManager class uses the authentication services of the KerberosSecurityService class. The authentication check allows only authenticated clients to execute the methods of the KerberosKeyManager class remotely.


Listing 7. The KerberosKeyManager class
public class KerberosKeyManager 
     extends CardRemoteObject implements KeyManager 
{
    private EbankPIN ebankPIN;
    private KerberosSecurityService securityService;

    public KerberosKeyManager(
        KerberosSecurityService srv, EbankPIN ebankPIN){
        super();
        securityService = srv;
        this.ebankPIN = ebankPIN;
    }//KerberosKeyManager

    public byte[] getKey(byte[] encKerberosData) throws
        RemoteException, UserException {
        if(!securityService.isAuthenticated
            (SecurityService.PRINCIPAL_CARDHOLDER)) {
		UserException.throwIt(INVALID_PIN);
        }
        byte[] sessionKey = ebankPIN.getDecryptedSessionKey(encKerberosData);

	  if(sessionKey == null)
            UserException.throwIt(INVALID_DATA);

	  return sessionKey;
    }//public getKey()
}

Notice in Listing 7 that the KerberosKeyManager class extends a class named CardRemoteObject. The CardRemoteObject class is part of the Java Card framework and is included in the javacard.framework.service package. Recall from the previous section that you need to export a reference to the KerberosKeyManager object to the KerberosEBank MIDlet. The CardRemoteObject class contains the logic that handles all the issues related to the export of a Java Card object to the JCRE, which in turn manages the export to external clients like the KerberosEBank MIDlet.

Therefore, in order to develop a Java Card class whose reference I want to export to the JCRE, now I just need to do two more things:

  • Extend the CardRemoteObject class.
  • Call the CardRemoteObject constructor in the KerberosKeyManager constructor (the CardRemoteObject constructor automatically exports a reference to the KerberosKeyManager class to the JCRE).

You can see in Listing 7 that I have done these two things in the KerberosKeyManager class. Note in this listing that KerberosKeyManager has just two methods: a constructor and a method named getKey(). I called the KerberosKeyManager constructor from the JavaCardKerberosKey constructor, while discussing the install() method in Listing 3. Now let's see how the constructor works.

The KerberosKeyManager constructor
The KerberosKeyManager constructor takes two parameters. The first (secService) is an instance of the KerberosSecurityService object, and the second is an EBankPIN instance. I have already explained how the KerberosSecurityService class works. The EBankPIN class wraps the two keys -- the user's and e-bank's. I'll demonstrate how the EBankPIN class works in the next article in this series. For now, just note that the KerberosKeyManager stores the two objects in class-level variables for future use.

Now let's see how the getKey() method works.

The getKey() method
The KerberosEBank MIDlet will call the getKey() method. The getKey() method shown in Listing 7 takes a byte array, which contains two pieces of information. The first structure is the user's key, which I will use for authentication. The second is a byte array that contains an encrypted structure, which in turn contains the Kerberos session key. If the user's key authenticates successfully, I will decrypt the encKerberosData array, extract the session key from the decrypted data, and return the session key to the calling client MIDlet application.

The getKey() method uses the KerberosSecurityService class to check user authentication. It calls the KerberosSecurityService.isAuthenticated() method to check user authentication. isAuthenticated() returns true if the user is successfully authenticated.

After doing the authentication check, the getKey() method proceeds with decrypting the encKerberosData array. In order to decrypt the array, the getKey() method uses the services of the EBankPIN class. Notice in Listing 7 that the getKey() method calls the EBankPIN.getDecryptedSessionKey() method, passing the encrypted structure along with the method call.

Recall that the EBankPIN class holds the user's and e-bank's keys. The getDecryptedSessionKey() method will decrypt the encrypted structure, extract the session key from the decrypted structure, and return the key to the calling application. I'll discuss the programming logic of EBankPIN class in the next article of this series.

I have included the UserPIN and EBankPIN classes in the source code download of this article. This is only to help readers compile the code that I have developed so far. I'll develop the actual code for these two classes in the next article of this series.


Wrap up

In this article, I thoroughly discussed the life cycle of a Java Card application. I used the Kerberos-based e-bank application as a sample to demonstrate the working of Java Card applets. I discussed how to install a Java Card application onto a Java Card, and also explained how a client application uses the services offered by a Java Card application. Finally, I provided the sequence of events that takes place when a client invokes a Java Card application.

I'll start the next article by demonstrating how the UserPIN and EBankPIN classes work. I will then introduce and explain SATSA and demonstrate how to use the SATSA classes. I will also implement the J2ME-based client-side functions of the KerberosEBank application to demonstrate how to use SATSA.



Download

NameSizeDownload method
wi-satsasource.zip17 KB HTTP

Information about download methods


Resources

Learn

Get products and technologies

About the author

Faheem Khan is an independent software consultant specializing in enterprise application integration (EAI) and B2B solutions. Contact Faheem at fkhan872@yahoo.com.

Comments



Trademarks

static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=1
Zone=Java technology
ArticleID=91885
ArticleTitle=Securing Java Card applications, Part 1: Building a Kerberos-enabled J2ME application
publish-date=08162005
author1-email=fkhan872@yahoo.com
author1-email-cc=