How to write OpenCard card services for Java Card applets

Use different smart cards with the same application -- here's how!

The OpenCard Framework allows you to develop end-to-end solutions using smart cards that are not bound to one platform, card, or application. OpenCard achieves this with an architecture that provides two primary subsystems, one for card terminals and one for card services. Card terminals are devices you insert smart cards, Java Rings, and the like, into. Card services are used by an application to communicate with the application on the card inserted into a terminal. For inserted cards, OpenCard can automatically select and load the right card service implementation.

In this article, we'll develop a card service that demonstrates how Java Card applications can be used to provide secure portable storage to applications running on your host system -- either your PC, Unix workstation, network computer (NC), or set-top. We've run OpenCard on all these platforms successfully. OpenCard bridges the void left by other solutions that only run on single operating systems.

Let's start with a brief overview of OpenCard. Figure 1 below shows the components that are part of the OpenCard Framework.

Figure 1: The OpenCard Framework and the components that can be plugged into it

The OpenCard Framework integrates CardTerminal classes and CardService classes and offers a standardized, high-level interface to applications. Card terminal manufacturers who want to make their terminals available to OpenCard applications need to provide a CardTerminal class, which encapsulates card terminal behavior, and a CardTerminalFactory class. The card terminal factory has to be registered with the card terminal registry, which keeps track of all card terminals to the OpenCard Framework and will be used by the Framework to create CardTerminal instances when the Framework is initialized. Card services offer smart-card functionality to application developers via high-level interfaces. Smart-card manufacturers have to provide CardService classes encapsulating the behavior of their smart cards and a CardServiceFactory class. The card service factory must be registered with the OpenCard Framework and is thereafter used by the Framework to instantiate card services.

Assuming we have a smart card that we want to use via OpenCard, we, as card developers, must provide at least one card service for our smart card as well as a "factory" that can represent the card service for the Framework. Thus, we enable application developers to use our smart card in their OpenCard applications.

card services

As you can see in the image above, there may be several instances of card services per card, owned by different threads. A card service offers a certain functionality of a card to the application developer via a high-level interface.

OpenCard defines interfaces for standard functions, such as filesystem access or generation of digital signatures. Card services for cards that offer such functions should implement these interfaces. For specialized cards, dedicated interfaces may be defined -- losing interoperability, of course.

card channel

To communicate with a smart card in a card terminal, card services use CardChannel objects, which represent a communication link to the smart card and offer methods for sending commands to smart cards and for receiving the responses. Concurrent access of card services to the card via a card channel is scheduled by the CardServiceScheduler, which serializes the access of different services to the card channel.

Card applet proxies

For ISO filesystem cards that have a fixed set of commands for accessing files on the card, OpenCard defines the FileSystemCardService interface. However, Java Cards are much more flexible than conventional filesystem-oriented smart cards. They may contain a set of different applets, each supporting a different card applet-specific command set. The only common properties of these applets are that they may be identified by an application identifier (AID), selected, and once selected, can process APDUs (application protocol data units). (For more on APDUs, see the section devoted to this topic in "Smart cards: A primer".)

Because Java Cards are more flexible than other smart cards (for example, filesystem cards) we need a more flexible concept for accessing and using Java Cards or, to be more exact, the applets on Java Cards. We use card applet proxies representing the applets on the card. Proxies are card applet-specific: each card applet proxy class belongs to a particular Java Card applet. Each proxy class has to know the application identifier of the card applet to communicate with and the protocol for interaction with that card applet.

Proxies are the most suitable method for interacting between applications and applets on Java Cards: An application obtains a proxy card service that represents the card applet on the card. The application uses high-level methods offered by the proxy. Whenever the application invokes a proxy method, the proxy starts communication with the card applet on the card and generates some result, which it returns to the application.

Figure 2: Interaction of application, card applet proxy, and Java Card. The proxy conducts the protocol with the card on behalf of the application.

Card applet proxies may be used in multithreaded programs. This means they may be instantiated several times by different threads so that concurrent access by different instances to the card must be serialized. As there might also be several instances of the same card applet proxy class, it must be possible to share the associated card applet's state so that the different proxy instances properly interact.

As specialized card services, before sending APDUs, card applet proxies must allocate a card channel for communication with the Java Card from the card service scheduler -- like any other card service. (As mentioned above, a card channel represents a communication link to the card, and the card service scheduler serializes concurrent access to the card channel.) If the card channel has already been allocated by another card applet proxy, the threads of activity of card applet proxies trying to allocate it are blocked until the current owner of the channel releases it.

A card channel may hold several state objects at most one for each card applet on the card. The state object represents the state of a card applet on the card. If, for example, we have a card applet that simulates a filesystem, the state would consist of the currently selected directory and information about access conditions. There may be applets that are stateless; their associated proxies don't need a state object (see Figure 3). As access to the channel is synchronized and states can only be obtained from the channel, there is no need for additional synchronization of access to states.

Figure 3: Six card applet proxies accessing one Java Card. Several card applet proxies may be using the same card applet. All proxies are accessing the Java Card via a shared channel. Access to this channel is synchronized by a card service scheduler. For applets that have state, appropriate state objects keeping track of those applets' states can be attached to the channel.

