Contents Previous Next Index

Chapter   3

Developing RMI Applications for the Java Card Platform


This chapter describes how to write RMI applications for the Java Card platform. In this release, you can run and debug Java Card RMI applications in the C-language Java Card RE and the Java Card WDE.

Steps for Developing an RMI Applet for the Java Card Platform

The main steps for developing an RMI applet for the Java Card platform include:

For a simple applet, the main class of the applet could also be the class implementing the remote interface.

Generating Stubs

The Java Card RMI Client framework requires stubs only when the remote_ref_with_class format is used for passing remote references. These stubs of remote classes of applets must be pre-generated and available on the client. When the remote_ref_with_interfaces format is used, stubs are not necessary.

In this example, Sun Microsystems, Inc.’s standard RMI Compiler (rmic) is used to generate these stubs.

The command line to run the rmic is:

rmic -v1.2 -classpath <path> -d <output_dir> <class_name>  

where:

<path> includes the path to the remote class

<output_dir> is the directory in which to place the resulting stubs

<class_name> is the name of the remote class

The -v1.2 flag is required by the RMI client framework for the Java Card platform.

The rmic must be called for each remote class in your applet.


Note – Some versions of the rmic do not generate stubs for remote classes which do not list remote interfaces in their implements clause. If you encounter this situation, list at least one remote interface in the implements clause for each remote class in your project. It is permissible for such a remote class to use a remote interface from a superclass.

The file javacardframework.jar is provided in version 2.2.1 of the Development Kit for the Java Card platform (“Java Card Development Kit”). This jar file contains compiled implementations of packages javacard.framework, javacard.framework.service, and javacard.security. Classes in these packages might be referenced by Java Card RMI applets and thus might be needed by the rmic to generate stubs.

Running a Java Card RMI Applet

The server part (that is, the Java Card RMI-enabled applet) can be run on both CREF and Java Card platform Workstation Development Environment (“Java Card WDE”).

To run the applet on CREF, the standard procedures apply: the applet must be installed first, using the installer applet. After the applet is installed, the EEPROM state can be saved and used to run CREF against the Java Card RMI client.

The simplest way to run a Java Card RMI-enabled applet on the Java Card WDE is to add it to the WDE configuration file on the first line. This uses the fact that the Java Card WDE automatically installs the first applet on “power up”. The Java Card WDE is a very convenient environment to debug Java Card RMI applets. Of course, all of the standard limitations (such as absence of firewall support) apply.

Running the Java Card RMI Client Program

The client program can be compiled using javac or your favorite IDE. The compiler will require that the remote interfaces for your applet be present in your classpath.

Running the client program requires that:

For a description of the opencard.properties file, refer to OCF documentation. A sample opencard.properties file is located in the samples/src/demo directory Java Card Development Kit, version 2.2.1, and can be used without modification.

For a sample command line to run a client program, refer to the file rmidemo or rmidemo.bat in this directory.

The opencard.properties file supplied in the samples/src/demo directory configures the client to use the Java Card WDE or CREF. The RMI client can also use a TLP224-compliant card reader connected to a serial port.

To configure the client to use the card reader, change the OpenCard.terminals line in the opencard.properties file to the following:

OpenCard.terminals =com.sun.javacard.comterminal.ComTerminalFactory|com|COM|<port#> 

where <port#> is the name of the serial port, such as COM1.

Basic Example

The basic example that we will use is the Java Card platform equivalent of “Hello World”, which is a program that manages a counter remotely, and is able to decrement the value of the counter, increment the value of the counter, and return the value of the counter.

The Main Program

As for any Java Card RMI program, the first thing that we need to do is to define the interface that will be used as contract between the server (that is, the Java Card technology-based application) and its clients (that is, the terminal applications):

package examples.purse ; 
import java.rmi.* ; 
import javacard.framework.* ; 
public interface Purse extends Remote { 
  public static final short MAX_AMOUNT = 400 ; 
  public static final short REQUEST_FAILED = 0x0102 ; 
  public short debit(short amount) throws RemoteException, UserException; 
  public short credit(short amount) throws RemoteException, 
      UserException ; 
  public short getBalance() throws RemoteException, UserException ; 
} 

