The previous Java Developer column, "Smart cards: A primer", gave a general overview of smart cards and how they work. It included a section on smart card standards, introducing the concept of OpenCard. As described in the first article, OpenCard is an open standard that provides interoperability of smart card applications across NCs, POS terminals, desktops, laptops, set tops, and PDAs. OpenCard can provide 100% pure Java smart card applications. Smart card applications often are not pure because they communicate with an external device or use libraries on the client. In this article we will provide two implementations to two different card readers, demonstrating how you would add support for card readers to OpenCard. We are hopeful that ports for Litronic, Gemplus, Schlumberger, Bull, Toshiba, and SCM will be available soon, compliments of OpenCard and JavaWorld.
Introduction
In order to use a smart card, you need to be able to read the card and communicate with it using an application. OpenCard provides a framework for this by defining interfaces that must be implemented. The OpenCard framework defines several of these interfaces. Once these interfaces are implemented, you can use other services in the upper layers of the API. For example, with a properly interfaced reader, OpenCard can start a Java card agent whenever the card is inserted. The card agent can then communicate with applications on the smart card via the card terminal in the context of a session.
This article will teach you how to interface card terminals to OpenCard. Future articles will discuss how to write an agent. A small test application, which gets the ATR (Answer to Reset) string is provided. The ATR is fundamental to smart cards. We will take the OpenCard development kit and explain implementations for two different smart card readers using the Card Terminal Interface. The techniques discussed in the article for powering up readers, starting card sessions, and the use of Protocol Data Units and Application Protocol Data Units can be reused for most of the readers on the market.
While it's not necessary to use OpenCard in creating 100% pure Java smart card applications, without it developers are forced to use home-grown interfaces to smart cards. (For a detailed explanation of what 100% pure really means, see the Resources section.) OpenCard also provides developers with an interface to PC/SC (a smart card application interface developed by Microsoft and others for communicating with smart cards from Win32-based platforms for PCs) for use of existing devices on Win32 platforms. Read on and learn how to use smart cards with your browser.
OpenCard architecture: An overview
OpenCard provides an architecture for developing applications in Java that utilize smart cards or other ISO 7816-compliant devices on different target platforms such as Windows, network computers, Unix workstations, Webtops, set tops, and so on. The OpenCard Framework provides an application programming interface (API), which allows you to register cards, look for cards in readers, and optionally have Java agents start up when cards are inserted in the reader. The architecture of OpenCard is depicted in Figure 1.

The architecture of the OpenCard Framework is made up of the CardTerminal
, the CardAgent
, the Agents and/or applications that interact with these components. OpenCard consists of four Java packages with the prefix opencard:
- application
- io
- agent
- terminal
The terminal package in OpenCard
The packages opencard.application and opencard.io provide the high-level API used by the application developer. The services needed by the high-level API are carried out by classes in the opencard.agent and opencard.terminal packages. The opencard.agent package abstracts the functionality of the smart card through the CardAgent
. Package opencard.terminal abstracts the card terminals (also known as card readers). Understanding the structure of the opencard.terminal package is required to understand the sample implementations of card terminals provided in this article.
A card terminal abstracts the device that is used in a computer system to communicate with a smart card. The opencard.terminal package contains classes to represent the card-terminal hardware, to interact with the user, and to manage card-terminal resources. Not all readers have these abilities. When implementing a reader that doesn't have keyboard entry, we will use the UserInteractionHandler
.
Card terminal representation
Each card terminal is represented by an instance of class CardTerminal
that defines the abstract OpenCard-compliant card terminal. A card terminal may have one or more slots for smart cards and optionally a display and a keyboard or PIN pad. The slots of a card terminal are represented by instances of the abstract class Slot
, which offers methods to wait for a card to be inserted, to communicate with the card, and to eject it (if possible).
User interaction
Using a smart card requires interaction with the user -- for card-holder verification. The interface UserInteraction
provides for this functionality. It provides methods to write a message onto the display and receive input from the user. Card terminals that do not support all user interaction features can make use of the UserInteractionHandler
, which implements a UserInteraction
as a graphical user interface based on the abstract windowing toolkit (AWT).
Resource management
Cards and card readers require resource management so that agents can be granted the level of access control they require. Resource management provides for the sharing of card terminals and the cards inserted in them among the agents in the system. For example, say you are using your smart card to sign a document at the same time that a high-priority mail message comes in that needs to be decoded using your smart card. Resource management arbitrates the access to the CardTerminal
and the correct port.
The resource management for card terminals is achieved by the CardTerminalRegistry
class of OpenCard. There is only one instance of CardTerminalRegistry
: the system-wide card terminal registry. The system-wide card terminal registry keeps track of the card terminals installed in the system. The card terminal registry can be configured from properties upon system start up or dynamically through register
and unregister
methods to dynamically add or remove card terminals from the registry.
During the registration of a card terminal, a CardTerminalFactory
is needed to create an instance of the corresponding implementation class for the card terminal. The card terminal factory uses the type name and the connector type of the card terminal to determine the CardTerminal
class to create. The concept of a card terminal factory allows a card terminal manufacturer to define a mapping between user-friendly type names and the class name.
Sample implementation: IBM card terminal
In this section, we'll describe the integration of the IBM 5948 card terminal into OpenCard. The IBM 5948 card terminal has one slot for smart cards, an LCD display, and a PIN pad. It is connected to the workstation or PC via a serial port. More information on this reader is available in the
section.
In order to access a card terminal from within OpenCard, an implementation for both abstract classes CardTerminal
and Slot
must be provided. These have been named IBM5948CardTerminal
and IBM5948Slot
, respectively. In addition, an appropriate CardTerminalFactory
named IBMCardTerminalFactory
is needed. The terminal implementation consists of package com.ibm.zurich.smartcard.terminal.ibm5948. Figure 2 depicts the inheritance relationships between the classes of opencard.terminal, the Java classes, and the terminal implementation. The class diagram also contains class IBM5948Driver
, which does not implement any abstract class of OpenCard but serves as a Java interface to the terminal driver library written in C.

