Previous Table of Contents Next


Java Card API Development

The application programming interface (called the class library in Java-speak) provided on the first commercial Java Card smart card, Schlumberger’s Cyberflex 1.0, was modest albeit useful and had very much an ISO 7816-4 flavor. This card went on sale in a Prerelease developer’s series in May 1997.

Almost immediately, Java aficionados and object-oriented smart card developers started to propose improved class libraries for the Java Card. Java Card class libraries on a smart card are very size constrained. The byte codes of the class library itself displace applications and user data in NVM and the native functions needed to support the library compete for space in ROM. The API design preferred by many smart card vendors and application writers was a small core of services available on every Java Card smart card with industry- and application-specific extensions available on some Java Cards smart card. The Java Card Forum, a consortium of both card manufacturers and card issuers, participates heavily in the effort to define the Java API core services. Consult http://www.javacardforum.org for the latest news on this activity.

Sun Microsystems, through its JavaSoft subsidiary, is also very active in class library definition with its Java Card 2.0 API, which it released in October 1997. The JavaSoft Java Card 2.0 API encompasses more of the environment than simply an API, since it defines how an application must be structured, how it must be written, how it must handle communication with the terminal, and what its security policies must be. There is to date no commercially available smart card that implements the entire JavaSoft Java Card 2.0 specification. The details and documentation for the JavaSoft API can be found at http://java.sun.com/products/javacard.

Visa has announced that it will base its Open Technology Platform smart card on the Java Card specification, so you can expect that there will be a Visa Java Card class library sometime in the relatively near future. Schlumberger has followed Cyberflex 1.0 with Cyberflex 2.0 Core and Cyberflex SIM, which is a Java Card-based GSM SIM card with its own class libraries. In fact, there is no reason to doubt that card-side APIs will not be buffeted by exactly the same marketplace and technology evolution pressures as reader-side APIs.

One of the nice things about a smart card that contains a virtual machine is that you can load your own libraries onto the card and hence create your own card-side API.

Schlumberger’s Java Card 1.0 API

The native function suite underlying Schlumberger’s Cyberflex 1.0 Java Card API is a direct descendent of Schlumberger’s Multiflex smart card discussed at length in Chapter 6, “Smart Card Software Development Tools.” The FlexCash card discussed in Chapter 11, “The FlexCash Card: An E-commerce Smart Card Application,” is built using this API and is the ISO-7816 card emulator shown in Listing 8.1.

Listing 8.1. Schlumberger’s Java Card version 1.0 API.

public class AdminApp
{
  // Constants used throughout the program
  static final byte BUFFER_LENGTH = (byte)0x50;

  static final byte ACK_SIZE                  = (byte)1;
  static final byte ACK_CODE                  = (byte)0;
  static final byte OS_HEADER_SIZE            = (byte)0x10;
  static final byte GPOS_CREATE_FILE          = (byte)0xE0;

  static final byte ST_INVALID_CLASS          = (byte)0xC0;
  static final byte ST_INVALID_PARAMETER      = (byte)0xA0;
  static final byte ST_INS_NOT_SUPPORTED      = (byte)0xB0;
  static final byte ST_SUCCESS                = (byte)0x00;
  static final byte ST_NO_RETURN              = (byte)0xFF;

  static final byte ISO_COMMAND_LENGTH        = (byte)5;

  static final byte ISO_READ_BINARY           = (byte)0xB0;
  static final byte ISO_UPDATE_BINARY         = (byte)0xD6;

  static final byte ISO_READ_RECORD           = (byte)0xB2;
  static final byte ISO_UPDATE_RECORD         = (byte)0xDC;

  static final byte ISO_INIT_APPLICATION      = (byte)0xF2;
  static final byte ISO_VERIFY_KEY            = (byte)0x2A;
  static final byte ISO_SELECT_FILE           = (byte)0xA4;

  static final byte ISO_DIRECTORY             = (byte)0xA8;
  static final byte ISO_GET_RESPONSE          = (byte)0xC0;
  static final byte ISO_DELETE_FILE           = (byte)0xE4;
  static final byte ISO_GET_FILE_INFO         = (byte)0xF8;

  static final byte GPOS_SET_ACL              = (byte)0xFC;

  static final byte ISO_VERIFY_CHV            = (byte) 0x20;
  static final byte ISO_CHANGE_CHV            = (byte) 0x24;
  static final byte ISO_UNBLOCK_CHV           = (byte) 0x2C;

