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

1 2 3 Page 2
Page 2 of 3

The abstract methods of CardTerminal are implemented using the command set of the IBM 5948 card terminal. Before constructing a command PDU, the actual parameters of the method are checked against the features of the card terminal. For the UserInteraction, for example, we must use the UserInteractionHandler if we want to read alpha-numeric input, since our terminal has only a pinpad and no keyboard.

If the terminal supports the request, a command PDU is constructed and passed on to the driver's data method. The response PDU is then checked for errors, and, in the case of an error, an appropriate CardTerminalException is thrown. The interaction with the terminal driver looks like this:

byte[] text;
// ... put the message into text
CommandPDU cpdu = new CommandPDU(TERMINAL, DISPLAY, (byte)0, (byte)0, text);
ResponsePDU rpdu = driver.data(CARDTERMINAL, cpdu.toT1ByteArray());

The implementation of the waitForCard method is straightforward since only one slot exists. With only one slot in the terminal, waiting for a card in the terminal is equivalent to waiting at the slot itself. Hence, we call the waitForCard method of the slot instance.

Implementing the Slot

A slot as defined in OpenCard can either extend the Slot class or the PollingSlot class, depending on whether or not the insertion and removal of the smart card causes the virtual machine to generate an event. Normally, such an event is not generated, so a thread must be started to poll for smart card presence in the slot. This is carried out by the PollingSlot class. Our driver is implemented in C, and, as it does not generate the event, we extend PollingSlot.

The IBM 5948 card terminal offers three slot-related control commands: "get status," "request card," and "eject card":

  • The "get status" command returns information on whether a smart card has been inserted, the power status of the card, the status of the terminal's LEDs, and the configuration of the card terminal itself.

  • The "request card" command instructs the terminal to wait for the user to insert a card within a given time interval. Once the card is inserted, it is powered up, reset, and the answer to reset (ATR) is returned.

  • The "eject card" command does not really eject the card but powers down the card and waits for the user to remove it.

The cardPresent method is implemented by the "get status" command. The methods powerUpCard and waitForCardID must be implemented through the "request card" command. The difference between the two methods is that waitForCardID returns a CardID object and powerUpCard doesn't.

The IBMCardTerminalFactory

The implementation of the CardTerminalFactory is shown below. As long as only one terminal type is supported, all you need to do is check for the type parameter and instantiate the IBM5948CardTerminal class.

The IBMCardTerminalFactory uses the factory design pattern, which returns a specific implementation for the specified type of reader. For example, if there were 20 readers from IBM, there would be 20 possible types of readers returned from this factory class.

package com.ibm.zurich.smartcard.terminal.ibm5948;
import opencard.terminal.*;
public class IBMCardTerminalFactory implements CardTerminalFactory {
  public CardTerminal createCardTerminal(String name, String type,
                                         String connector)
    throws ClassNotFoundException, CardTerminalException {
      IBM5948CardTerminal terminal = null;
      if (type.equals("IBM5948-B02")) {
        terminal = new IBM5948CardTerminal(name, type, connector);
      }
      else {
        throw new ClassNotFoundException("Type unknown: " + type);
      }
      return terminal;
  }
}

Configuration of the CardTerminalRegistry

The new terminal must be registered with the OpenCard card terminal registry before it can be accessed. For a test program, it is recommended that you register the new card terminal dynamically using the register method:

CardTerminalRegistry registry = CardTerminalRegistry.registry();
registry.register("Zueri",
     "com.ibm.zurich.smartcard.terminal.ibm5948.IBMCardTerminalFactory",
     "IBM5948-B02",
     "1");

Once the card terminal has been debugged, it can be registered automatically from the OpenCard properties file called opencard.properties. Assuming the new card terminal is the first OpenCard card terminal, the following lines must be added:

# ... the "Zueri" terminal
OpenCard.CardTerminal.0.name=Zueri
OpenCard.CardTerminal.0.factory=com.ibm.zurich.smartcard.
     terminal.ibm5948.IBMCardTerminalFactory