We assume that the terminal is already connected to the workstation or PC, and that the serial port is configured to work with the terminal. In the following section, we describe the design and implementation of the driver, the terminal, the slot, and the card terminal factory. The configuration of the card terminal registry also is provided.
The card terminal driver
The card terminal is shipped with a driver that is available as a dynamic link library (DLL). The DLL has a C API that offers the functions CT_init
, CT_data
, and CT_close
:
The function
CT_init
is used to open a connection to a card terminal that is connected to a certain serial port. After the connection has been established, protocol data units (PDU) can be exchanged with the card terminal and APUs can be exchanged with the smart card that is plugged into the slot of the terminal via theCT_data
function.The
CT_data
call is used to send one PDU and retrieve the response from the terminal or the smart card, respectively.- The
CT_close
function is used to close the connection to the card terminal and release any resources.
Success or failure of all three API calls is indicated by the return code.
The Java API
Similar to the C API, we define a Java API for the card terminal driver. The Java API for the card terminal consists of class IBM5948Driver
, which has native methods calling the C API. We decided to implement as much functionality as possible in Java and have only some "glue" code written in C. In fact, the parameters of the ctInit
and ctClose
method are just passed on to the respective C API function. Since arrays are organized differently in C and Java, they need to be handled by calls to the Java Native Interface (JNI) API of the virtual machine. The native methods return the return code of the C API. The implementation of the ctData
method is shown below:
JNIEXPORT jint JNICALL Java_com_ibm_zurich_smartcard_terminal_ibm5948_IBM5948Driver_ctData(JNIEnv *env, jobject that, jbyte destination, jbyteArray command, jint commandLength, jbyteArray response, jint responseMax) { short rc; unsigned char sad = HOST; unsigned char dad = destination; unsigned short responseLength = (unsigned short)responseMax; unsigned char *commandArray; unsigned char *responseArray; jclass cls = (*env)->GetObjectClass(env, that); jfieldID fid; jint ctn; fid = (*env)->GetFieldID(env, cls, "ctNumber", "I"); if(fid == NULL) { return(CT_ERR_HTSI); } ctn = (*env)->GetIntField(env, that, fid); commandArray = (unsigned char *) (*env)->GetByteArrayElements(env, command, 0); responseArray = (unsigned char *) (*env)->GetByteArrayElements(env, response, 0); rc = CT_DATA(ctn, &dad, &sad, commandLength, commandArray, &responseLength, responseArray); (*env)->ReleaseByteArrayElements(env, command, (signed char *)commandArray, 0); (*env)->ReleaseByteArrayElements(env, response, (signed char *)responseArray, 0); fid = (*env)->GetFieldID(env, cls, "responseLength", "I"); if(fid == NULL) { return(CT_ERR_HTSI); } (*env)->SetIntField(env, that, fid, responseLength); return rc; }
The native methods described above mimic the C API in Java. The reason for this was to have as little C code to maintain as possible. On top of the native methods, which are private, the methods init
, data
, and close
are implemented. They call the native methods and throw an exception if the return code indicates an error. In the case of the data method, the response byte array is returned upon a successful completion of the native method call. The example below shows the data method:
synchronized byte[] data(byte destination, byte[] pdu) throws CardTerminalException { int rc = ctData(destination, pdu, pdu.length, response, response.length); if (rc == CT_OK) { byte[] result = new byte[responseLength]; System.arraycopy(response, 0, result, 0, responseLength); return result; } else throw new CardTerminalException(rc2String(rc)); }
In order to keep memory management inside Java, a buffer response for the answer from the terminal is allocated once and passed on to the native code. Since the C API is not re-entrant, the methods of IBM5948Driver
must be declared synchronized.
Implementing the card terminal
The card terminal is controlled by submitting control PDUs to the data method of the IBM5948Driver
. The format of the control PDUs is ISO 7816-4 compliant. This allows us to deploy class opencard.agent.CommandPDU
to construct the PDUs and opencard.agent.ResponsePDU
to handle the responses.
The IBM5948CardTerminal
class extends class CardTerminal
. The constructor initializes the super class and instantiates the driver. Then it instantiates the array to hold the slots, and instantiates one instance of IBM5948Slot
to represent the only slot of the IBM 5948 card terminal.