![]() |
|
![]() |
![]() |
![]() |
![]() |
![]() |
![]() |
|
![]() |
![]() |
Java Developer
SummaryBy Rinaldo Di Giorgio with Mike Montgomery
Loading Java Cards can be a confusing process requiring proprietary development tools that work on only one platform. Sun Microsystems is developing a standard in this area for Java Card licensees. But what do you do in the interim: wait for the standard or use proprietary tools? This article demonstrates easy methods for loading Java Cards using loading information published by various card manufacturers. It also shows you that Java Card development can be platform-independent and can make use of 100-percent-Java development tools. In this article, Rinaldo and guest contributor Mike Montgomery provide two card services for loading Java Cards -- one for Schlumberger's CyberFlex 16K and one for Dallas Semiconductor's Java iButton. (The latter has always supported cross-platform development.) This article is intended for any developer looking to use Java Cards from manufacturers that support published interfaces for downloading their cards. The article assumes the reader has some knowledge of smart cards, Java Cards, and OpenCard. (3,000 words)
n prior articles, we've discussed methods for connecting smart card readers to your system so that you can use your Java Card. In this article we'll build on that knowledge, teaching you how to load Java Cards from a Web-delivery perspective.
This article assumes some knowledge of smart cards, Java Cards, and OpenCard. See the Resources section below for links to prior Java Developer columns that have covered these subjects.
Little programs that load card apps directly from a PC are a dime a dozen. These programs are uninteresting, however, because they require you to download files, then start up programs, and so on. Our approach is to allow files on Web pages containing interesting Java Card applications to be fed directly onto your personal Java Card.
We've been focusing on using PC/SC, the C standard for loading and managing cards on WIN32 machines, and OpenCard, the Java based standard. As a developer you have three choices for developing dynamically loadable Java Card applications for mass markets: cook your own for each combination of different cards and readers; use PC/SC and follow Microsoft; or use OpenCard, which leverages PC/SC when possible yet allows you to extend and have equivalent functionality on Windows platforms.
The best choice is OpenCard, because PC/SC doesn't work on a lot of platforms that OpenCard works on -- such as computers smaller than PalmPilots, set-top boxes, and network computers, to name a few. Additionally, PC/SC doesn't support dynamic and flexible automatic loading of services to communicate with an inserted iButton or smart card as part of the standard. Writing your own loading application only works if you can afford to do so in terms of support and maintenance. And it doesn't leverage the efforts of others, which means you'll have to supply the different program's plumbing yourself.
Why OpenCard?
Java Cards are small, personal, secure storage devices with a tamper-resistant architecture. This makes Java Cards ideal for storing important personal information and for financial applications, generally any application where authentication, Integrity, and secrecy are desired. Java Cards are accessed through special devices that attach to your computer or are already on your computer, much like ATM cards are accessed through ATM machines and scanners. Some potential users of Java Cards are concerned about writing applications that work the same on all the various entrance ramps to the Internet, intranet and extranet. OpenCard provides a solution to the problem of interfacing different devices for reading cards to these platforms.
The following are some of the advantages of using OpenCard:
We were able to develop and load an application for the Java iButton on systems that support Java only. The folks at iButton started with a 100-percent-Java development environment over two years ago, so when using iButtons, you never have to pop into an application written in C. (Nothing against C, but C applications are often not portable due to the user interface bindings.) We didn't realize we could do this with any smart card out there until Schlumberger Engineering Specialist Mike Montgomery told us this was actually relatively easy. We also wanted to be able to use OpenCard and write one application that would load any Java Card as long as the specific implementation was available.
In this article, we'll show you how to load Java Cards with OpenCard services that implement the specific protocol for each of the devices. We'll cover two devices: Dallas Semiconductor's Java iButton and Schlumberger's CyberFlex 16K card. In each case you'll need some information from the card manufacturer on how to load the card. Some manufacturers, like Schlumberger, provide this information with sample programs, others just list the protocol without samples, and others do not even make it available. Eventually Sun and OpenPlatform will resolve this issue by providing converters and security to ensure that cards are loaded easily and safely with trusted programs.
We'll take two off-the-shelf cards and readers and provide the source code for loading specific Java Card devices as an OpenCard service. We're concentrating here on the process of putting the program onto the card. We're at very low level here -- kind of like writing individual sectors to a disk. Eventually you can execute the copy command, but how does the copy command really work? It moves every datum from the source to the destination. Smart cards require the same, in addition to wanting the data to be trusted and verified.
The complete cycle: creation, conversion and secure loading
|
One of the challenges for credit card companies and banks is defining card ownership. All sorts of business issues come into play here, given the widespread functionality of the cards. Java Cards can support multiple applications, however, this isn't quite like putting a file on your disk drive. If you read your credit card agreement, you'll see that you don't own the card. Which means legally you may not be entitled to put more apps on your Java Card without permission. Suppose a card-owning business entity decides to allow other companies -- possibly competing companies -- to put applications on its cards?
What's the process for this? Sun and Visa are busy working on this in the form of a standard for loading Java Card applets, as described briefly in the Java Card 2.1 Specification and the OpenPlatform Standard from Visa. (For more information on these standards, see Resources.) Visa recognized this issue very early on and is proposing a standard that will allow smart card applications to be written and loaded onto your card by many different institutions.
The OpenCard Framework and the components that can be plugged in
|
Smart cards should someday be able to hold several applications at once -- one from American Express, one from MBNA, and one from your personal bank, for example. The big question is whether (and how soon) competing institutions will realize some consumers want only one card, not several different cards boasting a lot of pretty artwork!
Security and loading the app
Loading an application securely onto a Java Card can be accomplished in a number of ways, but generally you need to take care of the following -- using the existing, well-established security procedures Java enables.
Accomplishing the tasks listed above is straightforward using the Java Plug-in. The Java Plug-in provides a consistent execution environment for Java across different browsers and platforms. If you get tired of fighting your browser, take a look at the Java Plug-in in the Resources below.
When deploying a Java Card loading system in the real world, you must deal with the security issues raised in this section. OpenCard promises to be a big help in this area.
Let's put aside the security concerns for a short time and get on to loading the card.
The structure of a CardService loader
In a previous Java Developer column, "Write OpenCard card services for Java Card applets," Thomas Schaeck covered the anatomy of a card service. 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. For a refresher on card services, or to gather more details, consult that article.
The value of the Java application provided in this article is that it can load any Java Card on a number of different readers. This is a very useful program because we'll be seeing many card readers and many cards from various manufacturers. As stated earlier, for this article's purposes we are interested only in OpenCard solutions.
To load Java Cards using OpenCard, we must have the card-specific information to load cards, discussed earlier, and a CardService
that can be called to load cards. Both should be available from the manufacturer of the card.
First, we will discuss the creation of a CardService
in terms of the OpenCard framework requirements. Then we will examine the code that takes a Java Card application and loads it onto the card or iButton.
We use the model of a proxy loader for inserted cards, rings, and other devices. When a card is inserted into an OpenCard terminal, OpenCard makes an attempt to load a CardService
for it. Loading a CardService
is something you can control by looking at the ATR (answer to reset) and the AIDs (application identifiers). In prior articles we've discussed the ATR and pointed out that this field is not really guaranteed to be unique across all cards. (For more on the ATR, see last month's column.) ATRs are defined in ISO7816-3 (this spec is available for a fee), but there is no requirement that the ATR be unique. Besides, an ATR identifies a card and should not be used to identify applications on the card. In future articles, we'll discuss how to register applications.
AIDs are guaranteed to be unique, in that you apply for an AID and register it. The smart card industry would benefit from using the Web to register and download applications using AIDS. The proxy for loading is designed as an interface and requires specific loaders to provide implementations.
We're finally ready to load a card. We're assuming the OpenCard framework has already determined which card is loaded and which loader to use (using the imperfect ATR scheme). The proxy for loading is called, and it will send a series of APDUs to the card to get the application loaded onto the card.
The first interface is the Java CardLoader
proxy interface, which determines the loader to call. You can easily extend this reader to support more loaders by providing specific implementations. The Java CardLoaderProxy
interface is very simple and consists of the following three methods:
import java.util.Hashtable;
import java.io.ByteArrayInputStream;
/**
* Manufacturer specific loader need to implement this interface
*
* @param Hashtable Properties that can be used by instance of loader
* @param ByteArrayInputStream The applet code to download in a format that
the card understands
* @returns Hashtable Properties of load
*/
public interface Java CardLoaderProxy {
public Hashtable downloadApplet(Hashtable loadProperties,
ByteArrayInputStream
bis);
public int getBytesSent();
public int getBytesToSend();
}
The most important method in this interface is downloadApplet
, which has two arguments. Rather than having myriad methods for setting various load attributes, here we use a property sheet so that additional features can be added easily. The second formal parameter, ByteArrayInputStream
, contains the bytes representing the class file in the manufacture-specific format. Dallas Semiconductor and Schlumberger provide converter programs to convert the class file produced by any certified Java compiler into the device-specific bytecodes. Ideally, the converter program will be written in Java so that it is portable. The utility programs from Dallas Semiconductor are written in Java. Every loader for a particular target such as a card, ring, or other device needs to provide a card service. This card service registers the implementation described above specific to the card that has been recognized using its ATR.
Code for the CyberFlex card
The code sample below for a CyberFlex card registers a LoadCyberflex
class, which is an implementation of Java CardLoaderProxy
. The Java CardLoaderProxyFactoryCyberFlex
answers queries from the card service registry when the CardService
scheduler is looking for a service for a particular card. This is very useful when, say, you have 10 services written for different types of devices. How do you decide which one to use? OpenCard left this to the application programmer to figure out: the CardServices
will be called in the order registered, and you can take the desired action.
A more complete implementation of this loader example would be to query for any applications on the card, and prompt you, the user, for information about applications on the card. For example, it may inquire whether you really want to delete your 3.9% APR MasterCard and replace it with a 6.9% MasterCard?
Here's the code for the CyberFlex card:
import java.lang.reflect.Constructor;
import java.util.Enumeration;
import java.util.Vector;
import java.util.Hashtable;
import opencard.core.OpenCardConstants;
import opencard.core.service.CardService;
import opencard.core.service.SmartCard;
import opencard.core.service.CardServiceScheduler;
import opencard.core.service.CardServiceException;
import opencard.core.service.CardServiceFactory;
import opencard.core.terminal.CardID;
import javaworld.oc.javacard.loaders.schlumberger.LoadCyberFlex;
/**
* Creates instances of the card service Java CardLoaderProxy
* for Java Cards.
*
* @author Thomas Schaeck (schaeck@de.ibm.com)
* @author Rinaldo Di Giorgio(rinaldo.digiorgio@sun.com)
*
* @version $Id: Java CardLoaderProxyFactoryCyberFlex.java,v Exp $
*/
public class Java CardCCProxyServiceFactory extends CardServiceFactory
{
/** Holds the services which are available. */
private static Vector services_ = new Vector();
private final static byte BZ = (byte)0;
private static byte cyberFlexATR[] =
{(byte)0x3B,(byte)0x16,(byte)0x94,(byte)0x81,(byte)0x10,(byte)05,(byte)00,(byte)00,(byte)0x51};
CardID cyberFlexCID;
static
{
// This factory can only create instances of Java CardLoaderProxy.
services_.addElement(LoadCyberFlex.class);
}
/**
* Constructs an object of this class.
*/
public Java CardLoaderProxyFactoryCyberFlex()
{
try {
cyberFlexCID = new CardID ( cyberFlexATR );
} catch ( Exception e ) {
}
System.out.println("Java CardLoaderProxyFactoryCyberFlex()");
}
/**
* Returns an enumeration of known CardService classes.
*
* @param cid The CardID for which to enumerate.
*
* @return An Enumeration of class objects.
*/
protected Enumeration cardServiceClasses(CardID cid)
{
return services_.elements();
}
/**
* Indicates whether this CardServiceFactory "knows" the smart
card
* OS represented by cid and might be able to instantiate
* CardServices for it.
*
* @param cardID A CardID received from a Slot.
*/
public boolean knows(CardID cardID)
{
System.out.println("Java CardLoaderProxyFactoryCyberFlex():knows");
if ( cardID.equals( cyberFlexCID ) ) {
System.out.println(" yes.");
return true;
} else {
System.out.println(" no.");
return false;
}
}
/**
* Normalize the card operating system values.
*
* @param cid The CardID object to examine.
*
* @return The normalized COS value.
*/
protected int normalizedCOSValue(CardID cid)
{
if(cid.isOpenCardCompliant())
{
return cid.getCardOS();
}
else
{
return 0;
}
}
}
CyberFlex 16K loader
The above code is registered with OpenCard as a service, and when a CyberFlex 16K card is inserted into any of several different types of readers, the LoadCyberFlex
class included below is called to perform the loading process. Our implementation of the class below is intended for teaching purposes only. In production you would have to add options for the following:
Here's the LoadCyberFlex
class:
import java.io.*;
import opencard.core.terminal.*;
import opencard.core.service.*;
import javaworld.oc.javacard.loaders.Java CardLoaderProxy;
import java.util.Hashtable;
public class LoadCyberFlex extends CardService implements Java CardLoaderProxy
{
private short containerSize = 4100;
private short cid = 200;
private byte reservedOne[] = {(byte)0xFF, 0, 0, 0, 0, 0, 0};
private short programFileSize = 3000;
private short dataFileSize = 950;
private byte aid[] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14,
15, 16};
private byte aidLength = 16;
private int index = 0;
private byte ZERO = (byte)0x00;
private byte FF = (byte)0xFF;
private byte AA = (byte)0xAA;
private int appletSize = 0;
private short offset = 0;
ByteArrayOutputStream baos = new ByteArrayOutputStream();
private final byte selectRoot[] = {(byte)0xA0, (byte)0xA4, (byte)0x00,
(byte)0x00, (byte)0x02, (byte)0x3F, (byte)0x00};
private final byte selectCid[] = {(byte)0xA0, (byte)0xA4, (byte)0x00,
(byte)0x00, (byte)0x02, (byte)0x00, (byte)0xc8};
private final byte deleteCid[] = {(byte)0xF0, (byte)0xE4, (byte)0x00,
(byte)0x00, (byte)0x02, (byte)0x00, (byte)0xC8};
private final byte deletecDir[] = {(byte)0xF0, (byte)0xE4, (byte)0x80,
(byte)0x00, (byte)0x00};
private final byte authenticate[] = {(byte)0xF0, (byte)0x2A, (byte)0x00,
(byte)0x00, (byte)0x08, (byte)0xAD, (byte)0x9F, (byte)0x61, (byte)0xFE,
(byte)0xFA, (byte)0x20, (byte)0xCE, (byte)0x63};
private String containerID;
private String cardletByteCodes;
private String blocksize;
private ResponseAPDU sendAPDU(byte apdu[]) throws CardTerminalException
{
CommandAPDU commandAPDU = new CommandAPDU(apdu);
System.out.println(commandAPDU);
ResponseAPDU responseAPDU =
getCardChannel().sendCommandAPDU(commandAPDU);
System.out.println("Status:" +
Integer.toHexString(responseAPDU.sw()));
return (responseAPDU);
}
public LoadCyberFlex()
{
}
public Hashtable downloadApplet(Hashtable loadProps, ByteArrayInputStream
bis)
{
System.out.println("About to start download");
Hashtable results = new Hashtable();
try
{
allocateCardChannel();
// APDUs for downloading and verifying a program.
// Step 1:
// Select the card root 3F00
// A0 A4 00 00 02 3F 00
sendAPDU(selectRoot);
// Step 2:
// Verify yourself to the card (Key 0)
// F0 2A 00 00 08 AD 9F 61 FE FA 20 CE 63
sendAPDU(authenticate);
sendAPDU(selectCid);
sendAPDU(deletecDir);
sendAPDU(selectRoot);
sendAPDU(deleteCid);
// Step 3: See description in makeLoadAPDU()
sendAPDU(makeLoadAPDU());
// Step 4
// Select the container directory
// A0 A4 00 00 02 XX XX Where XX XX is the container ID you
chose above.
byte sCID[] = {(byte)0xa0, (byte)0xA4, ZERO, ZERO, 0x02,
left(cid), right(cid)};
sendAPDU(sCID);
// Step 5
// Select file 2222 (Program file is ALWAY 2222)
// A0 A4 00 00 02 22 22
byte s2222[] = {(byte)0xa0, (byte)0xA4, ZERO, ZERO, 0x02, 0x22,
0x22};
sendAPDU(s2222);
// Step 6
// Download the processed class to file 2222 as follows:
// P1 and P2 form a 16-bit offset in the file; P1 is the most
significant
// byte, P2 is the least. Le is the length of the block you are
sending. I
// use a block size of 120 bytes of data. Just use something
convenient, such
// as 100, 120, or 128. Remember that the larger the size, the
fewer APDUs
// are needed, and the faster the download will be. I would not
go above 128,
// though, since this might cause buffering problems.
// A0 D6 P1 P2 Le XX XX ...
// Keep sending APDUs as necessary to get the entire file
downloaded.
int blockSize = 120;
ByteArrayOutputStream xmitBuffer = new ByteArrayOutputStream();
appletSize = bis.available();
long fullBlocks = appletSize / blockSize;
long lastBlock = appletSize % blockSize;
System.out.println("The size of the applet: " +
appletSize);
System.out.println("The number of full blocks is: " +
fullBlocks);
System.out.println("The bytes in the last block: " +
lastBlock);
offset = 0;
int blockNumber = 0;
byte dataBlock[] = new byte[blockSize];
while(fullBlocks-- > 0)
{
xmitBuffer.reset();
xmitBuffer.write(0xA0);
xmitBuffer.write(0xD6);
xmitBuffer.write(left(offset));
xmitBuffer.write(right(offset));
xmitBuffer.write(blockSize);
int amtread = bis.read(dataBlock,0,blockSize);
xmitBuffer.write(dataBlock,0,blockSize);
System.out.println("Sending block: " +
blockNumber++);
ResponseAPDU rapdu = sendAPDU(xmitBuffer.toByteArray());
if(rapdu.sw() != 0x9000)
{
results.put("error",rapdu.toString());
return (results);
}
offset += blockSize;
}
System.out.println("Sending block: " + blockNumber++);
xmitBuffer.reset();
xmitBuffer.write(0xA0);
xmitBuffer.write(0xD6);
xmitBuffer.write(left(offset));
xmitBuffer.write(right(offset));
xmitBuffer.write((int)lastBlock);
int amtread = bis.read(dataBlock,0,blockSize);
xmitBuffer.write(dataBlock,0,(int)lastBlock);
sendAPDU(xmitBuffer.toByteArray());
offset += lastBlock;
System.out.println("Sending last Block:");
// Step 7
//Select the container directory again
//A0 A4 00 00 02 XX XX Where XX XX is the container ID you chose
above.
// System.out.println("Select the Container");
// sendAPDU ( sCID );
System.out.println("Select Root");
sendAPDU(selectRoot);
xmitBuffer.reset();
xmitBuffer.write(0xA0);
xmitBuffer.write(0xA4);
xmitBuffer.write(0x04);
xmitBuffer.write(0x00);
xmitBuffer.write(0x10);
xmitBuffer.write(aid,0,16);
System.out.println("Select AID");
sendAPDU(xmitBuffer.toByteArray());
//Step 8
//Validate
//F0 A8 06 00 00
byte validate[] = {(byte)0xF0, (byte)0x08, 0x06, ZERO, ZERO};
System.out.println("Validate");
sendAPDU(validate);
//Step 9
//Install
//F0 AE 13 00 00
byte install[] = {(byte)0xF0, (byte)0xAE, 0x13, ZERO, ZERO};
System.out.println("Install");
sendAPDU(install);
//Step 10 Register
//F0 AE 14 00 00
if(loadProps.get("cyberflex.register") != null)
{
byte register[] = {(byte)0xF0, (byte)0xAE, 0x14, ZERO, ZERO};
System.out.println("Register");
sendAPDU(register);
}
//
//The applet is now automatically selected for execution. APDUs sent
using
//class 00 will be sent to your applet on the card.
}
catch(Exception e)
{
System.out.println("Download APDU Exception: " + e);
e.printStackTrace();
}
finally
{
releaseCardChannel();
try
{
bis.close();
}
catch(Exception e)
{
}
}
results.put("OK",getBytesSent() + " transferred.");
return (results);
}
void debugObject(Object o)
{
System.out.println(o.toString());
}
/**
* Create the load apdu command
*/
byte[] makeLoadAPDU()
{
// Create a directory which contains the object store and program file
// (container file).
// XX represents a byte which you fill in. AA represents padding
that you
// fill with "AA". The length is the sum of all the data
bytes. The program
// file is automatically created as file 2222, the data file is
automatically
// created as 2223. There is a translation for the whole string in
the
// programmers guide.
// APDU:
// F0 08 0 0 LENGTH
// Data bytes:
// XX XX Container size
// XX XX Container Id (Your choice)
// AAAAAAAA Padding characters
// FF00000000000000
// XX XX Program file size (from Makesolo)
// AAAAAAAAAAAA Padding characters
// FF00000000000000
// XX XX Data File Size (Makesolo suggests a value, but use
your own judgement)
// AAAAAAAA0000
// FF00000000000000
// AAAAFFFF0000AAAA
// XX AID size
// XX... AID
stuffBytes((byte)0xF0,1);
stuffBytes((byte)0x08,1);
stuffBytes((byte)0x00,1);
stuffBytes((byte)0x00,1);
stuffBytes((byte)0x4A,1);
stuffShort(containerSize);
stuffShort(cid);
stuffBytes(AA,4);
stuffBytes(FF,1);
stuffBytes(ZERO,7);
stuffShort(programFileSize);
stuffBytes(AA,6);
stuffBytes(FF,1);
stuffBytes(ZERO,7);
stuffShort(dataFileSize);
stuffBytes(AA,4);
stuffBytes(ZERO,2);
stuffBytes(FF,1);
stuffBytes(ZERO,7);
stuffBytes(AA,2);
stuffBytes(FF,2);
stuffBytes(ZERO,2);
stuffBytes(AA,2);
stuffBytes((byte)0,1);
stuffBytes((byte)0x10,1);
baos.write(aid,0,16);
byte apdu[] = baos.toByteArray();
apdu[4] = (byte)(baos.size() - 5);
return (apdu);
}
byte left(short s)
{
return ((byte)((s & (short)0xFF00) >> 8));
}
byte right(short s)
{
return ((byte)((s & (short)0x00FF)));
}
void stuffShort(short s)
{
baos.write(left(s));
baos.write(right(s));
}
void stuffBytes(byte b, int number)
{
int count = number;
while(count-- > 0)
baos.write(b);
}
public int getBytesToSend()
{
return (appletSize);
}
public int getBytesSent()
{
return (offset);
}
}
Java iButton loader
The proxy for loading the Java iButton is very similar to the proxy for loading the CyberFlex card and is included in the jar file in the Resources. The same general points made above for the CyberFlex card apply to the iButton. The iButton has its own set of APDUs to be used for communicating loading information -- such as where to put the application, what to call the application, when to register, when to install, and so on.
Here is the code for the Java iButton:
import java.io.*;
import opencard.core.terminal.*;
import opencard.core.service.*;
import com.ibutton.oc.JibAPI;
import javaworld.oc.javacard.loaders.Java CardLoaderProxy;
import opencard.opt.terminal.ISOCommandAPDU;
import java.util.Hashtable;
public class LoadiButton extends CardService implements Java CardLoaderProxy
{
static final byte CLA = (byte)0xD0;
static final byte INS = (byte)0x95;
// Class file header
public static final int PASSWORD_LENGTH_SIZE = 1;
public static final int PASSWORD_SIZE = 8;
public static final int AID_LENGTH_SIZE = 1;
public static final int AID_SIZE = 16;
public static final int AID_LENGTH_OFFSET = PASSWORD_LENGTH_SIZE +
PASSWORD_SIZE;
public static final int AID_NAME_OFFSET = PASSWORD_LENGTH_SIZE +
PASSWORD_SIZE + AID_LENGTH_SIZE;
public static final int CLASS_FILE_HEADER_SIZE = PASSWORD_LENGTH_SIZE +
PASSWORD_SIZE + AID_LENGTH_SIZE + AID_SIZE;
// end of class file header
public static int APDU_PACKET_LENGTH = 116;
public int bytesSent = 0;
public int bytesToSend = 0;
/**
* Constructor for iButton loader
*/
public LoadiButton()
{
}
/**
* Load a Java iButton with the provide byte stream.
*
* @param Hashtable Properties that can be used by instance of loader
* @param ByteArrayInputStream Java iButton bytecodes to be loaded
* @returns Hashtable Properties of load
*/
public Hashtable downloadApplet(Hashtable loaderProps,
ByteArrayInputStream bis)
{
String fileToLoad = null;
String pin = null;
Hashtable results = new Hashtable();
try
{
allocateCardChannel();
//System.out.println("Performing Master Erase on Java
iButton");
String password = (String)loaderProps.get("pin");
byte data[];
if (password != null)
{
data = new byte[1 + password.length()];
data[0] = (byte)password.length();
System.arraycopy(password.getBytes(), 0, data, 1,
password.length());
}
else
{
data = new byte[1];
data[0] = (byte)0;
}
ISOCommandAPDU apdu = new ISOCommandAPDU( CLA, INS, (byte)0,
(byte)0, data);
ResponseAPDU rapdu = getCardChannel().sendCommandAPDU(apdu);
if(rapdu.sw() != 0x9000)
{
System.err.println("Erase error:" + rapdu);
results.put("errorDuring", "Erase" );
results.put("error", rapdu );
return ( results );
}
String aidName = (String)loaderProps.get("aidName");
if(aidName == null)
{
aidName = (new
File((String)loaderProps.get("fileName"))).getName();
if(aidName == null)
{
System.err.println("Could not derive a name from file
or aidname");
results.put("error","Could not derive name
for loadable file");
return (results);
}
}
rapdu = loadApplet(loaderProps, bis,pin,aidName);
if(rapdu.sw() != 0x9000)
{
System.err.println("Load error:" + rapdu);
results.put("errorDuring", "Load" );
results.put("error", rapdu );
return ( results );
}
}
catch(Exception e)
{
e.printStackTrace();
System.err.println("Download Exception: " + e);
results.put("error",e.toString() );
return ( results );
}
finally
{
releaseCardChannel();
}
results.put("OK","");
return ( results );
}
/**
* @param fileName of the applet to be loaded into the iButton with Java.
* @param directoryName path to the applet to be loaded.
* @param aidName of the applet to be loaded.
* @return Response APDU indicating success or failure. Possible return
values are
* Insufficient Memory 0x6400
* Incomplete Packet 0x6A87
* Invalid Signature 0x6982
* Invalid Packet 0x6A87
* Class Length Overrun 0x6A84
* Invalid Loader Command 0x6A86
* Invalid AID Length 0x6901
* Invalid API Version 0x6902
* Invalid Password 0x6903
* Invalid Signature Length 0x6904
* Hash Corruption 0x6905
* Hash Failure 0x6906
* Success Load 0x9000
*/
public ResponseAPDU loadApplet(Hashtable loaderProps, ByteArrayInputStream
bis, String pin, String aidName) throws CardTerminalException, IOException
{
int i;
byte apduBuffer[] = new byte[APDU_PACKET_LENGTH];
byte[] classFileBuffer, signatureBuffer, dataBuffer;
long classFileLength = bis.available();
// class file header size + the length of the JiB file.
classFileBuffer = new byte[CLASS_FILE_HEADER_SIZE +
(int)classFileLength];
// Password FIX THIS TO HANDLE LOADING WITH A PASSWORD.
if(pin != null)
{
classFileBuffer[0] = (byte)pin.length();
System.arraycopy(pin.getBytes(),0,classFileBuffer,1,pin.length());
}
/*classFileBuffer[0] = 0;
classFileBuffer[1] = 0;
classFileBuffer[2] = 0;
classFileBuffer[3] = 0;
classFileBuffer[4] = 0;
classFileBuffer[5] = 0;
classFileBuffer[6] = 0;
classFileBuffer[7] = 0;
classFileBuffer[8] = 0;
*/
int offset;
// set up the AID
for(offset = 0;offset < 16;offset++)
{
if(offset < aidName.length())
classFileBuffer[offset + AID_NAME_OFFSET] =
(byte)aidName.charAt((int)offset);
else
break;
}
classFileBuffer[AID_LENGTH_OFFSET] = (byte)offset;
//AID Length
// get the class file
bis.read(classFileBuffer,CLASS_FILE_HEADER_SIZE,(int)classFileLength);
bis.close();
char[] returnVal;
boolean firstPacket = true;
ISOCommandAPDU apdu;
byte[] temp = {(byte)0x90, (byte)0x00};
ResponseAPDU rapdu = new ResponseAPDU(temp);
bytesToSend = (int)(classFileLength + CLASS_FILE_HEADER_SIZE -
APDU_PACKET_LENGTH);
long time = 0;
while(bytesSent < (classFileLength + CLASS_FILE_HEADER_SIZE -
APDU_PACKET_LENGTH))
{
for(i = 0;(i < APDU_PACKET_LENGTH) && (i <
classFileBuffer.length);i++)
apduBuffer[i] = classFileBuffer[i + bytesSent];
bytesSent += APDU_PACKET_LENGTH;
time = System.currentTimeMillis();
//System.out.println("APDU:" + time );
if(firstPacket == true)
{
apdu = new
ISOCommandAPDU(CLA,(byte)0xA6,(byte)0x01,(byte)0x00,apduBuffer);
firstPacket = false;
}
else
{
apdu = new
ISOCommandAPDU(CLA,(byte)0xA6,(byte)0x02,(byte)0x00,apduBuffer);
}
if ( loaderProps.get("verbose") != null)
System.err.println("Command\n" + apdu );
CardChannel cc = getCardChannel();
System.err.println(" Send t:" +
System.currentTimeMillis());
rapdu = cc.sendCommandAPDU(apdu);
System.err.println("Receive t:" +
System.currentTimeMillis());
if ( loaderProps.get("verbose") != null)
System.err.println("Response:\n" + rapdu );
if(bytesSent < (classFileLength + CLASS_FILE_HEADER_SIZE))
{
// WE NEED TO THROW A JIBLOAD EXCEPTION IF A FAILURE OCCURS
if(rapdu.sw() != 0x6301) {
//bytesSent = (int)(classFileLength +
CLASS_FILE_HEADER_SIZE + 1);
throw new CardTerminalException ("Unabel to
load");
}
}
time = System.currentTimeMillis() - time;
//System.out.println("APDU Elapsed:" +
System.currentTimeMillis() );
}
if(bytesSent < (classFileLength + CLASS_FILE_HEADER_SIZE))
{
apduBuffer = new byte[(int)(classFileLength +
CLASS_FILE_HEADER_SIZE - bytesSent)];
for(i = 0;i < ((classFileLength + CLASS_FILE_HEADER_SIZE) -
bytesSent);i++)
apduBuffer[i] = classFileBuffer[i + bytesSent];
apdu = new
ISOCommandAPDU(CLA,(byte)0xA6,(byte)0x02,(byte)0x00,apduBuffer);
if ( loaderProps.get("verbose") != null)
System.err.println("Command\n" + apdu );
rapdu = getCardChannel().sendCommandAPDU(apdu);
if ( loaderProps.get("verbose") != null)
System.err.println("Response:\n" + rapdu );
}
return rapdu;
}
/**
* Provide count of bytes currently loaded on Java iButton.
*
* @return Current number of bytes sent to card
*/
public int getBytesSent(){
return ( bytesSent );
}
/**
* Total number of bytes to load onto Java iButton.
*
* @return size of code to load onto card.
*/
public int getBytesToSend(){
return ( bytesToSend );
}
}
The Java iButton loader has some very simple APDUs for loading a card. This demonstrates the power of Java Card and OpenCard to allow implementations that are very different to be managed and loaded from a common framework.
Conclusion
OpenCard provides an easy-to-use framework for working with Java Cards and other smart cards. Working with the CyberFlex 16K card from Schlumberger and the Java iButton from Dallas Semiconductor, we have demonstrated that a small amount of code can be used to build downloadable applications. We have also shown the advantages of having an all-Java development environment, which allows developers, card terminal manufacturers, and security device manufacturers to provide jar files that can be used in all the emerging environments. OpenCard allows developers to distribute services that can be used in your favorite development environment with your current favorite Java Card.
About the author
Michael Montgomery is a leading innovator at Schlumberger, holding eight patents for his work in communications and smart cards. He is currently working on the next-generation Java Card products.
Rinaldo Di Giorgio is a staff engineer in the research department at Sun Microsystems. There he experiments with digital economies.
![]() |
![]() |
|
![]() |
Copyright © 2001 JavaWorld.com, an IDG Communications company |
![]() |
![]() |
![]() |