This interface is a typical Java Card RMI interface:

Implement a Remote Interface

The next step consists in providing an implementation for this interface. This implementation will run on a Java Card platform, and it therefore needs to use only features that are supported by a Java Card platform:

package examples.purse ; 
import javacard.framework.* ; 
import javacard.framework.service.* ; 
import java.rmi.* ; 
public class PurseImpl extends CardRemoteObject implements Purse 
{ 
  private short balance ; 
  PurseImpl() 
  { 
    super() ; 
    balance = 0 ; 
  } 
  public short debit(short amount) throws RemoteException, UserException 
  { 
    if (( amount < 0 )||( amount > MAX_AMOUNT )) 
      UserException.throwIt(REQUEST_FAILED) ; 
    balance -= amount ; 
    return balance ; 
  } 
  public short credit(short amount) throws RemoteException, UserException 
  { 
    if (( amount < 0 )||( balance < amount )) 
      UserException.throwIt(REQUEST_FAILED) ; 
    balance -= amount ; 
    return balance ; 
  } 
  public short getBalance() throws RemoteException, UserException 
  { 
    return balance ; 
  } 
} 

Here, the remote interface is the Purse interface, which declares the remotely accessible methods. By implementing this interface, the class establishes a contract between itself and the compiler, by which the class promises that it will provide method bodies for all the methods declared in the interface:

public class PurseImpl extends CardRemoteObject implements Purse 

The class also extends the javacard.framework.service.CardRemoteObject class. This class provides our class with basic support for remote objects, and in particular the ability to export and/or unexport an object.

Define the Constructor for the Remote Object

The constructor for a remote class provides the same functionality as the constructor of a non-remote class: it initializes the variables of each newly created instance of the class.

In addition, the remote object instance will need to be “exported”. Exporting a remote object makes it available to accept incoming remote method requests. By extending CardRemoteObject, a class guarantees that its instances are exported automatically upon creation on the card.

If a remote object does not extend CardRemoteObject (directly or indirectly), you will need to explicitly export the remote object by calling the CardRemoteObject.export method in the constructor of your class (or in any appropriate initialization method). Of course, this class must still implement a remote interface.

To review:

The implementation class for a remote object needs to:

Provide an Implementation for Each Remote Method

The implementation class for a remote object contains the code that implements each of the remote methods specified in the remote interface. For example, here is the implementation of the method that debits the purse:

public short debit(short amount) throws RemoteException, UserException 
   
    if (( amount < 0 )||( balance < amount ) 
      UserException.throwIt(REQUEST_FAILED) ; 
    balance -= amount ; 
    return balance ; 
  } 

An operation is only allowed if the value of its parameter is compatible with the current state of the purse object. In this particular case, the application only checks that the amounts handled are positive and that the balance of the purse always remains positive.

In Java Card RMI, the arguments to, and return values from, remote methods are restricted. The main reason for this limitation is that the Java Card platform does not support object serialization. The rules for the Java Card platform are:

On the other hand, object passing in Java Card RMI follows the normal RMI rules:


Note – Even though the semantics of the Java Card platform transient arrays is somewhat similar to transient fields in the Java programming language, different rules apply; its contents are copied in Java Card RMI and passed by value when it is returned from a remote method.

A class can define methods not specified in a remote interface, but they can only be invoked on-card within the Java Card VM and cannot be invoked remotely.

Building an Applet

In version 2.2.1 of the Java Card platform (as in version 2.1), all applications must include a class that inherits from javacard.framework.Applet, which will provide an interface with the outside world. This also applies to applications which are based on remote objects, for two main reasons:

For conversion, an applet should be assigned with an AID known on the client side.

This is the basic code for such an applet:

