An Introduction to Java Card Technology - Part 3, The Smart Card Host Application

By C. Enrique Ortiz, September 2003    

Part 1 of this article covered the high-level aspects of Java Card technology - what smart cards are, the elements of a Java Card application, communication aspects, and a summary of the various Java Card technology specifications. Part 2 covered the development aspects of Java Card applets: the typical steps when developing a Java Card application, Sun's Java Card Development Kit, and the Java Card and Java Card RMI APIs. The final part of this article will cover the development aspects of host applications for smart cards.

Elements of a Java Card Application

Keep in mind that Java Card applications are not standalone, but rather part of an end-to-end application:

Figure 1. Typical Components of a Java Card Application
Figure 1. Typical Components of a Java Card Application

A Java Card application typically comprises:

  • The back-end application that provides access to back-office services such as security or electronic-payment information stored in databases. How the back-end application is developed is outside the scope of this article.
  • Off the card, residing in a card terminal, the host application accesses applets on the smart card using one of a number of interfaces for card access, such as the Java Card RMI, the OpenCard Framework API, or the Security and Trust Services API (SATSA).
  • The card reader, card terminal, or card acceptance device, provides the physical interface between the host application and on-card applets.
  • On the card are the Java Card applet, and the Java Card framework. Note that before accessing the applet, the host application must supply credentials and authenticate itself.

Writing a Host Application - Accessing Your Applet

The host application, on the client side, handles communication among the user, the Java Card applet, and the provider's back-end application. The host program accesses the services provided by your applet. It resides on a terminal or card acceptance device such as a workstation, a point of sale (POS) terminal, a cell phone, or a set-top box. Recall that a host and an applet interact by way of a card reader or terminal, using ISO-7816 APDU commands.

Traditionally, reader-side applications have been written in C, but the host program can be written in the Java programming language, or any other, as long as it's prepared to exchange valid ISO-7816 APDU commands with the applet.

Most cell phones deployed today integrate a smart card reader to access the SIM card bundled with it. With the coming introduction of JSR 177, the Security and Trust Services API (SATSA) for J2ME, and the widespread adoption of J2ME devices, we can expect that a variety of host applications will be written using Java technology on mobile devices. The intent of SATSA is to enable a Java Card host application to run on a J2ME-based device. JSR 177 is currently in JCP community review.

Three major APIs are available when you're writing client-side applications: the OpenCard Framework, the Java Card RMI Client API, and the Security and Trust Services API (SATSA). We'll look at each of these in turn.

Introduction to the OpenCard Framework

Smart card vendors typically provide, not only a development kit, but also APIs to support reader-side applications as well as Java Card applets. Many of these vendors support the OpenCard Framework (OCF), a Java-based set of APIs that hide some of the details of interacting with card readers from different vendors.

The OpenCard Consortium is a group of companies driving the definition and adoption of the OpenCard Framework, currently in its 1.2 version. OCF's objective is to provide developers of host-side applications with an API that works across different card-reader vendors.

To accomplish vendor-independence, the OCF defines two software layers:

  • The CardTerminal layer provides the card reader abstractions, such as CardTerminal (representing a physical card reader), APDU, CommandADPU, and ResponseAPDU.

The OCF defines a number of standard card services for you. Two of these are FileAccessCardService and SignatureCardService. A special type is ApplicationManagerCardService, which provides life-cycle management methods to install, register, and remove applets on the card.

When writing a host-side OCF-based application, you basically separate it into two parts:

  • Main application objects that interact with the terminal or reader (initializing OCF, waiting for card insertion, and terminating OCF), and that expose the high-level card-access methods, such as getBalance().
  • An applet proxy that implements the actual low-level channel management and APDU I/O. The Proxy design pattern allows you to expose an object-oriented interface, while hiding the APDU details from the application.
Figure 2. Structure of an OCF Application
Figure 2. Structure of an OCF Application

In summary, a typical OCF application has one or more main objects, which are created on the host, probably on their own threads of execution. These main application objects expose the high-level application-specific calls, which are eventually delegated to the applet proxy. They make use of the SmartCard object, which is the application's entry point to the OCF, enabling the application to initialize and shut down the OCF, and wait for cards to be inserted. The main object can implement a CTListener, which as you'll see soon, provides asynchronous notification of events such as card insertion and removal.

You can write your application using a synchronous or asynchronous model.

In the synchronous model your host application initializes the OCF, then waits for a card to be inserted. Then it executes your main application logic, and when done shuts down the OCF:

