Dr. Dobb's is part of the Informa Tech Division of Informa PLC

This site is operated by a business or businesses owned by Informa PLC and all copyright resides with them. Informa PLC's registered office is 5 Howick Place, London SW1P 1WG. Registered in England and Wales. Number 8860726.


Channels ▼
RSS

C/C++

Java Card Application Development


Feb99: Java Card Application Development

Darryl has been a consultant and entrepreneur in Silicon Valley for the past eight years. He can be reached at dbarnes@ jsource.com.


Sidebar: Java Card History

Smart cards are credit-card-like computing devices used in applications ranging from cash replacement for pay telephones and vending-machine purchases, to security devices for medical records and network services.

As a computing device, smart cards (sometimes referred to as "pocket PCs") currently have limited resources. A typical smart card, for instance, has an 8-bit processor, 8 KB of ROM, 8 KB of EEPROM, and 128 bytes of RAM. This also means that writing software for small systems is more akin to embedded systems development than to desktop or client/server development. Such development typically involves proprietary platforms, APIs, languages, and environments to deal with low-level protocols, memory management, and the like.

Java's portability and other features offer a solution to obstacles hindering smart card development and acceptance. However, there is clearly no way that a standard Java virtual machine can fit within the constraints of the typical smart card platform. Consequently, JavaSoft and others have developed the Java Card specification -- a subset of Java designed for smart card applications. Like standard Java, Java Card is a platform defined by a language specification, virtual machine specification, and API. Essentially, the Java-Card Language Specification (the current version is 2.1) and Java Card Virtual Machine (JCVM) specification are subsets of their standard Java counterparts. However, the Java Card API (JCAPI) has little in common with the standard Java API. In this article, I'll discuss Java Card and present a typical smart card applet.