package examples.purse ; 
import javacard.framework.* ; 
import javacard.framework.service.* ; 
import java.rmi.*; 
public class PurseApplet extends Applet 
{ 
  private Dispatcher dispatcher ; 
  private PurseApplet() 
  { 
    // Allocates an RMI service and sets for the Java Card platform 
    // the initial reference 
    RemoteService rmi = new RMIService( new PurseImpl() ) ; 
    // Allocates a dispatcher for the remote service 
    dispatcher = new Dispatcher((short)1) ; 
    dispatcher.addService(rmi, Dispatcher.PROCESS_COMMAND) ; 
  } 
  public static void install(byte[] buffer, short offset, byte length) 
  { 
    // Allocates and registers the applet 
    (new PurseApplet()).register() ; 
  } 
  public void process(APDU apdu) 
  { 
    dispatcher.process(apdu) ; 
  } 
} 
Preparing and Registering the Remote Object

The PurseApplet constructor contains the initialization code for the remote object. First, a javacard.framework.service.RMIService object needs to be allocated. This service is an object that knows how to handle all the incoming APDU commands related to the Java Card RMI protocol. The service needs to be initialized to allow remote methods on an instance of the PurseImpl class. A new instance of PurseImpl is created, and is specified as the initial reference parameter to the RMIService constructor as shown in the code snippet below. The initial reference is the reference that is made public by an applet to all its clients. It is used as a bootstrap for a client session, and is similar to that registered by a Java RMI server to the Java Card RMI registry.

RemoteService rmi = new RMIService( new PurseImpl() ) ; 

Then, a dispatcher is created and initialized. A dispatcher is the glue amongst several services. In our example, the initialization is quite simple, since there is a single service to initialize:

dispatcher = new Dispatcher((short)1) ; 
dispatcher.addService(rmi, Dispatcher.PROCESS_COMMAND) ; 

Finally, the applet needs to register itself to the Java Card RE, to be made selectable. This is done in the install method, where the applet constructor is invoked and immediately registered:

(new PurseApplet()).register() ; 
Processing the Incoming Commands

The processing of the incoming commands is entirely delegated to the Java Card RMI service, which knows how to handle all the incoming requests. The service will also implement a default behavior for the handling of any request that it does not recognize. In Java Card RMI, there are two kinds of requests that can be handled:

To perform these actions, the service will need privileged access to some resources that are owned by the Java Card RE (in particular to perform the method invocation). The applet delegates processing to the Java Card RMI service from its process method as follows:

dispatcher.process(apdu) ; 

This corresponds to the simplest case, in which the handling of commands is entirely delegated to the dispatcher.

Writing a Client

The client application runs on a terminal supporting a Java Virtual machine environment such as J2SE or J2ME.

The PurseClient application interacts with the remote stub classes generated by a stub generation tool and the Java Card platform-specific information managed by the Java Card platform client-side framework com.sun.javacard.javax.smartcard.rmiclient package.

The example we describe here uses standard Java RMIC compiler generated client side stubs. The client application as well as the Java Card client-side framework rely on the Open Card Framework (OCF) for managing and communicating with the card reader and the card on which the Java Card technology-based applet (“Java Card applet”) PurseApplet resides. This makes the client application very portable on J2SE platforms.

The OCF can be downloaded from the OCF Web site:

http://www.opencard.org/index-downloads.html

The following shows a very simple PurseClient application which is the client application of the Java Card technology-based program PurseApplet:

import opencard.core.service.* ; 
import examples.purse.* ; 
import com.sun.javacard.javax.smartcard.rmiclient.* ; 
import com.sun.javacard.ocfrmiclientimpl.* ; 
import javacard.framework.UserException ; 
 
  public class PurseClient extends java.lang.Object { 
 
    /** Creates new PurseClient */ 
    public PurseClient() { 
    } 
    public static void main(java.lang.String[] argv) { 
         
      // arg[0] contains the debit amount 
      short debitAmount = (short) Integer.parseInt(argv[0]) ;    
      try { 
        // initialize OCF 
        SmartCard.start() ; 
        // wait for a smartcard 
          CardRequest cr = new CardRequest (CardRequest.NEWCARD, 
              null,OCFCardAccessor.class); 
          SmartCard myCard = SmartCard.waitForCard ( cr ) ; 
          // obtain an RMI Card Accessor CardService for the  
          // Java Card platform 
          CardAccessor myCS = (CardAccessor) 
            myCard.getCardService(OCFCardAccessor.class, true) ; 
          // create an RMI connector instance for the Java Card platform 
          JavaCardRMIConnect jcRMI = new JavaCardRMIConnect( myCS ) ; 
          // select the Java Card applet 
          byte[] appAID = new byte[] {0x01,0x02,0x03,0x04,0x05,0x06,0x07, 0x08};     
          jcRMI.selectApplet( appAID ) ; 
          // obtain the initial reference to the Purse interface 
          Purse myPurse = (Purse) jcRMI.getInitialReference() ; 
          // debit the requested amount 
          try { 
            short balance = myPurse.debit ( debitAmount ) ; 
            }catch ( UserException jce ) { 
              short reasonCode = jce.getReason() ; 
              // process UserException reason information 
            }  
              // display the balance to user 
        }catch (Exception e) { 
          e.printStackTrace() ; 
        } finally { 
          try { 
           SmartCard.shutdown() ; 
            }catch (Exception e) { 
            e.printStackTrace() ; 
          } 
        } 
    } 
}  
Initializing and Shutting Down OCF

