In previous chapters, you have seen code fragments of an electronic wallet applet. This chapter walks you through the process of creating such an applet. The development steps are detailed, from designing the applet through constructing its code. At the end, the chapter provides a discussion of error checking for an applet.
As with any software application development, before sitting down and writing a Java Card applet, you should first go through a design phase. In this phase, you define the architecture of the applet in four steps:
Specify the functions of the applet.
Request and assign AIDs to both the applet ansd the package containing the applet classes.
Design the class structure of the applet programs.
Define the interface between the applet and the host application.
The example wallet applet stores electronic money and supports credit, debit, and check-balance functions. The Java smart card user can add money to the wallet (credit), make purchases or withdrawals (debit), and inquire about the current available balance.
To help prevent unauthorized use of the card, the applet applies a security algorithm. This algorithm requires the user to enter a PIN, a string of at most eight digits. The card user types his or her PIN on a keypad connected to the CAD. The security algorithm causes the card to block after three unsuccessful attempts to enter the PIN. The PIN is initialized according to the installation parameters when the applet is installed and created. The PIN must be verified before any credit or debit transaction can be executed.
A real-world wallet applet would require a much more sophisticated security mechanism to prevent unauthorized access to the wallet.
For simplicity, let's say that the card's maximum balance is $10,000 and that no credit or debit transaction can exceed $100. Thus, Java variables of type short
and byte
can represent the wallet balance and the amount of each transaction, respectively.
The Java classes of the wallet applet are defined in a single Java package. AIDs for the wallet applet and the applet package are defined in Table 12.1.
An AID consists of two parts: an RID (5 bytes long) and a PIX (0 to 11 bytes long). The RID (0xa0, 0x00, 0x00, 0x00, 0x62
) in Table 12.1 is the RID for Sun Microsystems. Your organization must request an RID from the International Standards Organization (ISO). Your organization is responsible for managing the PIX assignment for packages and applets provided by your organization.
As discussed in Chapter 7, a Java Card applet class must extend the javacard.framework.Applet
class, whose public and protected methods are listed in Table 12.2. The applet overrides one or more of these public methods to implement the desired behavior. (For more information, refer to Chapters 7 and 9.) An applet must define and implement the static method install
to create an applet instance and to register the instance with the JCRE by invoking one of the two register
methods.
The process
method in the base Applet
class is an abstract class. Your applet must override it. In the process
method, the applet interprets each APDU command and performs the function specified by the command. Typically, an applet supports a set of APDU commands. See the next section for a discussion of the APDU commands needed by the electronic wallet applet.
The select
or the deselect
method is invoked by the JCRE when the applet is selected or deselected. Your applet can override them to provide initialization or cleanup functions. However, not every applet requires initialization or cleanup.
Table 12.2. Public and protected methods defined in the class javacard.framework.Applet
public static void
| install (byte[] bArray, short bOffset, byte bLength)
|
public boolean
| select ()
|
public void
| deselect ()
|
public abstract void
| process (APDU apdu)
|
public Shareable
| getShareableInterfaceObject (AID client AID, byte parameter)
|
protected final void
| register()
|
protected final void
| register(byte[] bArray, short bOffset, byte bLength)
|
protected final boolean
| selectingApplet()
|
The applet uses the select
or deselect
method in the base class if it does not require additional functions during selection or deselection.
The getShareableInterfaceObject
method is called to return a shareable interface object. The use of this method is covered in Chapter 9. For simplicity, the wallet applet in this chapter does not implement the object sharing function. Interested readers can add the function to the wallet applet by using the code examples in Chapter 9.
Two register
methods and the selectingApplet
method are protected final methods. They are invoked only by applets for registering an applet instance with the JCRE or detecting an applet SELECT APDU command.
An applet running in a Java smart card communicates with the host application at the CAD side by using the application protocol data units (APDUs). In essence, the interface between an applet and its host application is a set of APDU commands that are agreed on and supported by both the applet and the host application.
A Java Card applet should support a set of APDU commands comprising a SELECT APDU command and one or more process APDU commands.
The SELECT command instructs the JCRE to select the applet on the card.
The set of process commands defines the commands that the applet supports. They must be defined according to the intended behavior of the applet.
Java Card technology specifies the encoding of the SELECT APDU command used for selecting applets. As an applet developer, you are free to define the encoding of the process commands of an applet as long as they comply with the structure outlined in ISO 7816-4. That is, the SELECT command and each process command are pairs of command and response APDUs.
For each command APDU, the applet should first decode the value of each field in the header. If the optional data field is included, the applet should also determine the data format and the content. Knowing how to interpret the command and read the data, the applet can then execute the function requested by the command.
For the response APDU, the applet should define a set of status words to indicate the result of processing the corresponding command APDU. During normal processing, the applet returns the success status word (0x9000, as specified in ISO 7816). If an error occurs, the applet must return a status word other than 0x9000 to denote its internal state or a diagnosis of the error. If the optional data field is required in the response APDU, the applet should define what to return.
The wallet applet example supports credit, debit, and check-balance functions. In addition, it supports the VERIFY command for PIN verification. The SELECT command and four process APDU commands for the wallet applet are defined in Tables 12.3–12.12.
Table 12.3. SELECT APDU—command APDU
CLA | INS | P1 | P2 | Lc | Data field | Le |
---|---|---|---|---|---|---|
0x0 | 0xA4 | 0x04 | 0x0 | 0x0A | 0xa0, 0x00, 0x00, 0x00, 0x62, 0x03, 0x01, 0x0c, 0x06, 0x01 | N/A |
The command header (CLA
, INS
, P1
, and P2
) must be coded as in Table 12.3 so that the JCRE can identify it as a SELECT APDU. The data field contains the AID of the wallet applet. The JCRE searches its internal registry table against the AID bytes. If a match is found, the wallet applet is selected, and the SELECT APDU is forwarded to the applet's process
method for further processing.
Table 12.5. VERIFY APDU—command APDU
CLA | INS | P1 | P2 | Lc | Data field | Le |
---|---|---|---|---|---|---|
0xB0 | 0x20 | 0x0 | 0x0 | Length of the PIN data | PIN data | N/A |
CLA
byte denotes the structure of the command.
INS
byte (0x20) indicates a VERIFY instruction.
P1
and P2
are not used and are both set to 0.
The data field contains the PIN.
Table 12.7. CREDIT APDU—command APDU
CLA | INS | P1 | P2 | Lc | Data field | Le |
---|---|---|---|---|---|---|
0xB0 | 0x30 | 0x0 | 0x0 | 1 | Credit amount | N/A |
The data field contains the credit amount
Table 12.9. DEBIT APDU—command APDU
CLA | INS | P1 | P2 | Lc | Data field | Le |
---|---|---|---|---|---|---|
0xB0 | 0x40 | 0x0 | 0x0 | 1 | Debit amount | N/A |
The data field contains the debit amount
Table 12.11. GET BALANCE APDU—command APDU
CLA | INS | P1 | P2 | Lc | Data field | Le |
---|---|---|---|---|---|---|
0xB0 | 0x50 | 0x0 | 0x0 | N/A | N/A | 2 |
The data field of the response APDU contains the balance amount.
Table 12.12. GET BALANCE APDU—response APDU
Data | Status word | Meaning of status word |
---|---|---|
Balance amount | 0x9000 | Successful processing |
In addition to the status words declared in each response APDU command, the interface javacard.framework.ISO7816
defines a set of ISO status words that signal common errors in applets, such as an APDU command formatting error.
Once you've completed the applet design phase, the next phase of writing applets is to construct the applet code. This section provides the wallet applet implementation.
package com.sun.javacard.samples.wallet; import javacard.framework.*; public class WalletApp extends Applet { // codes of CLA byte in the command APDUs final static byte Wallet_CLA = (byte)0xB0; // codes of INS byte in the command APDUs final static byte VERIFY = (byte) 0x20; final static byte CREDIT = (byte) 0x30; final static byte DEBIT = (byte) 0x40; final static byte GET_BALANCE = (byte) 0x50; // maximum wallet balance final static short MAX_BALANCE = 10000; // maximum transaction amount final static byte MAX_TRANSACTION_AMOUNT = 100; // maximum number of incorrect tries before the // PIN is blocked final static byte PIN_TRY_LIMIT =(byte)0x03; // maximum size PIN final static byte MAX_PIN_SIZE =(byte)0x08; // Applet-specific status words: final static short SW_VERIFICATION_FAILED = 0x6300; final static short SW_PIN_VERIFICATION_REQUIRED = 0x6301; final static short SW_INVALID_TRANSACTION_AMOUNT = 0x6A83; final static short SW_EXCEED_MAXIMUM_BALANCE = 0x6A84; final static short SW_NEGATIVE_BALANCE = 0x6A85; // instance variables declaration OwnerPIN pin; short balance; /** * called by the JCRE to create an applet instance */ public static void install(byte[] bArray, short bOffset, byte bLength) { // create a Wallet applet instance new WalletApp(bArray, bOffset, bLength); } // end of install method /** * private constructor — called by the install method to * instantiate a WalletApp instance */ private WalletApp (byte[] bArray, short bOffset, byte bLength){ pin = new OwnerPIN(PIN_TRY_LIMIT, MAX_PIN_SIZE); // bArray contains the PIN initialization value pin.update(bArray, bOffset, bLength); // register the applet instance with the JCRE register(); } // end of the constructor /** * initialize the applet when it is selected */ public boolean select() { // the applet declines to be selected // if the pin is blocked if (pin.getTriesRemaining() == 0) return false; return true; } // end of select method /** * perform any cleanup and bookkeeping tasks before * the applet is deselected */ public void deselect() { // reset the pin pin.reset(); } /** * process APDUs */ public void process(APDU apdu) { // APDU object carries a byte array (buffer) to // transfer incoming and outgoing APDU header // and data bytes between the card and the host // at this point, only the first five bytes // [CLA, INS, P1, P2, P3] are available in // the APDU buffer byte[] buffer = apdu.getBuffer(); // return if the APDU is the applet SELECT command if (selectingApplet()) return; // verify the CLA byte if (buffer[ISO7816.OFFSET_CLA] != Wallet_CLA) ISOException.throwIt(ISO7816.SW_CLA_NOT_SUPPORTED); // check the INS byte to decide which service method to call switch (buffer[ISO7816.OFFSET_INS]) { case GET_BALANCE: getBalance(apdu); return; case DEBIT: debit(apdu); return; case CREDIT: credit(apdu); return; case VERIFY: verify(apdu); return; default: ISOException.throwIt (ISO7816.SW_INS_NOT_SUPPORTED); } } // end of process method /** * add money to the wallet */ private void credit(APDU apdu) { // verify authentication if (!pin.isValidated()) ISOException.throwIt(SW_PIN_VERIFICATION_REQUIRED); byte[] buffer = apdu.getBuffer(); // get the number of bytes in the // data field of the command APDU byte numBytes = buffer[ISO7816.OFFSET_LC]; // recieve data // data are read into the apdu buffer // at the offset ISO7816.OFFSET_CDATA byte byteRead = (byte)(apdu.setIncomingAndReceive()); // error if the number of data bytes // read does not match the number in the Lc byte if (( numBytes != 1 ) || (byteRead != 1)) ISOException.throwIt(ISO7816.SW_WRONG_LENGTH); // get the credit amount byte creditAmount = buffer[ISO7816.OFFSET_CDATA]; // check the credit amount if (( creditAmount > MAX_TRANSACTION_AMOUNT) || ( creditAmount < 0 )) ISOException.throwIt(SW_INVALID_TRANSACTION_AMOUNT); // check the new balance if ((short)( balance + creditAmount) > MAX_BALANCE) ISOException.throwIt(SW_EXCEED_MAXIMUM_BALANCE); // credit the amount balance = (short)(balance + creditAmount); return; } // end of deposit method /** * withdraw money from the wallet */ private void debit(APDU apdu) { // verify authentication if (! pin.isValidated()) ISOException.throwIt(SW_PIN_VERIFICATION_REQUIRED); byte[] buffer = apdu.getBuffer(); byte numBytes = (byte)(buffer[ISO7816.OFFSET_LC]); byte byteRead = (byte)(apdu.setIncomingAndReceive()); if (( numBytes != 1 ) || (byteRead != 1)) ISOException.throwIt(ISO7816.SW_WRONG_LENGTH); // get debit amount byte debitAmount = buffer[ISO7816.OFFSET_CDATA]; // check debit amount if (( debitAmount > MAX_TRANSACTION_AMOUNT) || ( debitAmount < 0 )) ISOException.throwIt(SW_INVALID_TRANSACTION_AMOUNT); // check the new balance if ((short)( balance - debitAmount ) < (short)0) ISOException.throwIt(SW_NEGATIVE_BALANCE); balance = (short) (balance - debitAmount); } // end of debit method /** * the method returns the wallet's balance */ private void getBalance(APDU apdu) { byte[] buffer = apdu.getBuffer(); // inform the JCRE that the applet has data to return short le = apdu.setOutgoing(); // set the actual number of the outgoing data bytes apdu.setOutgoingLength((byte)2); // write the balance into the APDU buffer at the offset 0 Util.setShort(buffer, (short)0, balance); // send the 2-byte balance at the offset // 0 in the apdu buffer apdu.sendBytes((short)0, (short)2); } // end of getBalance method /** * verify the PIN */ private void verify(APDU apdu) { byte[] buffer = apdu.getBuffer(); // receive the PIN data for validation. byte byteRead = (byte)(apdu.setIncomingAndReceive()); // check pin // the PIN data is read into the APDU buffer // starting at the offset ISO7816.OFFSET_CDATA // the PIN data length = byteRead if (pin.check(buffer, ISO7816.OFFSET_CDATA,byteRead) == false) ISOException.throwIt(SW_VERIFICATION_FAILED); } // end of verify method } // end of class Wallet
The next step in coding a Java Card applet is to provide for error checking. Error checking is essential in any software development and typically requires a significant amount of the total development work.
Error checking is particularly important in smart card application development. An undetected error can cause the card to be blocked or result in the loss of critical data stored in the card.
Once an applet is installed in a smart card, it interfaces with the outside world only through APDU commands. Even though ISO 7816 sets the protocol standard, the applet and the host application must agree on the significance of the value in each field of an APDU command.
In the wallet applet code, much attention is devoted to detecting illegal or ill-formatted commands. In this example, the APDU commands are examined to ensure that the APDU header bytes (CLA, INS, P1, and P2) are set correctly, that the Lc or Le field matches the data field length, that the PIN has been verified before a transaction, and that the balance and transaction amounts are valid.
In general, before performing the task indicated by an APDU command, an applet must validate the command according to the requirements of the applet. An applet should confirm the following before attempting to carry out a command:
The APDU command is supported by the applet.
The APDU command is well formatted.
The APDU command meets the security or other internal conditions of the applet.
While executing the task, the applet should also detect whether the task can be performed successfully without leaving the applet in an invalid state.
As important as error checking is, it is just as important that the applet report to the host errors that occur. This ensures that the host application knows what is going on inside the applet. When an error is detected, a Java Card applet will normally terminate the process and throw an ISOException
containing a status word to indicate the processing state of the applet. If the ISOException
is not handled by the applet, it will be caught by the JCRE, which then retrieves the status word and reports it to the host.
The next step during the applet development process is to test the wallet applet in a Java Card simulation or emulation environment. You can find instructions on how to complete applet testing at the Web site http://java.sun.com/docs/books/series/javacard. You can also download the Java Card development tools, the Java Card API classes, and related documents from the Java Card Web site http://java.sun.com/docs/books/javacard.
3.14.70.203