OpenCard.CardTerminal.0.type=IBM5948-B02
OpenCard.CardTerminal.0.address=1}

Building the example for the IBM driver

To build the IBM 5948 files, you can get the release from the OpenCard Web site in the Resources section and use GNUmake (make utility from the GNU group). This release is for more experienced programmers because it involves the entire source hierarchy. Later in this article, we will offer a minimal version of the release that keeps all the functionality in .jar files.

Supporting a Reflex20 PCCard reader in the OpenCard Framework

In this section, we will expand on all of the little pieces required to implement the concrete classes CardTerminal, Slot, and CardTerminalFactory for another type of reader, the Reflex20 from Schlumberger. (For a link to information on this reader, see the Resources section.) One of the values of a standard is that once you understand it, you are supposed to be able to repeat it without having to go through a significant learning curve each time. With the knowledge we gained from the previous section, we should be able to quickly write the interface code for the Reflex20 driver. One of the major advantages of Java is that you can support new functionality quickly; it is easier to program with Java, especially in the area of device drivers. Classically, device drivers have been located in the kernel or the operating system and often have real-time performance constraints. For smart cards, this is not the case because the driver is not running as part of the VM and smart cards are very slow. So if some new card terminal comes along, you should be able to interface to it quickly and have your old applications run without modification. Of course, this isn't always true if there is new functionality in the reader -- but at least we are not taking a step backward.

Background material on JNI and PCCard support for OpenCard

The terminal implementation consists of package ora.smartcard.terminal.reflex20. The Reflex20 is a PCCARD reader. This type of reader plugs into a PCCard socket (also referred to as a PCMCIA socket). Most laptops come with one or two PCCARD slots. PCCARD readers are available as add-on cards for PCs or as external devices. You can look into PCCard support for your computer in the Resources section. In order to write drivers, you need hardware, so get yourself a Reflex20 reader or another reader of choice and some smart cards.

The Reflex20 card terminal driver

The Reflex20 reader comes with a DLL and a .lib file that implement the C API. We proceed as we did earlier and use the Java native bindings to call these methods. A useful tip for looking at DLLs is provided with the MSVC and is standard equipment on Unix platforms. This DLL offers the functions CT_init, CT_data, and CT_close. I have included two dumps of the two relevant DLLs, the first being the DLL that calls the methods in the second DLL. I have included the dumps because readers have asked how to debug library loading errors. I always start with the signatures and make sure they are correct and match what the Java VM expects them to be.

The first dumpbin is for the DLL, which calls the CT_init methods and implements the native methods for Reflex20Drvier.java.

Microsoft (R) COFF Binary File Dumper Version 5.00.7022
Copyright (C) Microsoft Corp 1992-1997. All rights reserved.
Dump of file Reflex20Driver_w32.dll
File Type: DLL
Section contains the following Exports for Reflex20Driver_w32.dll
0 characteristics
3477B204 time date stamp Sun Nov 23 04:33:08 1997
0.00 version
1 ordinal base
3 number of functions
3 number of names
ordinal hint name
1 0 _Java_com_ora_smartcard_terminal_reflex20_Reflex20Driver_ctClose@12
(0000119A)
2 1 _Java_com_ora_smartcard_terminal_reflex20_Reflex20Driver_ctData@28
(0000101F)
3 2 _Java_com_ora_smartcard_terminal_reflex20_Reflex20Driver_ctInit@16
(00001000)
Summary
4000 .data
1000 .idata
1000 .rdata
1000 .reloc
4000 .text

What is interesting about this dump is the signature methods generated for the C functions. It is very important that the signatures match or you will get runtime errors. As mentioned earlier in the Java API section, JNI is needed to interface to native method libraries. The JNI convention is really very logical. Always prepend _Java, then provide the fully qualified name of your package, followed by the name of the method. The "@" stuff is appended by Windows.

The second file, below -- Ctscrw95.dll -- contains the reader-specific interface functions; it simply contains the implementations of the CT_init, Ct_data, CT_close methods that are called from the DLL above. Consult the Resources section to review JNI.