The client application needs to initialize the OCF classes on the terminal. The following code shows this as well as how it is shut down:

try { 
        // initialize OCF 
        SmartCard.start() ; 
               
        // the main client work is performed here 
        // ... 
}catch (Exception e) { 
        e.printStackTrace() ; 
} finally { 
        try{ 
        SmartCard.shutdown() ; 
        }catch (Exception e) { 
           e.printStackTrace() ; 
        } 
    } 
Obtaining and Using the OCFCardAccessor Object

To access the Java Card applet using remote methods, the client application needs to obtain an instance of the CardAccessor interface. The OCFCardAccessor class implements the CardAccessor interface and is an OCF card service.

The code below shows how the client interacts with the OCF services. Note that the client specifies that it is requesting a SmartCard object on any newly inserted card which supports the OCFCardAccessor class:

// wait for a smartcard 
CardRequest cr = new CardRequest  
    (CardRequest.NEWCARD,null,OCFCardAccessor.class) ; 
          SmartCard myCard = SmartCard.waitForCard ( cr ) ; 
 
// obtain a Java Card API Accessor for OCF 
CardAccessor myCS = (CardAccessor) 
          myCard.getCardService(OCFCardAccessor.class, true) ; 

The CardAccessor interface is a platform and framework independent interface which is used by the RMI framework for the Java Card platform to communicate with the card. The CardAccessor object is then provided as a parameter during construction of the JavaCardRMIConnect class to initiate an RMI dialogue for the Java Card platform as shown below:

// create an RMI connection object for the Java Card platform 
JavaCardRMIConnect jcRMI = new JavaCardRMIConnect( myCS ) ;  
Selecting the Java Card Applet and Obtaining the Initial Reference

In order to invoke methods on the remote objects of the Java Card applet PurseApplet on the card it must first be selected using the AID:

// select the Java Card applet 
  byte[] appAID = new byte[] {0x01,0x02,0x03,0x04,0x05,0x06,0x07, 0x08} ; 
  jcRMI.selectApplet( appAID ) ; 

Then, the client needs to obtain the initial reference remote object for PurseApplet. JavaCardRMIConnect returns an instance of a stub class corresponding to the PurseImpl class on the card which implements the Purse interface. The client application knows beforehand that the PurseApplet’s initial remote reference implements the Purse interface and therefore casts it appropriately:

// obtain the initial reference to the Purse interface 
Purse myPurse = (Purse) jcRMI.getInitialReference() ; 
Using Remote Objects in Remote Method Invocations

The client can now invoke remote methods on the initial reference object. The remote methods are declared in the Purse interface. The code below shows the client invoking the debit method. Note how an UserException exception thrown by the remote method is caught by the client code in a normal Java program style.

// debit the requested amount 
try { 
    short balance = myPurse.debit ( debitAmount ) ; 
    }catch ( UserException jce ) { 
    short reasonCode = jce.getReason() ; 
    // process on card exception reason information 
} 
Generate the Stubs

The client side scenario outlined above uses RMIC generated stubs for the remote classes. RMIC is the Java RMI stub compiler. For the client application PurseClient to execute correctly on the terminal, it needs these remote stub classes and the remote interface class files it uses to be accessible in its classpath.