All proxies need to send APDUs to the card applets they represent. This function is implemented in a common base class of all proxies, which we name AppletProxy service. In order to send an APDU to a card applet, the card applet must be selected -- except if it is the currently selected card applet. To avoid unnecessary selections, the proxy services have to keep track of the currently selected card applet. There may exist several instances of proxy services for one open platform card simultaneously. In this case, it must be assured that all proxy services accessing the same card also share the representation of the card's state; that is, the currently-selected card applet.

On a Java Card with several card applets, selection of one applet always causes deselection of another applet, potentially causing the deselected applet to loose its state. This potential state loss requires that applet proxies are notified whenever a card applet -- other than the one they are associated with -- is selected, so that they can update their representation of the associated card applet's state. The notification mechanism works as follows: in the constructor of the base class AppletProxy, each applet proxy registers with the card state object of its associated card as an applet selection listener. As mentioned above, the class AppletProxy, which is the base class of all applet proxy services, offers methods for sending APDUs to the associated card applet to derive classes. These methods implicitly select the associated card applet if it is not already the current applet.

In this case, these methods modify the card state to indicate that now a different card applet has been selected by calling the method setSelectedAppletAID of the card state object. This method updates the selected card applet in the card state and notifies all other card applet proxies associated with card applets on the same card by calling their appletSelection methods. The default implementation of this method in the base class is empty; it must be overwritten in derived card applet proxies if their associated card applets may lose or change their states when other card applets are selected.

Proxy services will usually extend the generic AppletProxy service and use inherited methods for communication with associated card applets. For obtaining and releasing exclusive communication with the card, inherited methods are used as well.

Figure 4: Card applet proxy services accessing the same Java Card. All card applet proxy services accessing the same Java Card share a common card state, which keeps track of the currently-selected card applet. By checking the state for the currently-selected card applet before actually making a selection, card applet proxy services can avoid sending unnecessary select-applet APDUs.

In the next sections, we will show how the card service and card service factory are developed for a card applet on a Java Card.

Tutorial: Implementing and using OpenCard support for a Java card applet

The card applet

For this tutorial, we use a card applet that's quite simple: a business card applet that can hold several business cards, each consisting of a name, title, e-mail address, phone number, and mailing address.

Let's assume the card applet offers the following APDUs for accessing this data.

MethodCLAINSP1P2LcDataLe
Do CHV0x800x010x000x000x08CHV-
Get Name0x800x02index0x01 

-

0x00
Get Title0x800x02index0x02 

-

0x00
Get E-mail Address0x800x02index0x03 

-

0x00
Get Phone Number0x800x02index0x04 

-

0x00
Get Address0x800x02index0x05 

-

0x00
Set Name0x800x03index0x01lengthname

-

Set Title0x800x03index0x02lengthtitle

-

Set E-mail Address0x800x03index0x03lengthe-mail

-

Set Phone Number0x800x03index0x04lengthphone

-

Set Address0x800x03index0x05lengthaddress

-

Using the APDUs listed in the table above, it's possible to read data from the card or write data to the card. However, it's quite inconvenient for application programmers to program the applet at this low level. In the next section, we present an applet-proxy card service that provides an easy-to-use API for access to this card applet.

The applet-proxy card service

In OpenCard Framework, card services provide a high-level, easy-to-use interface to applications. To implement this interface, they encapsulate the protocol that must be carried out with the card. For our example card applet, we may want to have a card service that provides convenient get and set methods for access, which automatically asks for a password if required, so that no unnecessary APDUs for getting data are sent to the card. To decide whether the card-holder verification (CHV) has been performed, or still must be done or repeated because another card applet has been selected in the meantime, we will use a state object that is an instance of the following class:

public class BusinessCardState
{
  boolean chvPerformed_ = false;
  public boolean isCHVPerformed()
  {
    return chvPerformed_;
  }
  public void setCHVPerformed(boolean chvPerformed)
  {
    chvPerformed_ = chvPerformed;
  }
}

As mentioned in the "Card applet proxies" section above, there is a base class called AppletProxy from which all card applet proxy classes should inherit the methods for allocating and releasing a card channel and for communicating with the card.

In the first few lines, as shown below, we define some constants for the return codes that may be received from the card in response to commands sent by the proxy.

public class BusinessCardProxy extends AppletProxy
{  
  protected final static int OK                 = 0x9000;
  protected final static int INVALID_PASSWORD   = 0x7000;
  protected final static int INDEX_OUT_OF_RANGE = 0x8001;
  protected final static int CHV_MISSING        = 0x8002;

Each card applet proxy knows its associated applet's Application Identifier (AID) -- a unique identifier for a card resident applications. It consists a 5-byte prefix which has to be registered with ISO by the application provider and a proprietary extension with a length of 0 to 11 bytes. The application provider is responsible for the uniqueness of the extension within his company -- the AID of the card applet to which it belongs. Let's assume our card applet has the AID 0xD27600002200000062. The following code initializes the Application Identifier object for the business card applet.

1 2 3 Page 1
Page 1 of 3