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. 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 proxiesFor 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.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 appletThe 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. MethodCLAINSP1P2LcDataLeDo CHV0x800x010x000x000x08CHV–Get Name0x800x02index0x01 –0x00Get Title0x800x02index0x02 –0x00Get E-mail Address0x800x02index0x03 –0x00Get Phone Number0x800x02index0x04 –0x00Get Address0x800x02index0x05 –0x00Set 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. private static final ApplicationID BUSINESS_CARD_AID = new ApplicationID( new byte[] { (byte)0xD2,(byte)0x76,(byte)0x00,(byte)0x00,(byte)0x22, (byte)0x00,(byte)0x00,(byte)0x00,(byte)0xFF }); When calling the initialize method of its base class AppletProxy, our business card applet proxy must pass this identifier. The base class needs this information to select the appropriate card applet before sending the APDUs when its method sendCommand is called. The initialize method is also responsible for assuring that a BusinessCardState object is present in the card channel:protected initialize(CardServiceScheduler scheduler, SmartCard card, boolean blocking) throws CardServiceException { super (BUSINESS_CARD_AID, scheduler, card, blocking); try { allocateCardChannel(); Hashtable cardState = (Hashtable) getCardChannel().getState(); if (cardState == null) { cardState = new Hashtable(); getCardChannel().setState(cardState); } BusinessCardState state = (BusinessCardState) cardState.get(BUSINESS_CARD_AID); if (state == null) { state = new BusinessCardState(); state.setCHVPerformed(false); cardState.put(BUSINESS_CARD_AID, state); } } finally { releaseCardChannel(); } } The method getBusinessCardState can be used to conveniently obtain the business card state from the channel:protected BusinessCardState getBusinessCardState() { return (BusinessCardState) ((Hashtable) getCardChannel().getState()).get(BUSINESS_CARD_AID); } Each method that communicates with the card needs to obtain a card channel to do so. The method allocateCardChannel has to be called to obtain a channel. Once it owns the channel, the method can exclusively communicate with the card; the OpenCard Framework assures that other threads are blocked when they call allocateCardChannel until the method calls releaseCardChannel to free the channel again. In this example, the methods communicating with the card are performCHV, getField and setField. performCHV is called by the other methods to obtain the card-holder verification from the user and pass it to the card to enable access to the business card data. It takes a card channel and a CHV number as parameters. In the first few lines, we set up the command APDU for doing the card holder verification, including the CHV number, as shown in the code below. Then we set up a CHVControl object, which is required by the OpenCard Framework to get the password for access to the data stored by the business card applet, and put it into the APDU before sending it to the card. Normally, this is done by the OCF using a configurable dialog. However, if the card terminal used has a PIN pad and a display, and the associated CardTerminal class implements the required methods, getting the password from the user and inserting it into the APDU can also be done by the terminal. The potential capability of the used terminal to obtain the PIN from the user and to insert it into the APDU before passing it to the card is why we use sendVerificationAPDU instead of sendCommandAPDU. The rest of the method is code for error handling: depending on the card’s return code, we either return or throw the appropriate subclass of CardServiceException (see package opencard.core.service). This is shown in the code listing below.protected void performCHV(CardChannel channel, int numCHV) throwsCardServiceException, CardTerminalException { // Class and instruction for VerifyCHV command. final byte[] DO_CHV_COMMAND_PREFIX = {(byte) 0x80, (byte) 0x01}; final byte[] PLACEHOLDER = {(byte) 0, (byte) 0, (byte) 0, (byte) 0, (byte) 0, (byte) 0, (byte) 0, (byte) 0}; // Set up the command APDU. CommandAPDU doCHVAPDU = new CommandAPDU(13); doCHVAPDU.append(DO_CHV_COMMAND_PREFIX); // Class, Instruction doCHVAPDU.append((byte) numCHV); // CHV number doCHVAPDU.append((byte) 0x00); // Reserved doCHVAPDU.append((byte) 0x08); // Reserved doCHVAPDU.append(PLACEHOLDER); // Placeholder for password // Let the card terminal ask for the password. If it is unable // to do it, OpenCard will use the CHVDialog set in the service. CardTerminalIOControl ioctrl = new CardTerminalIOControl(8, 30, null, null); CHVControl chvctrl = new CHVControl("Enter your password", numCHV, CHVEncoder.STRING_ENCODING, 0, ioctrl); ResponseAPDU response = sendVerificationAPDU(channel, doCHVAPDU, chvctrl, getCHVDialog(), -1); switch (response.sw() & 0xFFFF) { case OK : // The card applet keeps in mind that we now have performed // the CHV. Our state must reflect this. getBusinessCardState().setCHVPerformed(true); return; case INVALID_PASSWORD : throw new CardServiceInvalidCredentialException("Wrong CHV"); default : throw new CardServiceUnexpectedResponseException("RC = " + response.sw()); } } The method getField, shown below, can be used to obtain a field from a business card information entry. It takes the index of the business card info entry and a field identifier as parameters. We check the state to find out whether the card holder verification has already been performed. If it hasn’t, we call performCHV to do it. The next action is to construct the command APDU for getting the name field and sending it to the card. Depending on the return code, we either return the name or do some error handling. If the index is out of range, we simply throw an appropriate exception. We update the state so that it indicates that the CHV has not already been performed and then recursively call getName to retry.public String getField(int index, int field) throws CardServiceException, CardTerminalException { // Class and instruction byte for the get field command. final byte[] GET_FIELD_COMMAND_PREFIX = {(byte) 0x80, (byte) 0x02}; try { allocateCardChannel(); // Perform Card Holder Verification if necessary if (!getBusinessCardState().isCHVPerformed()) { performCHV(getCardChannel(), 1); } // Set up the command APDU and send it to the card. getFieldAPDU.setLength(0); getFieldAPDU.append(GET_FIELD_COMMAND_PREFIX); // Class, Instruction getFieldAPDU.append((byte) index); // Business Info Index getFieldAPDU.append((byte) field); // Field identifier for Name getFieldAPDU.append((byte) 0x00); // Lc getFieldAPDU.append((byte) 0x00); // Le // Send command APDU and check the response. ResponseAPDU response = sendCommandAPDU(getCardChannel(), BUSINESS_CARD_AID, getFieldAPDU); switch (response.sw() & 0xFFFF) { case OK : return new String(response.data()); case INDEX_OUT_OF_RANGE : throw new CardServiceInvalidParameterException("Index out of range"); default : throw new CardServiceUnexpectedResponseException("RC=" + response.sw()); } } finally { releaseCardChannel(); } } The method setField is quite similar to the method getField, so we omit it here. It sets the name field of a business-card information entry on the card, taking the index of the entry, field ID and the the string that will be its parameters. As mentioned in the first part of this article, when a card applet is deselected, it may lose its state. This is the case for our business card applet. When another applet is selected by another card applet proxy, it forgets any successful card-holder verification. This must be reflected in the state so that the next time the business card proxy is used, it performs the card-holder verification again. This state update can be assured by overwriting the method appletSelected, which is called whenever another card applet is selected. Here we modify the state to indicate that a CHV needs to be performed — again.protected void appletSelected() { // As the applet lost its state, a CHV will be required // for the next access. getBusinessCardState().setCHVPerformed(false); } The card service factory To create a card service instance, the OpenCard Framework needs a card service factory. All card service registries must inherit from the common base class opencard.core.service.CardTerminalFactory and must overwrite two abstract methods that are used by the OpenCard Framework to instantiate services: knows and CardServiceClasses. The constructor of the card service factory can be empty:public class BusinessCardProxyFactory extends CardServiceFactory { public BusinessCardProxyFactory () {} The method knows takes a CardID object — encapsulating a card’s ATR (Answer to Reset, that is, the byte sequence returned by a smart card when it’s powered up) and returns “true” if the factory knows the card and can create services for the card; otherwise, it returns “false”: public boolean knows (CardID cardID) { if (normalizedCOSValue(cardID) == IBMMFCConstants.JAVA_CARD_COS) { return true; } else { return false; } } protected int normalizedCOSValue (CardID cid) { if (cid.isOpenCardCompliant()) { return cid.getCardOS(); } else { return 0; } } The method cardServiceClasses returns the classes that the factory can create and from which the Framework may choose when creating a service that shall implement a particular interface: private static Vector services_ = new Vector(); static { services_.addElement(BusinessCardProxy.class); } protected Enumeration cardServiceClasses (CardID cid) { return services_.elements(); } } The applicationThe application we want to take as an example is a demo that allows us to view and edit business card information stored on a smart card and to exchange business card information between smart cards. Figure 4 shows what this application might look like.It’s good programming practice to separate the smart card-specific part of the application from the application logic and user interface, so we encapsulate all card-related things into a class named BusinessCard. In this class, we need two attributes for storing references to a SmartCard object and to a BusinessCardProxy object:public class BusinessCard implements CTListener { SmartCard card = null; BusinessCardProxy businessCardProxy = null; With the first call, the constructor starts the OpenCard Framework. From this moment on, OpenCard is running and will create events when cards are inserted or removed. The second call registers the new object with the card terminal registry as a listener for card terminal events. In the case that a card was present in a slot before the constructor was invoked, the third call lets the card terminal registry create card insertion events for the cards that are already present. These events are only sent to the object passed as the only parameter, in this case the new business card object: BusinessCard() throws BusinessCardException { try { SmartCard.start (); CardTerminalRegistry.getRegistry().addCTListener(this); CardTerminalRegistry.getRegistry().createEventsForPresentCards(this); } catch (Exception e) { // Do exception handling } } The cardInserted and cardRemoved methods are called by the OpenCard Framework on card insertion or card removal events, respectively. When a card is inserted, we try to get a SmartCard object and a BusinessCardService object for it:public void cardInserted(CardTerminalEvent ctEvent) { try { card = SmartCard.getSmartCard(ctEvent); businessCardProxy = (BusinessCardProxy) card.getCardService(BusinessCardProxy.class, true); } catch(Exception e) { // do exception handling } } On card removal, the references to these objects are invalidated again:public synchronized void cardRemoved(CardTerminalEvent ctEvent) { card = null; businessCardProxy = null; } These are the access methods for getting and setting business card information on the card. As the methods of the card service are quite high level, all that must be done is to get or set the business information attributes using the appropriate methods of the business card proxy service. As we want to assure that the business card information entries are read or written in atomic operations, we obtain exclusive access to the card by calling the method beginMutex of the smart card object before accessing the entry and do not release it before all fields of the particular entry are processed, as shown in the code sample below. The call to the method endMutex that releases exclusive access to the card is made from the final statement to assure that it is performed under all circumstances. This is important, because failing to release exclusive access to a card would make the card inaccessible.public BusinessInfo getBusinessInfo(int index) throws BusinessCardException { try { card.beginMutex(); return new BusinessInfo(businessCardProxy.getName(index), businessCardProxy.getTitle(index), businessCardProxy.getEmail(index), businessCardProxy.getPhone(index), businessCardProxy.getAddress(index)); } catch (Throwable e) { throw new BusinessCardException(); } finally { card.endMutex(); } } public void setBusinessInfo(int index, BusinessInfo businessInfo) throws BusinessCardException { try { card.beginMutex(); businessCardProxy.setName(index, businessInfo.getName()); businessCardProxy.setTitle(index, businessInfo.getTitle()); businessCardProxy.setEmail(index, businessInfo.getEmail()); businessCardProxy.setPhone(index, businessInfo.getPhone()); businessCardProxy.setAddress(index, businessInfo.getAddress()); } catch (Throwable e) { throw new BusinessCardException(); } finally { card.endMutex(); } } With the last method, we call the static method shutdown of the SmartCard class, which shuts down the OpenCard Framework. public void close() { SmartCard.shutdown(); } } In this section, we explained how the components required to support a Java Card applet within the OpenCard Framework — a card applet proxy card service and the appropriate card service factory — can be implemented and how an application may use this support to conveniently access the Java Card applet.ConclusionIn this article we have given a short introduction to the OpenCard Framework, with a focus on OpenCard card services. We have also presented a flexible concept for supporting Java Cards through card applet proxy services for OpenCard. Finally, we provided a tutorial on how to write card applet proxy services for Java Card applets.Thomas received a degree in computer science from the University of Karlsruhe, Germany at the age of 23 and has been with IBM Global Smart Card Solutions for three years. He started programming distributed smart card middleware in C/C++ and jumped on the Java train when work on the OpenCard reference implementation began in January 1997. His tasks in the OpenCard team are mainly consulting and architecture, contributing smart card expertise and implementation of card services. In parallel, he also works as a consultant for other smart card-related projects at IBM. Java