The stub class PurseImpl_Stub.class for the PurseImpl class is produced by running the standard JDK1.2 or JDK1.3 RMIC compiler. For example, when in the examples/purse directory, enter:

Solaris and Linux platforms:

rmic -classpath ../..;$JC_HOME/lib/javacardframework.jar -d ../.. -v1.2 examples.purse.PurseImpl 

Microsoft Windows 2000 platform:

rmic -classpath ../..;%JC_HOME%/lib/javacardframework.jar -d ../.. -v1.2 examples.purse.PurseImpl 

This produces a stub class called examples.purse.PurseImpl_Stub.

Thus, for PurseClient to run correctly on the terminal, the following files must be present in the examples/purse directory and accessible via its classpath or from class loaders:

PurseImpl_Stub.class 
Purse.class 

Card Terminal Interaction

When a Java Card technology-enabled smartcard is first inserted into the card reader, the card sends an ATR (Answer to Reset) to the terminal. The Open Card Framework uses this to help locate the Card Service which may be suitable. In our case, the OCFCardAccessorFactory, which has been previously registered with OCF, responds with OCFCardAccessor support. FIGURE 1 illustrates this schematically.

This figure is described in the text.

FIGURE 1  –  Smart card sends an ATR to the terminal

When the PurseClient application calls the selectApplet method of JavaCardRMIConnect, it sends a SELECT APDU Command to the card via the OCFCardAccessor object. This results in a File Control Information (FCI) APDU response from the RMIService instance of PurseApplet on the card in a TLV (Tag Length Value) format which includes the initial reference remote object information. FIGURE 2 illustrates this schematically.

This figure is described in the text.

FIGURE 2  –  Terminal sends a SELECT command to the smart card. The card returns an FCI to the terminal

Later, when the PurseClient application calls the debit method of the remote interface Purse, the PurseImpl_Stub object sends an invoke command to the card via the OCFCardAccessor object, identifying the remote object reference, interface, method and parameter data for method invocation. The RMIService instance of PurseApplet unmarshalls this information and invokes the debit method of the PurseImpl instance and returns the return value in the response APDU. FIGURE 3 illustrates this schematically.

This figure is described in the text.

FIGURE 3  –  Terminal sends an INVOKE command to the smart card. The card returns a value to the terminal.

Adding Security

This first example is extremely simple and is not realistic. In particular, it does not include any kind of security. Users are not authenticated, and no transport security is provided. Of course, every smart card that implements the Java Card platform will include such security mechanisms, since they are central to Java Card technology.

In the following section, you will see how to add security support to the Purse example.

The Purse interface in the package examples.securepurse is similar to the Purse interface in the previous code sample. In addition, it might include reason codes for exceptions to report security violations to the terminal. It should be replaced with examples.securepurse. The interface does not include any implementation, which means that, in particular, it does not include any support for security.

package examples.securepurse ; 
import javacard.framework.* ; 
import javacard.framework.service.* ; 
import java.rmi.* ; 
public class SecurePurseImpl implements Purse 
{ 
  private short balance ; 
  private SecurityService security ; 
  SecurePurseImpl(SecurityService security) 
  { 
    this.security = security ; 
  } 
 
public short debit(short amount) throws RemoteException, UserException 
  { 
  if 
  ((!security.isCommandSecure(SecurityService.PROPERTY_INPUT_INTEGRITY)) 
  || 
  (!security.isAuthenticated(SecurityService.PRINCIPAL_CARDHOLDER))) 
    UserException.throwIt(REQUEST_FAILED) ; 
    if (( amount < 0 )|| ( balance < amount )) 
      UserException.throwIt(REQUEST_FAILED) ; 
    balance -= amount ; 
    return balance ; 
  } 
 
public short credit(short amount) throws RemoteException, UserException 
  { 
    if 
    ((!security.isCommandSecure(SecurityService.PROPERTY_INPUT_INTEGRITY)) 
    || 
    (!security.isAuthenticated(SecurityService.PRINCIPAL_APP_PROVIDER))) 
      UserException.throwIt(REQUEST_FAILED) ; 
    if (( amount < 0 )||( amount > MAX_AMOUNT )) 
      UserException.throwIt(REQUEST_FAILED) ; 
    balance += amount ; 
    return balance ; 
  } 
 
public short getBalance() throws RemoteException, UserException 
  { 
    if ((!security. isAuthenticated(SecurityService.PRINCIPAL_CARDHOLDER))  
    && 
    (!security.isAuthenticated(SecurityService.PRINCIPAL_APP_PROVIDER))) 
      UserException.throwIt(REQUEST_FAILED) ; 
    return balance ; 
  } 
} 