...
try {
    // Initialize OCF
    SmartCard.start();
    // Wait for a smart card
    CardRequest cr = new CardRequest(CardRequest.NEWCARD, null,
                                     OCFCardAccessor.class);
    SmartCard myCard = SmartCard.waitForCard(cr);

    // Main client work is done here...
    ...
    
} catch (Exception e){
    // Handle exception
} finally {
    try {
        // Shut down OCF
        SmartCard.shutdown();
    } catch (Exception e) {
        e.printStackTrace();
    }
}
...


Listing 1. Typical Structure of a Synchronous OCF Application

If you prefer to use the asynchronous approach, your class must implement the CTListener interface, and, at initialization, register itself for notification of card-terminal events such as insertion and removal. The following application skeleton starts by initializing the OCF and registering the listener, then defines callback methods for significant events.

public class MyHostSideApp implements CTListener
    ...
    public MyHostSideApp() {
        try {
            // Initialize the framework
            SmartCard.start ();
            // Register this as a Card Terminal Event Listener
            CardTerminalRegistry.getRegistry().addCTListener(this);
        } catch (Exception e) {
            // handle error...
        }
    }

    public void cardInserted(CardTerminalEvent ctEvent) {
        ...
    }

    public void cardRemoved(CardTerminalEvent ctEvent) {
        ...
    }
    ...
}

Listing 2. Typical Structure of an Asynchronous OCF Application

When a card is inserted, the runtime calls the cardInserted() method, and when the card is removed, the runtime calls the cardRemoved() method. In the more fleshed-out listing that follows, inserting a card initiates creation of an applet proxy, and removing the card triggers cleanup. The listing also shows delegation of requests for credit-card balances to the proxy.

import opencard.core.event.CTListener;
import opencard.core.event.CardTerminalEvent;
import opencard.core.service.SmartCard;
import opencard.core.service.CardService;
...

public class MyHostSideApp implements CTListener
{
    public void MyHostSideApp() {
        try {
            // Initialize the framework
            SmartCard.start ();
            // Register this as a Card Terminal Event Listener
            CardTerminalRegistry.getRegistry().addCTListener(this);
        } catch (Exception e) {
            // Handle error.
            ...
        }
    }