  static final byte ISO_CLASS                 = (byte)0xC0;
  static final byte ISO_APP_CLASS             = (byte)0xF0;

public static void main () {

    byte pbuffer[] = new byte[ISO_COMMAND_LENGTH];
    byte dbuffer[] = new byte[BUFFER_LENGTH];
    byte ackByte[] = new byte[ACK_SIZE];
    byte chvbuffer[]= new byte[8];

    // I would like to put that on the stack
    byte dirbuffer[] = new byte[16];

    short fileId;
    short offset;
    byte bReturnStatus;
    byte bTemp;

    _OS.Execute((short)0,(byte)0);

    /* Initialize Communications */
    dbuffer[0]=(byte)0x3B; dbuffer[1]=(byte)0x32; dbuffer[2]=(byte)0x15;
    dbuffer[3]=(byte)0x00; dbuffer[4]=(byte)0x49; dbuffer[5]=(byte)0x12;

    _OS.SendMessage(dbuffer,(byte)6);

    bTemp = 0;

    do {
        /* Retrieve the command header */
        _OS.GetMessage(pbuffer,ISO_COMMAND_LENGTH,ACK_CODE);

        // Init vars for iteration
        //fileId = 0;
        //offset = 0;

        /* Verify class of the message - Only ISO + Application */
        if ((pbuffer[0] != ISO_APP_CLASS)
         && (pbuffer[0] != ISO_CLASS)) {
            _OS.SendStatus(ST_INVALID_CLASS);

        }
        else {
          /* go through the switch */
          /* Send the acknowledge code */

          // Verify if data length too large
          if (pbuffer[4] > BUFFER_LENGTH) {
            bReturnStatus = ST_INVALID_PARAMETER;
          }
          else
          {
            switch (pbuffer[1]) {

            //  DeleteFile
            //  This command is not present in the regular bootstrap
            case ISO_DELETE_FILE:
            //  SelectFile
            case ISO_SELECT_FILE:
                /* we always assume that length is 2 */
                if (pbuffer[4] != 2) {
                    bReturnStatus = ST_INVALID_PARAMETER;
                }
                else
                {
                    //_OS.SendMessage(ackByte,ACK_SIZE);
                    // get the fileId in the response buffer
                    _OS.GetMessage(dbuffer,(byte)2,pbuffer[1]);
                    // cast dbuffer into a short
                    fileId = (short) ((dbuffer[0] << 8) |
                                      (dbuffer[1] & 0x00FF));
                    if (pbuffer[1] == ISO_DELETE_FILE) {
                        bReturnStatus = _OS.DeleteFile(fileId);
                    }
                    else {
                        bReturnStatus = _OS.SelectFile(fileId);
                    }
                }
                break;

            case ISO_GET_FILE_INFO:
                if (pbuffer[4] != (byte)OS_HEADER_SIZE) {
                    bReturnStatus = ST_INVALID_PARAMETER;
                }
                else
                {
                   // Extract the fileId from P1-P2
                   fileId = (short) ((pbuffer[2] << 8) |
                                     (dbuffer[3] & 0x00FF));
                   if ((bReturnStatus =
                      _OS.GetFileInfo(dbuffer)) == ST_SUCCESS) {
                      _OS.SendMessage(ackByte,ACK_SIZE);
                      _OS.SendMessage(dbuffer,pbuffer[4]);
                   }
                }
                                break;

            case GPOS_SET_ACL:
                if (pbuffer[4] != 0x08) {
                    bReturnStatus = ST_INVALID_PARAMETER;
                }
                else {
                    _OS.GetMessage( dbuffer,
                                     (byte)0x08,
                                     pbuffer[1]);
                    bReturnStatus = _OS.SetFileACL(dbuffer);
                }
                break;

            case ISO_DIRECTORY:
                if (pbuffer[4] > (OS_HEADER_SIZE+1)) {
                    bReturnStatus = ST_INVALID_PARAMETER;
                }
                else
                {
                    _OS.BackupFileStatus();
                    _OS.SelectCD();
                    // If info asked on current directory, no need to
                    // select a record
                    if (pbuffer[3] != 0) {
                        if ((bReturnStatus =
                            _OS.SelectRecord((byte)(pbuffer[3] !=
                             ST_SUCCESS){
                            break;
                        }
                        _OS.InitFileStatus();
                    }

                    // Get the header information for the file
                    bReturnStatus = _OS.GetFileInfo(dbuffer);

                    // Reformat the message
                   _OS.RestoreFileStatus();
                    if (bReturnStatus == ST_SUCCESS) {
                        ackByte[0] = pbuffer[1];
                        _OS.SendMessage(ackByte,(byte)1);
                       _OS.SendMessage(dbuffer,pbuffer[4]);
                    }
                }
                break;

            // CHV and key manipulation routines
            case ISO_VERIFY_CHV:
            case ISO_CHANGE_CHV:
            case ISO_UNBLOCK_CHV:
            case ISO_VERIFY_KEY:
                // Get key number
                _OS.GetMessage(dbuffer,pbuffer[4],pbuffer[1]);

                if (pbuffer[1] == ISO_VERIFY_KEY) {
                bReturnStatus = _OS.VerifyKey(  pbuffer[3],
                                                dbuffer,
                                                pbuffer[4]);
                }
                else {
                    // Get a good value for the unblocking flag
                    if (pbuffer[1] == ISO_UNBLOCK_CHV) {
                        pbuffer[2] = 1;
                    }
                    else {
                        pbuffer[2] = 0;
                    }
                    if (pbuffer[1] != ISO_VERIFY_CHV) {
                        for (bTemp=0;bTemp<8;bTemp++) {
                           chvbuffer[bTemp] = dbuffer[bTemp+8];
                        }
                        bReturnStatus = _OS.ModifyCHV ( pbuffer[3],
                                                        dbuffer,
                                                        chvbuffer,
                                                        pbuffer[2]);
                        break;
                    }

                    bReturnStatus = _OS.VerifyCHV(  pbuffer[3],
                                                    dbuffer,
                                                    (byte)0);
                }
                break;

            case ISO_INIT_APPLICATION:
                /* Should send the id of a valid program file */
                //_OS.SendMessage(ackByte,ACK_SIZE);
                _OS.GetMessage( dbuffer,(byte)1,pbuffer[1]);
                // cast dbuffer into a short
                fileId = (short) ((pbuffer[2] << 8) |
                                  (pbuffer[3] & 0x00FF));
                bReturnStatus = _OS.Execute(fileId, dbuffer[0]);
                break;

            case GPOS_CREATE_FILE:
                if (pbuffer[4] != OS_HEADER_SIZE) {
                    bReturnStatus = ST_INVALID_PARAMETER;
                    break;
                }
                //_OS.SendMessage(ackByte,ACK_SIZE);
                // Receive The data
                _OS.GetMessage( dbuffer,pbuffer[4],pbuffer[1]);
                bReturnStatus = _OS.CreateFile(dbuffer);
                break;

            case ISO_UPDATE_RECORD:
            case ISO_UPDATE_BINARY:
                _OS.GetMessage(dbuffer,pbuffer[4],pbuffer[1]);
                // assumes that a file is already selected
                if (pbuffer[1] == ISO_READ_BINARY) {
                    // compute offset from pbuffer[2..3] via casting
                    offset = (short) ((pbuffer[2] << 8) |
                                      (pbuffer[3] & 0x00FF));
                    bReturnStatus = _OS.WriteBinaryFile (offset,
                                                            pbuffer[4],
                                                            dbuffer);
                }
                else {
                    bReturnStatus = _OS.WriteRecord  (dbuffer,
                                                     pbuffer[2],
                                                     pbuffer[3],
                                                     pbuffer[4]);
                }
                break;

            case ISO_READ_RECORD:
            case ISO_READ_BINARY:
                // assumes that a file is already selected
                if (pbuffer[1] == ISO_READ_BINARY) {
                    // compute offset from pbuffer[2..3] via casting
                    offset = (short) ((pbuffer[2] << 8) |
                                      (pbuffer[3] & 0x00FF));
                    bReturnStatus = _OS.ReadBinaryFile (offset,
                                                           pbuffer[4],
                                                           dbuffer);
                }
                else {
                    bReturnStatus = _OS.ReadRecord  (dbuffer,
                                                     pbuffer[2],
                                                     pbuffer[3],
                                                     pbuffer[4]);
                }
                // Send the data if successful
                ackByte[0] = pbuffer[1];
                if (bReturnStatus == ST_SUCCESS) {
                    _OS.SendMessage(ackByte,ACK_SIZE);
                    _OS.SendMessage(dbuffer,pbuffer[4]);
                }
                break;

            default:
                bReturnStatus = ST_INS_NOT_SUPPORTED;
            }
          }

          // Verify we want to return a status
          if (bReturnStatus != ST_NO_RETURN)
              _OS.SendStatus(bReturnStatus);
      }
    }
    while (true);
  }
}


Previous Table of Contents Next