The applet keeps its original organization, but it also includes additional code that is dedicated to the management of security.

Initialize a Security Service

In this example, basic security services (principal identification and authentication, secure communication channel) are provided by an object that implements the SecurityService interface. Since a generic remote object should not be dependent on a particular kind of security service, it should take a reference to this object as a parameter to its constructor. This is exactly what happens here, where the reference to the object is stored in a dedicated private field:

private SecurityService security ; 

The SecurityService interface is part of the extended application development framework and offers an API that can then be used to check on the current security status.

Use the Service to Check the Current Security Status

In the example, this required security behavior for the applet is assumed:

The SecurityService provides methods and constants that allow the implementation to perform such checks. For instance, the code for the checks on the debit method is:

if 
((!security.isCommandSecure(SecurityService.PROPERTY_INPUT_INTEGRITY)) 
  ||   
  (security.isAuthenticated(SecurityService.ID_CARDHOLDER))) 
    UserException.throwIt(REQUEST_FAILED) ; 

This code is quite self-explanatory. If one of the two conditions is not satisfied, then the remote object throws an exception. This exception is caught by the dispatcher and forwarded to the client.

Implementing a Security Service

package com.sun.javacard.samples.SecureRMIDemo ; 
import javacard.framework.* ; 
import javacard.framework.service.* ; 
 
public class MySecurityService extends BasicService implements SecurityService { 
// list IDs of known parties... 
    private static final byte[] PRINCIPAL_APP_PROVIDER_ID = {0x12, 0x34} ; 
    private static final byte[] PRINCIPAL_CARDHOLDER_ID = {0x43, 0x21} ; 
    private OwnerPIN provider_pin, cardholder_pin = null ; 
    // and the security-related session flags 
    ...    
    public MySecurityService() { 
        // initialize the PINs 
        ... 
    } 
     public boolean processDataIn(APDU apdu) { 
      if(selectingApplet()) { 
           // reset all flags 
            ... 
      } 
      else { 
          return preprocessCommandAPDU(apdu); 
      } 
  } 
  public boolean isCommandSecure(byte properties) throws ServiceException { 
      // return the value of appropriate flag 
      .... 
  } 
  public boolean isAuthenticated(short principal) throws ServiceException { 
      // return the value of appropriate flag 
      .... 
  } 
  private byte authenticated ; 
  private boolean preprocessCommandAPDU(APDU apdu) { 
      receiveInData(apdu) ; 
      if(checkAndRemoveChecksum(apdu)) { 
        // set DATA_INTEGRITY flag 
      } 
      else { 
          // reset DATA_INTEGRITY flag 
      } 
      return false;   // other services may also preprocess the data 
  } 
  private boolean checkAndRemoveChecksum(APDU apdu) { 
          // remove the checksum 
          // return true if checksum OK, false otherwise 
  } 
  public boolean processCommand(APDU apdu) { 
      if(isAuthenticate(apdu)) { 
          receiveInData(apdu) ; 
          // check PIN 
          // set AUTHENTICATED flags 
          return true;     //  processing of the command is finished 
      } 
      else { 
           return false ;  // this command was addressed to another 
                           // service - no processing is done 
      } 
  } 
  public boolean processDataOut(APDU apdu) { 
      // add checksum to outgoing data 
      return false;  // other services may also postprocess outgoing data 
  } 
  private boolean isAuthenticate(APDU command) { 
              // check values of CLA and INS bytes  
  }  
} 

Building an Applet

The supporting applet also needs to undergo some significant changes, in particular regarding the initialization of the remote object:

