Smart cards and the OpenCard Framework

Learn how to implement a card terminal and use a standard API for interfacing to smart cards from your browser

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.

Figure 1. OpenCard Framework architecture

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:

  1. application
  2. io
  3. agent
  4. 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

Resources

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.

Figure 2. Inheritance diagram for the IBM 5948 card terminal

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 the CT_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.

Related:
1 2 3 Page 1
Page 1 of 3