Microsoft (R) COFF Binary File Dumper Version 5.00.7022
Copyright (C) Microsoft Corp 1992-1997. All rights reserved.
Dump of file native\Ctscrw95.dll
File Type: DLL
Section contains the following Exports for CTSCRW95.dll
0 characteristics
323D2FFF time date stamp Mon Sep 16 10:46:23 1996
0.00 version
1 ordinal base
5 number of functions
5 number of names
ordinal hint name
3 0 CT_close (00002F10)
4 1 CT_data (00002FE0)
2 2 CT_init (00002E00)
5 3 Get_VersionString (00004490)
1 4 WEP (00002D80)
Summary
3000 .data
1000 .idata
1000 .rdata
1000 .reloc
6000 .text

The CT API type of interface shown above is common on many readers listed in the Resources section. Because the OpenCard native interface has been modeled on CT_init, CT_data and CT_close API, it will be easy to interface to the card terminal. If you need to review native method interfaces, take a look at the articles in the Resources section that discuss this topic. The file Reflex20Driver.c is almost identical to the IBM5948Driver.c file. Some of the names were changed to make it specific to the Reflex20, and of course, to get the correct signature so that the Java VM can find these native methods at runtime.

Implementing the card terminal

The Reflex20CardTerminal class extends class CardTerminal and is provided below with comments. The card terminal is controlled by submitting control PDUs to the data method of the Reflex20CardTerminal. The format of the control PDUs is ISO 7816-4 compliant. See the Resources section for more information on this. All of the processing is the same as described in the CardTerminal section for the IBM5948 above.

The abstract methods (meaning implementation deferred for implementation times like this) of CardTerminal are:

public abstract class CardTerminal implements CardProvider, UserInteraction
{
protected abstract Properties internalFeatures(Properties features);
public abstract String promptUser(String prompt, CardTerminalIOControl
ioControl);
public abstract Object command(String appSpecCmd, Object appSpecParameter,
int timeout)

These are implemented in Reflex20CardTerminal.java, which is provided in the Resources section.

The implementation of the waitForCard method is usually different for each PCCard because many manufacturers use a different PDU to get information from the card terminal.

Implementing the Slot

As before, Slot in OpenCard can extend the Slot class or the PollingSlot class, depending on whether or not the insertion and removal of the smart card can be configured to generate an event to the virtual machine. The Java code for the Slot file is provided in the Resources section.

The ORACardTerminalFactory

As this ORACardTerminalFactory will be used in future articles, we will want to have several types of CardTerminals available for creation by name. These names correspond to the definitions in the property file. The property file can be searched for using the usual paths, and the implementation is very similar to the IBMCardTerminalFactory described above. To identify devices to OpenCard, you need to specify them in the opencard.properties file.

The attribute value pairs described below are consulted by OpenCard for registration information. Many of these properties are documented in the OpenCard documentation. See this documentation in the Resources section.

JAVA_INSTALL_DIR/lib/opencard.properties
$HOME/.opencard-properties
./opencard.properties
##################################################
# Card Terminal Section #
##################################################
OpenCard.CardTerminal.0.name=Reflex20
OpenCard.CardTerminal.0.factory=com.ora.smartcard.
     terminal.reflex20.ORACardTerminalFactory
OpenCard.CardTerminal.0.type=Reflex20
OpenCard.CardTerminal.0.address=0
OpenCard.terminal.trace=true
com.ora.smartcard.terminal.trace=true

The two trace lines turn on tracing. OpenCard has some finely instrumented code with diagnostics that can be turned on dynamically. This property file is required for every platform that supports OpenCard. The property file also can be extended so that reader- and/or card-specific information is available. The latter is not recommended.

Building the examples

While developing the Reflex20 PCCard driver, we created a very easy development environment for you to develop CardTerminal implementations. We do this by providing an opencard.jar file that you compile with and use on your CLASSPATH while the applet or application is running. The list of files and the DLL required to implement the Reflex20 are included below. In addition to the files listed, you need to use the opencard.jar file, which contains all the opencard.jar.* classes for your import statements.

Related:
1 2 3 Page 2
Page 2 of 3