package examples.securepurse ; 
import javacard.framework.* ; 
import javacard.framework.service.* ; 
import java.rmi.* ; 
import com.sun.javacard.samples.SecureRMIDemo.MySecurityService ; 
 
public class SecurePurseApplet extends Applet 
{ 
  Dispatcher dispatcher ; 
  private SecurePurseApplet() 
  { 
    SecurityService sec ; 
    // First get a security service 
    sec = new MySecurityService() ; 
    // Allocates an RMI service for the Java Card platform and  
    // sets the initial reference 
    RemoteService rmi = new RMIService( new SecurePurseImpl(sec) ) ; 
    // Allocates and initializes a dispatcher for the remote object 
    dispatcher = new Dispatcher((short)2) ; 
    dispatcher.addService(rmi, Dispatcher.PROCESS_COMMAND) ; 
    dispatcher.addService(sec, Dispatcher.PROCESS_INPUT_DATA) ; 
  } 
  public static void install(byte[] buffer, short offset, byte length) 
  { 
    // Allocates and registers the applet 
    (new SecurePurseApplet()).register() ; 
  } 
  public void process(APDU apdu) 
  { 
    dispatcher.process(apdu) ; 
  } 
} 

The security service that is used by the remote object needs to be initialized at some point. Here, this is done in the constructor for the SecurePurseApplet:

sec = new MySecurityService() ; 

The initialization then goes on with the initialization of the Java Card RMI service. The only new thing here is that the remote object being allocated and set as the initial reference is now a SecurePurseImpl:

RemoteService rmi = new RMIService( new SecurePurseImpl(sec) ); 

Next, we need to initialize the dispatcher. Here, it needs to not only dispatch simple Java Card RMI requests, but also to dispatch security-related requests (such as EXTERNAL AUTHENTICATE). In fact, the security service will handle these requests directly. We first need to allocate a dispatcher and to inform it that it will delegate commands to two different services:

dispatcher = new Dispatcher((short)2); 

Then, the services are “registered” with the dispatcher: the security service as a service that performs pre-processing operations on incoming commands, and the Java Card RMI service as a service that processes the command requested:

dispatcher.addService(rmi, Dispatcher.PROCESS_COMMAND) ; 
dispatcher.addService(sec, Dispatcher.PROCESS_INPUT_DATA) ; 

The rest of the class (that is, the install and process methods) remain unchanged.

Writing a Client

The driver client application itself only changes minimally to account for the authentication and integrity needs of SecurePurseApplet. It does additionally need to interact with the user for identification. Hence, a subclass of OCFCardAccessor needs to be developed which provides these additional interactions and the transport filtering required.

Here is the new SecurePurseClient application:

package examples.securepurseclient ; 
 
import com.sun.javacard.javax.smartcard.rmiclient.* ; 
import com.sun.javacard.clientsamples.securepurseclient.SecureOCFCardAccessor ; 
import opencard.core.service.* ; 
import examples.securepurse.* ; 
import javacard.framework.UserException ; 
 
public class SecurePurseClient extends java.lang.Object { 
 
// need to authenticate user with the secure purse applet 
// prompt user for account and password information 
// to initialize user key and card info from database 
private final static short PRINCIPAL_CARDHOLDER_ID=0x4321 
 