    /**
     * Card insertion event. Get new card and card service
     * @param ctEvent The card insertion event.
     */
    public void cardInserted(CardTerminalEvent ctEvent) {
        try {
            // Get a SmartCard object
            card = SmartCard.getSmartCard(ctEvent);
            // Get the card proxy instance.
            myCardProxy = (MyCardProxy) 
                card.getCardService(MyCardProxy.class, true);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    /**
     * Card removal event. Invalidate card and card service.
     * @param ctEvent The card removal event.
     */
    public synchronized void cardRemoved(CardTerminalEvent ctEvent) {
        card = null;
        myCardProxy = null;
        // Initialize the framework
        SmartCard.shutdown();
    }

    /**
     * Get balance from the smart card.
     */
    public int getBalance() {
        try {
            // Get mutex to prevent other Card Services from modifying 
            // data. Delegate the call to the applet proxy.
            card.beginMutex();
            return Integer.parseInt(myCardProxy.getBalance());
        } catch (Throwable e) {
            return 0;
        } finally {
            // End mutual exclusion
            card.endMutex();
        }
    }
    ...
}

Listing 3. An Amplified Listener-Based OCF Application

An excerpt from the applet proxy is next. The OCF application delegates service invocation to the applet proxy, which implements the (complex) APDU management piece:

public class MyCardProxy extends AppletProxy {
    // My APDU definitions.
    final static byte MyAPPLET_CLA = (byte)0x80;
    final static byte VERIFY_INS = (byte)0x20;
    final static byte GET_BALANCE_INS = (byte) 0x30;
    final static short GET_BALANCE_RESPONSE_SZ = 2;
    protected final static int OK = 0x9000;
    final static short SW_PINVERIFY_FAILED = (short)0x6900;

    /** 
     * Reusable command APDU for getting an information 
     *  entry field. 
     */
    private CommandAPDU getBalanceAPDU = new CommandAPDU(14);

    ...

    /** Application identifier of the BusinessCard applet */
    private static final ApplicationID MY_CARD_AID =
        new ApplicationID(new byte[] {  (byte)0xD4,
                                        (byte)0x55,
                                        (byte)0x00,
                                        (byte)0x00,
                                        (byte)0x22,
                                        (byte)0x00,
                                        (byte)0x00,
                                        (byte)0x00,
                                        (byte)0xFF});
    /**
     * Create a MyCardProxy instance.
     *
     * @param scheduler The Scheduler from which channels 
     * have to be obtained.
     * @param card      The SmartCard object to which this 
     * service belongs.
     * @param blocking  Currently not used.
     *
     * @throws opencard.core.service.CardServiceException
     *         Thrown when instantiation fails.
     */
    protected void initialize(CardServiceScheduler scheduler, 
            SmartCard card, boolean blocking) 
            throws CardServiceException {
        super.initialize(MY_CARD_AID, scheduler, card, blocking);
        try {
            // Allocate the card channel. This gives us 
            // exclusive access to the card until we release the 
            // channel.
            allocateCardChannel();

            // Get the Card State.
            ...

        } finally {
            releaseCardChannel();
        }
    }

    /**
     * Gets the balance.
     * @return The balance.
     */
    public String getBalance() 
        throws CardServiceInvalidCredentialException,
               CardServiceOperationFailedException,
               CardServiceInvalidParameterException,
               CardServiceUnexpectedResponseException,
               CardServiceException,
               CardTerminalException {
        try {
            allocateCardChannel();

            // Set up the command APDU and send it to the card.
            getBalanceAPDU.setLength(0);
            getBalanceAPDU.append(MyAPPLET_CLA); // Class
            getBalanceAPDU.append(GET_BALANCE_INS); // Instr'n
            getBalanceAPDU.append((byte) 0x00); // P1
            getBalanceAPDU.append((byte) 0x00); // P2
            getBalanceAPDU.append((byte) 0x00); // Lc
            getBalanceAPDU.append((byte) 0x00); // Le

            // Send command APDU and check the response.
            ResponseAPDU response = 
               sendCommandAPDU(getCardChannel(), MY_CARD_AID, 
                   getBalanceAPDU);
            switch (response.sw() & 0xFFFF) {
                case OK :
                    return new String(response.data());
                default :
                    throw new
                        CardServiceUnexpectedResponseException
                        ("RC=" + response.sw());
            }
        } finally {
            releaseCardChannel();
        }
    }

    ...
}

Listing 4. An Applet Proxy Example

You can find more information on use of OCF in the OpenCard Framework 1.2 Programmer's Guide. Also refer to the sample files in the OpenCard Framework reference implementation.

Not covered in this article but worth mentioning is the host-side API called the Global Platform. The Global Platform Card Committee provides a set of card APIs that complement the Java Card APIs, providing among other things card-management features. These specifications are in version 2.1.1.

The Java Card RMI Client API

Earlier you learned how to write a JCRMI-based applet. If your Java Card applet is JCRMI-based, you can use the Java Card RMI Client API to write a host application that accesses the applet's objects that are stored on the smart card.

For smart card management and access, the JCRMI Client API requires a card-terminal and services API such as the OpenCard Framework just described.

When we put these two APIs together we get a much-simplified, more fully object-oriented programming model, with several benefits:

  • No need to know details about the smart card and card reader
  • No need to know about low-level APDU communications
  • Code that's easier to design and maintain, resulting in shorter development time

The JCRMI Client API is defined in the following packages:

  • com.sun.javacard.javax.smartcard.rmiclient contains the core JCRMI Client API. It defines:
    • The CardAccessor interface that JCRMI stubs use to access the smart card.
    • The CardObjectFactory class that is the base class for JCRMI-stub generation implementations. An instance of this class is associated with one Java Card applet selection session.
    • The JavaCardRMIConnect class that is used by the client application to initialize a JCRMI session, and obtain an initial remote reference.
    • A number of Java Card exception subclasses, such as APDUExceptionSubclass, CardExceptionSubclass, CardRuntimeExceptionSubclass, CryptoExceptionSubclass, ISOExceptionSubclass, PINExceptionSubclass, PINException, ServiceExceptionSubclass, SystemExceptionSubclass, TransactionExceptionSubclass, and UserExceptionSubclass.
  • javacard.framework defines a number of Java Card exceptions that can be re-thrown on the client: APDUException, CardException, CardRuntimeException, ISOException, PINException, SystemException, TransactionException, and UserException.
  • javacard.framework.service defines the ServiceException, which represents an exception related to the service framework.
  • javacard.security defines the CryptoException that represents a cryptography-related exception.

Generating the RMI Client Stubs

You have the option of generating client stubs using the standard Java RMI compiler (rmic). You must run rmic for each remote class in your applet using a command in the following form:

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

...where:

  • -v1.2 is a flag required by the Java Card RMI client framework.
  • -classpath path identifies 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 preferred approach to generating RMI client stubs, though, is to use the dynamic proxy generation mechanism introduced with J2SE SDK 1.3. Version 2.2 of the Java Card RMI Client API will automatically generate stubs for you if you use the CardObjectFactory subtype JCCardProxyFactory when selecting the JCRMI applet - you don't have to generate any more stubs! This approach is illustrated in Listing 5.

Usage Restrictions

Because Java Card is a restricted runtime environment, we can expect to find limitations on what JCRMI actually supports. Java Card doesn't support serialization, and JCRMI arguments and return values are restricted:

  • Each argument to a remote method must be of one of the types Java Card supports - which don't include char, double, float, long, or multidimensional arrays. Support for int is optional.
  • The return value from any remote method must be of one of the supported types, or void, or a remote interface type.

The JCRMI Client Application

A JCRMI client application has some similarities to the OCF host application you've already seen, because the JCRMI Client API depends on the OCF for card management and communications.

The next code snippet first initializes the OCF and waits for a smart card to be inserted. It then creates an OCFCardAccessor implementation for our JCRMI connection to the card, client stubs are generated dynamically if needed, the applet is selected, we get the remote reference, and finally we make our remote call to getBalance():

...
try {
    // Initialize OCF
    SmartCard.start();

    // Wait for a smart card
    CardRequest cr = new CardRequest(CardRequest.NEWCARD, null,
                                     OCFCardAccessor.class);
    SmartCard myCard = SmartCard.waitForCard(cr);

    // Get an OCFCardAccessor for Java Card RMI
    CardAccessor ca = (CardAccessor) 
        myCard.getCardService(OCFCardAccessor.class, true);

    // Create a Java Card RMI instance
    JavaCardRMIConnect jcRMI = new JavaCardRMIConnect(ca);

    // Create a Java Card Proxy Factory that is used for dynamic 
    // proxy generation.
    CardObjectFactory factory = new JCCardProxyFactory(ca);
    // select the Java Card applet 
    jcRMI.selectApplet(MY_APPLET_AID, factory);

    // Get the initial reference
    MyRemoteInterface myRemoteInterface = 
        (MyRemoteInterface) jcRMI.getInitialReference();
    if(myRemoteInterface == null) {
        throw new 
           Exception("Received null instead of the initial ref");
    }

    // Invoke the remote getBalance() method
    short balance = myRemoteInterface.getBalance();
}
catch(UserException e) {
    // Handle exception
    ...
}
catch (Exception e){
    // Handle exception
    ...
} finally {
    // Clean up
    try{
        SmartCard.shutdown();
    }catch (Exception e){
        System.out.println(e);
    }
}

Listing 5. Sample JCRMI Client

As you can see, the coding you have to do is reduced and greatly simplified.

Using the Security and Trust Services API for J2ME

SATSA is a new set of optional packages for J2ME that defines a client-side API to access what are referred to as security elements: devices such as smart cards. In this section I'm going to introduce only the communication aspects of SATSA; SATSA PKI and crypto APIs are not covered.

The SATSA communication API is broken down as follows:

  • SATSA-APDU defines an API for communication with ISO-7816-4-compliant smart cards. This optional package consists of a single package javax.microedition.io.APDUConnection.
  • SATSA-JCRMI defines a Java Card RMI client API. This optional package consists of the following Java packages:
    • javax.microedition.io.JavaCardRMIConnection
    • javax.microedition.jcrmi.RemoteRef
    • javax.microedition.jcrmi.RemoteStub
    • java.rmi.Remote
    • java.rmi.RemoteException
    • javacard.framework.service.ServiceException
    • javacard.framework.CardRuntimeException
    • javacard.framework.ISOException
    • javacard.framework.APDUException
    • javacard.framework.CardException
    • javacard.framework.PINException
    • javacard.framework.SystemException
    • javacard.framework.TransactionException
    • javacard.framework.UserException
    • javacard.security.CryptoException

SATSA brings the J2ME and Java Card platforms closer together. SATSA uses the CLDC 1.0 Generic Connection Framework (GCF) for communications between a J2ME-based device and a smart card as illustrated here:

Figure 3. The Generic Connection Framework and SATSA Connections
Figure 3. The Generic Connection Framework and SATSA Connections

Because SATSA is based on the GCF, developing MIDlets that make use of the smart card on the cellphone is not only relatively easy, but also familiar to J2ME developers.

SATSA caters to both of the overall programming models for Java Card applications: the APDU-message model and the Java Card RMI object-oriented distributed model. For each of these models SATSA defines a new GCF connection type:

  • APDUConnection allows a J2ME application to exchange APDUs with a smart-card application using the ISO-7816 APDU protocol.
  • JavaCardRMIConnection allows a J2ME application to use Java Card RMI to invoke remote methods on a smart card.

Specifying SATSA Connection Types

All GCF connections are created using the Connector.open() method. One of the arguments to Connector.open() is a URL that indicates the type of connection to create. The CLDC GCF defines this URL as a string in the following format:

scheme:[target][params]

...where:

  • scheme is the connection type to create (and protocol to use).
  • target is typically some kind of network address.
  • params are optional parameters in the form name=value, separated by a semicolon.

For SATSA, the format of the URL is:

protocol:[slotID]; AID

...where:

  • protocol is either apdu for an APDU-based connection or jcrmi for a JCRMI-based connection.
  • slotID is the number that indicates the slot into which the card is inserted. The slotID field is optional; it defaults to 0.
  • AID is the application identifier for a smart card application. An AID is a string of five to 16 hexadecimal byte values separated by periods; for example, " A0.0.0.67.4.7.1F.3.2C.3".

Using an APDUConnection

An APDUConnection defines the methods that allow us to talk to ISO-7816-compliant cards using GCF. It defines three methods:

  • enterPIN() prompts the user for a PIN.
  • exchangeAPDU() exchanges an APDU with a smart card application. This call blocks until a response has been received from the smart card (or processing is interrupted).
  • getATR() returns the Answer To Reset (ATR) message sent by the smart card in response to the reset operation.

The following snippet shows how to open an APDUConnection, how to close it, and how to exchange a command APDU and receive a response APDU:

...
try {
    // Create an APDUConnection
    String url = "apdu:0;AID=A1.0.0.67.4.7.1F.3.2C.5";
    APDUConnection ac = (APDUConnection) Connector.open(url);

    // Send a command APDU and receive a response APDU
    byte[] responseAPDU = ac.exchangeAPDU(commandAPDU);
    ...

    // Close connection.
    ac.close();
} catch(IOException e){
    ...
}
...

Listing 6. Using SATSA-APDU

SATSA makes APDU communications simple. Note that the formats of the command and response APDUs are the same as you saw earlier in the "Writing a Card-Side Java Card Applet" section of Part 2, which tells you how to write a Java Card applet based on APDU messaging.

Using a JavaCardRMIConnection

A JavaCardRMIConnection defines the methods that allow us to use the Java Card RMI programming model with GCF. The JavaCardRMIConnection defines the method getInitialReference(), which returns the stub object for an initial remote reference.

...
try {
    // Create a JavaCardRMIConnection
    String url = "jcrmi:0;AID=A0.0.0.67.4.7.1F.3.2C.3";
    JavaCardRMIConnection jc = (JavaCardRMIConnection)
    Connector.open(url);
    MyRemoteObject robj = (MyRemoteObject) 
                          jc.getInitialReference();
    ...
    short balance = robj.getBalance();
    ...
    // Close connection
    jc.close();
} catch (Exception e) {
    ...
}
...

Listing 7. Using SATSA-JCRMI

Using the SATSA-JCRMI API as shown above allows us to invoke the getBalance() method that we defined in Part 2 of this article, which shows you how to write an RMI-based Java Card applet.

Summary

The first installment of this article covered the use of smart cards to store sensitive information and process transactions securely, and the various aspects of Java Card technology: the Java Card VM, the runtime environment, the relevant APIs, and the behavior of Java Card applets.

Part 2 examined the development of a Java Card applet. It covered the structure of a Java Card applet, the Sun Java Card Development kit, and use of the APIs and programming models that are available when writing card-based applets: the Java Card API, and the Java Card RMI API.

This final part of the article covered the development of host applications, and some of the Java APIs that are available when writing them: the OpenCard Framework, the Java Card RMI Client API, and the Security and Trust Services API (SATSA) for J2ME.

Smart cards with Java Card technology are the most portable and secure way of carrying digital personal information and computational capabilities - a very powerful and needed technology in today's digital world.

Links of Interest

  • The following references can be found in the Java Card Development Kit (JCDK):
    • Java Card 2.2 Application Programming Interface
    • Java Card 2.2 Runtime Environment (JCRE) Specification
    • Java Card 2.2 Development Kit User's Guide
    • Java Card 2.2 Virtual Machine Specification
    • Java Card Applet Developer's Guide
    • Java Card Programming Notes
    • Java Card RMI Client API
  • Developing a Java Card Applet

Acknowledgements

Many thanks to the Java Card team, Florian Tournier, Oscar A. Montemayor, Eduard de Jong, Victor Olkhovets, and Nachi Periakaruppan, and to Richard Marejka and Brian Christeson for their great contributions and feedback to this article.

Rate and Review
Tell us what you think of the content of this page.
Excellent   Good   Fair   Poor  
Comments:
Your email address (no reply is possible without an address):
Sun Privacy Policy

Note: We are not able to respond to all submitted comments.