Commercially, there are a number of Java Card products on the market, including Schlumberger's Cyberflex (http://www .cyberflex.slb.com/), Gemplus's GemXpresso (http://www.gemplus.com/). Several other smart card vendors -- including Bull, Giesecke & Devrient, Motorola, and DeLaRue -- have announced plans to release Java Card-based systems. (Although not a smart card, National Semiconductor's iButton was the first product released to implement Java Card 2.0. iButton is a microcontroller housed in a button-shaped case that's intended to be worn as part of a ring, badge, or watch.) Furthermore, companies such as IBM, Informix, Oracle, and Visa International have endorsed the Java Card specification.

Why Java on Smart Cards?

Java is an interpreted object-oriented language that provides a high level of abstraction, but is slower and "fatter" than other languages. So why Java, when the resource constraints on a smart card are severe?

There is currently no standard language for smart card development. Most smart card applications in use today were custom developed from the ground up, which is a time-consuming process. The industry needed to develop an open platform for smart card application development to facilitate faster growth and decrease time to market. This open platform needed to be secure enough for multiple applications on the card and provide for post-issuance dynamic downloading of application code.

By using Java as a standard application framework, smart card application development is accessible to existing Java developers. Time-to-market and application development costs will also decrease because developers are able to build on top of the standard framework as well as leverage extensions that others create.

Java's innate security features are also appealing for smart card development. The virtual machine creates a sandbox for each applet, securing them from one another. This facilitates the creation of multiapplication cards.

Additionally, in the future, Java's secure class-loading mechanism will allow for the dynamic downloading of applets. The current Java Card specification does not yet include this feature, since dynamic downloading requires bytecode verification, which is expensive to do on the card.

How Does Java Card Differ From Java?

There are differences between Java itself and Java Card. Smart card applications, for instance, contain fairly basic functionality, so multiple thread support is not essential and would unnecessarily consume a lot of space.

Nor is dynamic class loading included in the current specification, although it is likely to be included later to enable post-issuance downloading of applets. For now, classes are statically linked before they are loaded onto the card. As a consequence, applet code can only reference code on the card itself, and a secure installation process is required, which is not yet part of the specification.

Java Card does not provide an explicit Java security manager. Instead, the Java Card Runtime Environment (JCRE) handles all security issues. The JCRE is composed of the card operating system and the JCVM.

Finally, there is no requirement for a garbage collector in the JCVM. This has huge ramifications in the way the card is programmed. And it is the reason that cloning is not supported. The Object class no longer contains a clone() method, and the Clonable interface has been removed. Since there is no semantic for deleting objects in Java, objects that are allocated remain forever on the heap. So, passing objects by value is not practical.

Current implementations of the JCVM use specialized bytecode instructions. (The specific bytecodes are not yet specified so there is currently only source-code compatibility. This will likely change in the near future, however.) The reason for the special bytecodes is that, because the Java stack is 32-bit, everything under the hood in a JVM is 32-bit. When a byte is placed on the stack it still takes up 32 bits. This means that even if you optimize your code by changing all of your integers to bytes, it will make no difference. On the other hand, the JCVM bytecode set contains full support for smaller, primitive types, such as shorts. However, it does not support char, double, float, long, or arrays of more than one dimension.

The integer primitive type is optionally supported, meaning that it is not required to be implemented. The reason is speed. Doing 32-bit arithmetic on an 8-bit processor can be prohibitively slow. The typical smart card CPU runs at only 3.57 MHz. Realistically, integers will not be included in any of the initial implementations on 8-bit cards. (GemXpresso used a 32-bit chip and does support integers. It is the first smart card based on a 32-bit RISC processor.) In short, the JCVM has removed some bytecodes for unsupported features and added others to fully support smaller types.

Java Card exceptions use 2-byte reason codes rather than String-based messages, because the JCVM does not support String objects. Exceptions now have get/setReason() methods, and exceptions are not generally instantiated directly. Instead, each has a static method called throwIt(), which takes a reason code as a parameter and then throws the system instance of the exception. This saves space on the heap.

The JCAPI is very different from the standard Java API. Almost none of the standard Java classes are supported, including the wrapper classes for the supported primitive types as well as the String class. The JCAPI is composed of one core package (java card.framework) and three extension packages (java cardx.framework, java-cardx.crypto, and java cardx.crypto.enc). The "x" in java cardx stands for extension. The core package, java card.framework, defines all of the essential classes needed to create a Java Card applet. There is no relationship between a smart card applet and a java.applet.Applet.

The java cardx.framework contains all the classes that deal with the smart card file system. It was originally meant to contain additional nonfile-system-extension classes, which is why the name does not contain the words "file systems." The java cardx.crypto package contains classes that deal with cryptography, and the java cardx.crypto.enc package is meant to contain exportable encryption algorithms.

Development Process

There are five steps involved in the Java Card development process:

  1. You can compile your applications using any standard Java development environment, such as the JDK or Symantec's Café.

  2. Because Java Card defines a subset of Java, you must check to ensure that only features supported by Java Card are present. This step is called "verification."

  3. After verification, the code is in JVM bytecode format, but needs to be converted to JCVM bytecode. This step is called "conversion."

  4. After conversion, you are ready to load the code onto the card. Currently, there is no standardized file format for loading; however, one is in the works.

  5. The final step is installation of the applet. This is accomplished via a standard ISO 7816-4 INSTALL command. The ISO 7816 standard defines many aspects of smart cards from the physical dimensions to the transmission protocols and common instructions. Although a standard command is used, the installation process is proprietary: How the file is broken up and what security mechanism is utilized is unspecified.

Card/Terminal Communications

The main communication protocol between a smart card and a terminal is called "T=0." Instructions in the protocol are called Application Data Units (APDU). An APDU (see Figure 1) is composed of a 5-byte header and a data field, which is typically 32 bytes. The first byte in the header is the class byte, and the second is the instruction byte. The class and instruction bytes uniquely identify the instruction to be performed by the card. The next two bytes are instruction-dependent parameters, which are referred to as P1 and P2. The last byte, called "Le," specifies the number of bytes being sent in the data field. You will define many of these instructions yourself. However, many are also defined in various smart card standards, such as ISO 7816-4, EMV, and GSM 11.11.

There is a master/slave relationship between the terminal and card. The card responds to an instruction sent from a terminal, but cannot initiate communications on its own. The card ends communication by sending a status word. This is a 2-byte code indicating success or the type of failure.

When the card receives an APDU from the terminal, it examines the class and instruction codes to determine what action to take. The parameter bytes may or may not contain information, depending on the instruction. Le will be nonzero if there is data to be retrieved in the data field. The card then sends either data of its own followed by a status word, or just the status word.

The Loyalty Applet

Listing One is a sample Java Card applet -- a loyalty application. A loyalty system is one where points are accumulated toward some reward, like a frequent flyer program. The applet has four functions: validate PIN, crediting points, debiting points, and getting the balance.

The validate PIN instruction validates a PIN value against the value in the card. This PIN has to be validated before the other instructions can be executed. The credit instruction adds the given value to the point counter, and the debit instruction removes points. The "get balance" instruction returns the current points total on the card.

The class byte for all of the instructions in this applet is specified as 0xAA. Each instruction is identified by a unique instruction byte. For example, INS_GETBALANCE has instruction byte 0x20. In this case, when the terminal wants to get the balance from the card, it sends an APDU of 0xAA20000002. The 0x02 in the last byte indicates that the terminal is expecting two bytes to be returned from the card.

The applet is composed of only one class. I start with the java card.framework.Applet class in Example 1. This class contains the "lifecycle" methods for the applet: install(), select(), process(), and deselect().

The install() method (Example 2) is called during installation of the applet. It is a static method. The idea is to instantiate all of the objects that will be needed during the lifetime of the applet in this method. Remember, because there is no garbage collection, you do not want to instantiate new object instances every time the card is executed.

The first thing I do is instantiate the Loyalty applet itself. Next, I instantiate an OwnerPIN object, specifying that the PIN gets five failed attempts and that the PIN length is eight. An OwnerPIN object is a PIN that has methods for updating the PIN's value. There is also a ProxyPIN object that can be shared with other applets and that does not contain methods for updating. The updateAndUnblock() method is used to set the PIN value. updateAndUnblock() takes a byte array containing the value, the offset into the array where the value begins, and the length of the value.

At the end of the install() method, I call the register() method, which registers the applet with the JCRE, giving it a reference to the applet.

The process() method (Example 3) is called every time an APDU is sent from the terminal, and it takes an APDU object as a parameter. The APDU class contains methods for sending and receiving data from the terminal, and abstracts a lot of the protocol details. In the first line of Example 3, a reference to the APDU buffer is obtained as a byte array. The class value is validated, and then each instruction is branched based on the instruction byte of the APDU.

The value of the offset for the class byte is referenced using a constant from the ISO class, which is composed of constants for status word values and APDU buffer offsets. ISO was built as a class and not as an interface because interfaces incur more overhead.

Note the modified exception interface. The system instance is thrown via the throwIt() method. This exception is translated by the JCRE into a status word that is returned to the terminal.

Example 4 shows the methods that handle the loyalty functionality. The validatePIN() method validates the PIN from the terminal and must be called first. The setIncomingAndReceive() method retrieves the data field bytes from the terminal and returns the number received. The data in the data-field part of the buffer is the PIN value. The check() method is used to the validate the PIN. It also manages the number of failed attempts. If the PIN is correct, then a flag is set; otherwise, an exception is thrown.

In the debit() method, the debit value is stored in the parameter bytes (P1 and P2). The JCAPI contains a Util class that has methods for converting shorts to bytes and vice versa. In this case, the getShort() method is used to convert the two bytes into a short value.

In the getBalance() method, the APDU method setOutgoingAndSend() is used to send data back to the terminal. First, the data is placed on the APDU buffer, then the method is passed the offset and length of the data to be sent. The APDU buffer is assumed to contain the data.

Conclusion

The loyalty applet illustrates some of the issues involved in writing Java Card applets. Applets tend to have a fairly limited amount of functionality. After all, a smart card is well suited for only so many things. On the other hand, the JCAPI does not completely abstract all of the smart card specific details you need to take into account when writing an applet, so becoming a truly competent Java Card developer does have a learning curve.

DDJ

Listing One

package article;import javacard.framework.*;
public class Loyalty extends Applet
{
    // these attributes are made static because they 
    // are referenced in the static install method
    protected static short sPoints = 0;
    protected static byte[] baBuffer;
    // CLA & INS definitions
    public static final byte CLA_LOYALTY = (byte)0xAA;
    public static final byte INS_VALIDATE_PIN = 0x10;
    public static final byte INS_GETBALANCE = 0x20;
    public static final byte INS_DEBIT = 0x30;
    public static final byte INS_CREDIT = 0x40;


</p>
    // PIN attributes
    public static final byte PIN_LENGTH = 8;
    protected OwnerPIN pin;
    protected static byte[] baPINValue = {0x0A, 0x0B, 0x0A, 0x0B, 
                                            0x0A, 0x0B, 0x0A, 0x0B};
    protected transient boolean zIsAuthorized = false;
    
    public static void install(APDU _apdu)
    {
        Loyalty loyalty = new Loyalty();
        
        // initialize user PIN
        loyalty.pin = new OwnerPIN((byte)5, PIN_LENGTH);
        loyalty.pin.updateAndUnblock(baPINValue, (short)0, PIN_LENGTH);
        
        // register the applet
        loyalty.register();
    }
    public void process(APDU _apdu)
    {
        // get reference to apdu buffer
        byte[] baBuffer = _apdu.getBuffer();
        // verify class
        if(baBuffer[ISO.OFFSET_CLA] != CLA_LOYALTY)
            ISOException.throwIt(ISO.SW_CLA_NOT_SUPPORTED);
        // branch to each instruction
        switch(baBuffer[ISO.OFFSET_INS])
        {
            case INS_VALIDATE_PIN:
                this.validatePIN(_apdu);
                break;
            case INS_DEBIT:
                this.debit(_apdu);
                break;
            case INS_CREDIT:
                this.credit(_apdu);
                break;
            case INS_GETBALANCE:
                this.getBalance(_apdu);
                break;
        }
    }
    protected void validatePIN(APDU _apdu)
    {
        // get reference to apdu buffer
        baBuffer = _apdu.getBuffer();
        // get data from terminal
        byte bLc = (byte)_apdu.setIncomingAndReceive();
        // check length being sent
        if(bLc != PIN_LENGTH)
            ISOException.throwIt(ISO.SW_WRONG_LENGTH);
        // validate PIN
        if(pin.check(baBuffer, ISO.OFFSET_CDATA, PIN_LENGTH))
            zIsAuthorized = true;
        else
            ISOException.throwIt(ISO.SW_SECURITY_STATUS_NOT_SATISFIED);
    }
    protected void debit(APDU _apdu)
    {
        // check if PIN is validated
        if(!zIsAuthorized)
            ISOException.throwIt(ISO.SW_SECURITY_STATUS_NOT_SATISFIED);
        // get reference to apdu buffer
        baBuffer = _apdu.getBuffer();
        // get data from terminal
        byte bLc = (byte)_apdu.setIncomingAndReceive();
        // get amount from apdu buffer
        short sAmount = Util.getShort(baBuffer, ISO.OFFSET_P1);
        // don't allow negative balance
        if((sPoints - sAmount) < 0)
            sPoints = 0;
        else // debit account
            sPoints = (short)(sPoints - sAmount);
    }
    protected void credit(APDU _apdu)
    {
        // check if PIN is validated
        if(!zIsAuthorized)
            ISOException.throwIt(ISO.SW_SECURITY_STATUS_NOT_SATISFIED);
        // get reference to apdu buffer
        baBuffer = _apdu.getBuffer();
        // get data from terminal
        byte bLc = (byte)_apdu.setIncomingAndReceive();
        // get amount from apdu buffer
        short sAmount = Util.getShort(baBuffer, ISO.OFFSET_P1);
        // increment balance
        sPoints = (short)(sPoints + sAmount);
    }
    protected void getBalance(APDU _apdu)
    {
        // check access permissions
        if(!zIsAuthorized)
            ISOException.throwIt(ISO.SW_SECURITY_STATUS_NOT_SATISFIED);
        // get reference to apdu buffer
        baBuffer = _apdu.getBuffer();
        // put balance value into apdu buffer
        Util.setShort(baBuffer, ISO.OFFSET_CDATA, sPoints);
        // send balance
        _apdu.setOutgoingAndSend(ISO.OFFSET_CDATA, (short)2);
    }
}

Back to Article


Copyright © 1999, Dr. Dobb's Journal

Related Reading


More Insights






Currently we allow the following HTML tags in comments:

Single tags

These tags can be used alone and don't need an ending tag.

<br> Defines a single line break

<hr> Defines a horizontal line

Matching tags

These require an ending tag - e.g. <i>italic text</i>

<a> Defines an anchor

<b> Defines bold text

<big> Defines big text

<blockquote> Defines a long quotation

<caption> Defines a table caption

<cite> Defines a citation

<code> Defines computer code text

<em> Defines emphasized text

<fieldset> Defines a border around elements in a form

<h1> This is heading 1

<h2> This is heading 2

<h3> This is heading 3

<h4> This is heading 4

<h5> This is heading 5

<h6> This is heading 6

<i> Defines italic text

<p> Defines a paragraph

<pre> Defines preformatted text

<q> Defines a short quotation

<samp> Defines sample computer code text

<small> Defines small text

<span> Defines a section in a document

<s> Defines strikethrough text

<strike> Defines strikethrough text

<strong> Defines strong text

<sub> Defines subscripted text

<sup> Defines superscripted text

<u> Defines underlined text

Dr. Dobb's encourages readers to engage in spirited, healthy debate, including taking us to task. However, Dr. Dobb's moderates all comments posted to our site, and reserves the right to modify or remove any content that it determines to be derogatory, offensive, inflammatory, vulgar, irrelevant/off-topic, racist or obvious marketing or spam. Dr. Dobb's further reserves the right to disable the profile of any commenter participating in said activities.

 
Disqus Tips To upload an avatar photo, first complete your Disqus profile. | View the list of supported HTML tags you can use to style comments. | Please read our commenting policy.