    /** Creates new SecurePurseClient */ 
    public SecurePurseClient() { 
    } 
    public static void main(java.lang.String[] argv) {    
        // arg[0] contains the debit amount 
        short debitAmount = (short) Integer.parseInt(argv[0]) ; 
        try { 
          // initialize OCF 
          SmartCard.start() ; 
          // wait for a smartcard 
          CardRequest cr = new CardRequest ( CardRequest.NEWCARD, 
                null, SecureOCFCardAccessor.class ) ; 
          SmartCard myCard = SmartCard.waitForCard ( cr ); 
          // obtain a custom RMI CardAccessor class for  
          // the Java Card platform: SecureOCFCardAccessor 
          SecureOCFCardAccessor myCS = 
                  (SecureOCFCardAccessor) myCard.getCardService(  
                                SecureOCFCardAccessor.class, true ) ; 
 
            //create RMI connection for the Java Card platform 
            JavaCardRMIConnect jcRMI = new JavaCardRMIConnect(myCS) ; 
            // select the Java Card applet 
            byte[] appAID = new byte[] {0x01,0x02,0x03,0x04,0x05,0x07} ;   
            jcRMI.selectApplet( appAID ); 
 
            if (! myCS.authenticateUser( PRINCIPAL_CARDHOLDER_ID )) { 
              // handle error 
             } 
            // obtain the initial reference to the Purse interface 
            Purse myPurse = (Purse) jcRMI.getInitialReference() ; 
            // debit the requested amount 
            try { 
                // the debit invocation command will be signed 
                // by SecureOCFCardAccessor 
                short balance = myPurse.debit ( debitAmount ) ; 
            }catch ( UserException jce ) { 
                    short reasonCode = jce.getReason() ; 
                    // process on card exception reason information 
            } 
            // display the balance to user 
        }catch (Exception e) { 
          e.printStackTrace(); 
        } finally { 
            try{ 
            SmartCard.shutdown(); 
            }catch (Exception e) { 
                 e.printStackTrace(); 
            } 
        } 
    } 
} 

Note how the SecureOCFCardAccessor instance is now obtained instead of OCFCardAccessor:

CardRequest cr = new CardRequest ( CardRequest.NEWCARD, 
                      null, SecurePurseClientCardService.class ) ; 
SmartCard myCard = SmartCard.waitForCard ( cr ); 
    // obtain a customized Card Accessor class: SecureOCFCardAccessor 
    SecureOCFCardAccessor myCS = 
    (SecureOCFCardAccessor) myCard.getCardService(  
        SecureOCFCardAccessor.class, true ) ; 

An extra step to authenticate with the SecurePurseApplet after selectApplet has been added. This invokes a new method in SecureOCFCardAccessor to interact with the card using the users credentials:

if (! myCS.authenticateUser( PRINCIPAL_CARDHOLDER_ID )) { 
    // handle error 
} 

The rest of SecurePurseClient is the same as PurseClient.

Writing a Custom SecureOCFCardAccessor Class

The SecurePurseClient application uses a subclass of OCFCardAccessor called SecureOCFCardAccessor to perform user authentication functions and to sign every message sent thereafter for integrity purposes:

package examples.securepurseclient; 
 
import opencard.core.terminal.* ; 
public class SecureOCFCardAccessor extends  
              com.sun.javacard.ocfrmiclientimpl.OCFCardAccessor { 
      /** Creates new SecureOCFCardAccessor */ 
    public SecureOCFCardAccessor() { 
    } 
  public byte[] exchangeAPDU( byte[] sendData ) 
throws java.io.IOException { 
         
        byte[] macSignature = null ; 
        byte[] dataWithMAC = new byte[ sendData.length + 4 ] ; 
         
        // sign the sendData data using session key         
        // sign the data in commandBuffer using the user's session key 
         
        // add generated MAC signature to data in buffer before sending 
         
        return super.exchangeAPDU( dataWithMAC ) ; 
    } 
    boolean authenticateUser( short userKey ) { 
        byte[] externalAuthCommand = null ; 
         
        // build and send the appropriate commands to the 
        // applet to authenticate the user using the user Key 
        // and additional info provided 
        try { 
          byte[] response = super.exchangeAPDU ( externalAuthCommand ) ; 
            // ... 
         }catch (Exception e) { 
            // analyze 
            return false ; 
        }    
        // Then compute the session key for later use 
        return true; //successful authentication 
    } 
} 

As shown above, the SecureOCFCardAccessor class introduces the authenticateUser method to send APDU Commands to the SecurePurseApplet on the card to authenticate the user described by the userKey parameter and other parameters and to compute a transport key. It invokes super.sendCommandAPDU method to send the command without modification.

This custom Java Card RMI Card Accessor class also re-implements the exchangeAPDU method declared in a superclass OCFCardAccessor to sign each message before it is sent out by super.exchangeAPDU.

 


Contents Previous Next Index Application Programming Notes
Java Card Platform, Version 2.2.1