Use software components to deploy applications with Java Cards
A URL-based approach for deploying smart cards on different systems
In much of Europe, smart card technology has taken the tedium out of many forms of ebusiness, freeing consumers from having to provide personal information every time they want to conduct a transaction. Java Cards, available in the US today, provide a platform for the easy deployment of multifunctional smart-card applications; unfortunately for US companies and consumers, however, they have yet to catch on here. How can the US close the smart card gap and finally realize the potential of this underused technology?
One way is to develop applications for smart cards that provide consumers and corporate users with convenient and secure methods to access services. With the right kind of smart-card applications, users will be able to avoid the tedium of repeatedly typing information on Web pages in order to conduct secure transactions. The possibilities are great. In some countries, smart cards have eliminated the need for cash, credit cards, or debit cards. But we in the US are still struggling with upgrading point-of-sale terminals in stores to accept smart cards.
And our woes don’t end there. Even after typical US consumers wade through multiple layers of pages on the Web in order to carry out secure transactions, they are often reduced to repeating standard information — name, address, phone number — over the phone. Worse yet, we must frequently physically send that information out by surface mail once we’ve begun a transaction over the telephone. Wouldn’t it be nice to simply send the data from your smart card? (Smart cards would also reduce the neck and shoulder pain that results from having to balance the phone for long periods of time!) Smart cards can provide a sophisticated means of doing secure transactions and pack the whole process into a small, simple-to-use storage device. In short, they give developers the opportunity to create convenient and much-needed services for users.
Trial and error
There have been many smart card trials and pilots by forward-thinking companies, but few of these programs have generated much excitement or additional value to the consumer. The trials generally lacked integration with the Web, and thus could not showcase such exciting smart-card technologies as instant authentication and affinity programs. In these trials, users still had to enter form information over and over again, prove their identity with less secure methods such as passwords — or worse yet, social security numbers — and fall back on paper coupons, rather than just using a smart card with a coupon in its memory.
We can do much better. The technology exists now, and the basic resources to deploy Java Card-capable platforms are easy for Java developers to understand. For online use, you need to be able to load an application on the card that works with some network-based service. We will provide fully coded and tested examples of three services for the Smart Card URL Programming Interface (UPI), an architecture developed at Sun Labs and discussed in September’s installment of JavaWorld‘s Java Developer column. See the Resources section for links to general smart-card information.
We will also explain how to implement an end-to-end solution using Java Card technology in great detail, via documented code examples. Our discussion focuses on the Cyberflex access card manufactured by industry leader Schlumberger Ltd., but the techniques are general and could easily be applied to any Java Card-compliant smart card, such as Dallas Semiconductor Java iButtons, GemPlus GemExpresso cards, smart cards from De La Rue, Bull Information Systems, and even Mondex cards (which are issued by banks that have licensed the Mondex technology). For more information on using the Smart Card UPI to deploy digital cash solutions, take a look at the Resources section. For details on the Cyberflex Access card loading process, see the Loading a Cyberflex Access card sidebar. The converted applet file (CAP) format sidebar is also important to read because all future Java Card-compliant smart cards will support the CAP file format.
The Java Card solution
Let’s get started by discussing how to deploy a general-purpose Java Card-based technology that allows you to build applications that support strong authentication, secure personal storage, virtual ATM, and micro- and macropayments. (For the noneconomists in the crowd, micropayments refer to payments less than one dollar, and macropayments refer to larger payments, like 9.95.) Payment options on the Web are currently quite unsophisticated. There is no widespread deployment of support for such services as “pay as you go,” daily and weekly payments, subscriptions, and so on. Current payment systems are too heavyweight and require so many transactions that micropayments are not economically feasible.
In solving these problems, we will first discuss how to write an OpenCard service, followed by two specific examples with fully documented Java source code, followed by the source code for a Java Card applet that implements the services needed by SecureTokenHandler
on the smart card. Before going into the source code, the following three high-level summaries list the functionality provided by the SecureTokenHandler
, JavaCardLoaderHandler
, and CorporateCard
.
The SecureTokenHandler
supports the following UPI. For more detailed information, consul previous articles in JavaWorld which discuss the UPI. Each of the items below is decoded by SecureTokenHandler
to produce the desired result. Suppose, for example, that you wanted to get the ID of a smart card. You would send the card a URL of the from:
The handler would take the URL, parse and determine which command it is being sent, then return the values to you as either a name/property pair or as a Web page, depending on your configuration options.
-
GetID
: Returns the card number of the current card (card numbers are assigned by the application). -
GetPurse
: Returns all currencies and/or tokens supported. -
GetInfo
: Returns general information about the card. -
CheckPin
: Given a PIN, returns a message digest based on SHA1, XOR, or the like. -
VerifyPin
: Verifies a given digest. Usually done with a server. -
ChangeSetPin
: Changes or modifies user PINs. -
ValueTransfer
: Performs a value transfer (requires a serverSecureTokenHandler
). -
Decrypt
: Decrypts the input data, with optional type. -
ValueTransfer
: Encrypts the input data, with optional type.
The UPIs for loading cards support a few additional commands above and beyond LoadCardFromURL
. They work in a similar fashion to those described above; in fact, all handlers work this way. Take CardType
, for example. If you issued the following URL:
then the handler would return the card type as a string that can be used to create a correct loading profile for the card in question. This type of approach will be demonstrated at Cartes 99 in Paris by platform seven; see Resources for more information.
The JavaCardLoaderHandler
supports the following UPI:
-
LoadJavaCardFromURL
: The given URL is used to get information to load an applet onto a card. See the sidebar for more information on load file format. -
CardType
: Queries the handler for type of card (e.g., GemExpresso, Java iButton) -
EndLoad
: Tells loader to quit posting status. -
Status
: Returns an interactive status of the load.
The CorporateCard applet, which can load on to any Java Card-compliant smart card, provides the services via the Application Data Protocol Units (APDUs) needed to perform an operation — to authenticate, for example. A specific APDU command is built by the JavaCardCCProxyService
, which implements PurseCardService
. The more important methods are listed below. Consult the source listing for more detail.
Writing an OpenCard service
Let’s try to break down the functionality we need into software components that we can connect to get our system built. We’ll first describe this functionality, then provide some code that realizes the functionality described. We will require the following:
-
The ability to put Java Card applets onto a user’s personal Java Card using a browser. This functionality will require a loader OpenCard service to manage the details of loading. You don’t just copy applications onto smart cards. Installing an application on a smart card often requires the use of a very specific protocol. Two of the sidebars to this article describe this process, and we have also provided a specific example for Schlumberger, as well as a likely future direction in this area, using the CAP file format.
-
A Java Card applet that implements needed functionality, such as authentication, secure personal storage, micro- or macropayments, signature services, and encryption/decryption services.
-
A proxy for the Java Card applet that runs on the user’s host computer. The proxy communicates with the applet-sending and -receiving data in the specific APDUs for the card. This proxy is best realized as an OpenCard service. The Resources section offers examples from previous JavaWorld articles.
-
A UPI handler that takes requests from a user’s browser and maps the requests to methods in the proxy, which then communicates with the applet running on the smart card. This technology was described briefly in September’s Java Developer. Suppose you wanted to use a card to generate a signature for some data that you want it to send, and then use a different card to verify that data. Sending the following URL (making sure it remains a unified URL, though it has been broken up on your browser screen due to its length):
CheckPin?pin=$$$$java&random=01020304050607
produces the following results, which are returned as name-value pairs:
purse=1000200030004000
validate=dd4d255954ad5a1a8e9a201fa6c9cf47f4521f82
MACTYPE=SHA1
The smart card has signed the following 64 byte block:
0000: 4A 61 76 61 4F 6E 65 2B 01 02 03 04 05 06 07 31 JavaOne+.......1
0010: 30 30 30 32 30 30 30 33 30 30 30 34 30 30 30 80 000200030004000.
0020: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
0030: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 F8 ................
The server or other party can verify the identity by performing the following operation with its own local
SecureTokenServices
handler:&validate=dd4d255954ad5a1a8e9a201fa6c9cf47f4521f82&purse=1000200030004000
The server generates the same block of data and generates a digest. If the two digests match, then it’s clear that the data was generated by a user possessing a smart card with the secret and the signature algorithm. This example is meant to be introductory. To gain a better understanding of the concepts, look at the Resources section.
In prior JavaWorld articles on Java Card, we discussed how to write OpenCard services. A very quick overview of writing a service follows for those of you who have some knowledge of it. In order to refresh your memory, a simple service for sending APDUs to a card and getting a response can be found in PassThroughCardService.html
.
The OpenCard Cyberflex Access loader service
Schlumberger provides the necessary APDUs to load a Java Card application on the card. Tips on writing the application for earlier versions of the card are listed in Resources below. The loader service for the Cyberflex Access card can be found in Loader.html
. The OpenCard loader service for a Cyberflex Access card service follows:
<i>/*
* @(#)LoadCyberFlexAccess.java 1.0 09/06/1999
*
* Copyright (c) 1994-1999 Sun Microsystems, Inc.
* 901 San Antonio Road, Palo Alto, California, 94303, U.S.A.
* and Schlumberger
* 8311 N. FM 620 Road, Austin, Texas, 78726, U.S.A.
* All rights reserved.
*
*/</i>
<b>package</b> sunlabs<b>.</b>oc<b>.</b>javacard<b>.</b>loaders<b>.</b>schlumberger<b>;</b>
<b>import</b> sunlabs<b>.</b>oc<b>.</b>javacard<b>.</b>loaders<b>.</b>JavaCardLoaderProxy<b>;</b>
<b>import</b> java<b>.</b>io<b>.</b>ByteArrayInputStream<b>;</b>
<b>import</b> java<b>.</b>io<b>.</b>ByteArrayOutputStream<b>;</b>
<b>import</b> java<b>.</b>util<b>.</b>Hashtable<b>;</b>
<b>import</b> opencard<b>.</b>core<b>.</b>service<b>.</b>CardService<b>;</b>
<b>import</b> opencard<b>.</b>core<b>.</b>service<b>.</b>CardServiceRuntimeException<b>;</b>
<b>import</b> opencard<b>.</b>core<b>.</b>terminal<b>.</b>CardTerminalException<b>;</b>
<b>import</b> sun<b>.</b>oc<b>.</b>terminal<b>.</b>ISOCommandAPDU<b>;</b>
<b>import</b> opencard<b>.</b>core<b>.</b>terminal<b>.</b>CommandAPDU<b>;</b>
<b>import</b> opencard<b>.</b>core<b>.</b>terminal<b>.</b>ResponseAPDU<b>;</b>
<b>import</b> sunlabs<b>.</b>sts<b>.</b>ByteArray<b>;</b>
<b>import</b> sunlabs<b>.</b>oc<b>.</b>ErrorMsg<b>;</b>
<i>/**
* Load a Cyberflex Access card.
*
* This class loads a Cyberflex Access card given a ByteInputStream
* and a hashtable with load properties. The
* properties currently supported are:
*
* <br>fileIdContainerSize -- size in bytes to make container
* <br>instanceDataSize -- size in bytes to make instance
* <br>fileId -- the ID for the file
* <br>instanceFileId -- the id for the instance
* <br>aid -- an aid to indentify this application
* <br>appletDESKey -- the DES signature of the cardlets bytes for verification.
*
*
* @author Rinaldo Di Giorgio
* @author Mike Montgomery
* @author Jennifer Yonemitsu
* @version %I%, %G%
*/</i>
<b>public</b> <b>class</b> LoadCyberFlexAccess <b>extends</b> CardService <b>implements</b> JavaCardLoaderProxy <b>{</b>
<b>private</b> <b>int</b> tNumber = 0<b>;</b>
<b>private</b> <b>final</b> <b>byte</b> APPLET = 0x01<b>;</b>
<b>private</b> <b>short</b> fileIdContainerSize = 7000<b>;</b>
<b>private</b> <b>short</b> instanceDataSize = 6000<b>;</b>
<b>private</b> <b>short</b> fileId = 0x2222<b>;</b>
<b>private</b> <b>short</b> instanceFileId = 0x2223<b>;</b>
<b>private</b> <b>byte</b> aid<b>[</b><b>]</b> = <b>{</b>
0<b>,</b> 1<b>,</b> 2<b>,</b> 3<b>,</b> 4<b>,</b> 5<b>,</b> 6<b>,</b> 7<b>,</b> 8<b>,</b> 9<b>,</b> 10<b>,</b> 11<b>,</b> 12<b>,</b> 13<b>,</b> 14<b>,</b> 15<b>,</b> 16
<b>}</b><b>;</b>
<b>private</b> <b>byte</b> appletDESKey<b>[</b><b>]</b> = <b>null</b><b>;</b>
<b>private</b> <b>byte</b> BZ = <b>(</b><b>byte</b><b>)</b> 0<b>;</b>
<b>private</b> <b>byte</b> B1 = <b>(</b><b>byte</b><b>)</b> 1<b>;</b>
<b>private</b> <b>byte</b> B8 = <b>(</b><b>byte</b><b>)</b> 8<b>;</b>
<b>private</b> <b>byte</b> FF = <b>(</b><b>byte</b><b>)</b> 0xFF<b>;</b>
<b>private</b> <b>byte</b> SEL = <b>(</b><b>byte</b><b>)</b> 0xA4<b>;</b>
<b>private</b> <b>byte</b> DEL = <b>(</b><b>byte</b><b>)</b> 0xE4<b>;</b>
<b>private</b> <b>byte</b> SID = <b>(</b><b>byte</b><b>)</b> 0x02<b>;</b> <i>// Use file ID
</i> <b>private</b> <b>int</b> appletSize = 0<b>;</b>
<b>private</b> <b>short</b> offset = 0<b>;</b>
<b>private</b> String status = "Ready To Load"<b>;</b>
ByteArrayOutputStream baos = <b>new</b> ByteArrayOutputStream<b>(</b><b>)</b><b>;</b>
<b>private</b> <b>final</b> <b>byte</b> selectRoot<b>[</b><b>]</b> = <b>{</b>
BZ<b>,</b> SEL<b>,</b> BZ<b>,</b> BZ<b>,</b> SID<b>,</b> <b>(</b><b>byte</b><b>)</b> 0x3F<b>,</b> BZ
<b>}</b><b>;</b>
<b>private</b> <b>final</b> <b>byte</b> selectFileId<b>[</b><b>]</b> = <b>{</b>
BZ<b>,</b> SEL<b>,</b> BZ<b>,</b> BZ<b>,</b> SID<b>,</b> <b>(</b><b>byte</b><b>)</b> 0x22<b>,</b> <b>(</b><b>byte</b><b>)</b> 0x22
<b>}</b><b>;</b>
<b>private</b> <b>final</b> <b>byte</b> selectInstance<b>[</b><b>]</b> = <b>{</b>
BZ<b>,</b> SEL<b>,</b> BZ<b>,</b> BZ<b>,</b> SID<b>,</b> <b>(</b><b>byte</b><b>)</b> 0x22<b>,</b> <b>(</b><b>byte</b><b>)</b> 0x23
<b>}</b><b>;</b>
<b>private</b> <b>final</b> <b>byte</b> deleteFileId<b>[</b><b>]</b> = <b>{</b>
BZ<b>,</b> DEL<b>,</b> BZ<b>,</b> BZ<b>,</b> SID<b>,</b> <b>(</b><b>byte</b><b>)</b> 0x22<b>,</b> <b>(</b><b>byte</b><b>)</b> 0x22
<b>}</b><b>;</b>
<b>private</b> <b>final</b> <b>byte</b> deletecDir<b>[</b><b>]</b> = <b>{</b>
BZ<b>,</b> DEL<b>,</b> <b>(</b><b>byte</b><b>)</b> 0x80<b>,</b> BZ<b>,</b> BZ
<b>}</b><b>;</b>
<b>private</b> <b>final</b> <b>byte</b> verifyKey<b>[</b><b>]</b> = <b>{</b>
BZ<b>,</b> <b>(</b><b>byte</b><b>)</b> 0x2A<b>,</b> BZ<b>,</b> BZ<b>,</b> <b>(</b><b>byte</b><b>)</b> 0x08<b>,</b> <b>(</b><b>byte</b><b>)</b> 0xAD<b>,</b> <b>(</b><b>byte</b><b>)</b> 0x9F<b>,</b>
<b>(</b><b>byte</b><b>)</b> 0x61<b>,</b> <b>(</b><b>byte</b><b>)</b> 0xFE<b>,</b> <b>(</b><b>byte</b><b>)</b> 0xFA<b>,</b> <b>(</b><b>byte</b><b>)</b> 0x20<b>,</b> <b>(</b><b>byte</b><b>)</b> 0xCE<b>,</b>
<b>(</b><b>byte</b><b>)</b> 0x63
<b>}</b><b>;</b>
<b>private</b> <b>final</b> <b>byte</b> nonCryptoKey<b>[</b><b>]</b> = <b>{</b>
<b>(</b><b>byte</b><b>)</b> 0xCE<b>,</b> <b>(</b><b>byte</b><b>)</b> 0xA4<b>,</b> <b>(</b><b>byte</b><b>)</b> 0x5D<b>,</b> <b>(</b><b>byte</b><b>)</b> 0xC2<b>,</b> <b>(</b><b>byte</b><b>)</b> 0xAE<b>,</b>
<b>(</b><b>byte</b><b>)</b> 89<b>,</b> <b>(</b><b>byte</b><b>)</b> 0xFE<b>,</b> <b>(</b><b>byte</b><b>)</b> 0x4F
<b>}</b><b>;</b>
<b>private</b> <b>final</b> <b>byte</b> createFileId<b>[</b><b>]</b> = <b>{</b>
BZ<b>,</b> <b>(</b><b>byte</b><b>)</b> 0xE0<b>,</b> BZ<b>,</b> BZ<b>,</b> <b>(</b><b>byte</b><b>)</b> 0x10<b>,</b> <b>(</b><b>byte</b><b>)</b> 0x10<b>,</b> <b>(</b><b>byte</b><b>)</b> 0x00<b>,</b>
<b>(</b><b>byte</b><b>)</b> 0x22<b>,</b> <b>(</b><b>byte</b><b>)</b> 0x22<b>,</b> <b>(</b><b>byte</b><b>)</b> 0x03<b>,</b> <b>(</b><b>byte</b><b>)</b> 0x01<b>,</b> BZ<b>,</b> BZ<b>,</b> FF<b>,</b> FF<b>,</b>
FF<b>,</b> FF<b>,</b> FF<b>,</b> FF<b>,</b> FF<b>,</b> FF
<b>}</b><b>;</b>
<b>private</b> <b>final</b> <b>byte</b> selectDefaultLoader<b>[</b><b>]</b> = <b>{</b>
BZ<b>,</b> SEL<b>,</b> <b>(</b><b>byte</b><b>)</b> 0x04<b>,</b> BZ
<b>}</b><b>;</b>
<b>private</b> <b>final</b> <b>byte</b> resetProgram<b>[</b><b>]</b> = <b>{</b>
BZ<b>,</b> <b>(</b><b>byte</b><b>)</b> 0x0a<b>,</b> <b>(</b><b>byte</b><b>)</b> 0x02<b>,</b> BZ
<b>}</b><b>;</b>
<b>private</b> <b>final</b> <b>byte</b> validateAppletDES<b>[</b><b>]</b> = <b>{</b>
BZ<b>,</b> <b>(</b><b>byte</b><b>)</b> 0x0a<b>,</b> B1<b>,</b> BZ<b>,</b> B8
<b>}</b><b>;</b>
<b>private</b> <b>final</b> <b>byte</b> validateAppletRSA<b>[</b><b>]</b> = <b>{</b>
BZ<b>,</b> <b>(</b><b>byte</b><b>)</b> 0x0a<b>,</b> B1<b>,</b> BZ<b>,</b> <b>(</b><b>byte</b><b>)</b> 128
<b>}</b><b>;</b>
<b>private</b> <b>final</b> <b>byte</b> createInstance<b>[</b><b>]</b> = <b>{</b>
BZ<b>,</b> <b>(</b><b>byte</b><b>)</b> 0x0C<b>,</b> <b>(</b><b>byte</b><b>)</b> 0x13<b>,</b> BZ<b>,</b> 0x1B
<b>}</b><b>;</b>
<b>private</b> <b>final</b> <b>byte</b> resetInstance<b>[</b><b>]</b> = <b>{</b>
BZ<b>,</b> <b>(</b><b>byte</b><b>)</b> 0x08<b>,</b> <b>(</b><b>byte</b><b>)</b> 0x03<b>,</b> BZ
<b>}</b><b>;</b>
<b>private</b> <b>final</b> <b>byte</b> deleteInstance<b>[</b><b>]</b> = <b>{</b>
BZ<b>,</b> <b>(</b><b>byte</b><b>)</b> 0x08<b>,</b> <b>(</b><b>byte</b><b>)</b> 0x02<b>,</b> BZ
<b>}</b><b>;</b>
<b>private</b> String containerID<b>;</b>
<b>private</b> String prefix = ""<b>;</b>
<i>/**
* Loads the cardlet with the given properties.
*
*
* @param loadProps Properties to be used in laoding this cardlet.
* @param bis the bytes comprising the cardlet.
*
* @return a hashtabnle with information about the load attempt.
*
*/</i>
<b>public</b> Hashtable downloadApplet<b>(</b>Hashtable loadProps<b>,</b>
ByteArrayInputStream bis<b>)</b> <b>{</b>
appletSize = bis<b>.</b>available<b>(</b><b>)</b><b>;</b>
offset = 0<b>;</b>
status = "Ready To Load"<b>;</b>
String s = <b>(</b>String<b>)</b> loadProps<b>.</b>get<b>(</b>"LOADERPREFIX"<b>)</b><b>;</b>
<b>if</b> <b>(</b>s != <b>null</b><b>)</b> <b>{</b>
prefix = s<b>;</b>
<b>}</b>
System<b>.</b>out<b>.</b>println<b>(</b>"Starting download with Properties:" + loadProps<b>)</b><b>;</b>
System<b>.</b>out<b>.</b>println<b>(</b>"Starting download looking for prefixes:"
+ prefix<b>)</b><b>;</b>
ResponseAPDU resp<b>;</b>
Hashtable results = <b>new</b> Hashtable<b>(</b><b>)</b><b>;</b>
<i>//
</i> <i>// Get all the parameters from the user to overried current
</i> <i>// Parameters
</i> <i>//
</i>
<b>try</b> <b>{</b>
fileIdContainerSize = getParamShort<b>(</b>loadProps<b>,</b>
"fileIdContainerSize"<b>)</b><b>;</b>
<b>}</b> <b>catch</b> <b>(</b>Exception e<b>)</b> <b>{</b><b>}</b>
<b>;</b>
<b>try</b> <b>{</b>
instanceDataSize = getParamShort<b>(</b>loadProps<b>,</b> "instanceDataSize"<b>)</b><b>;</b>
<b>}</b> <b>catch</b> <b>(</b>Exception e<b>)</b> <b>{</b><b>}</b>
<b>;</b>
<b>try</b> <b>{</b>
fileId = getParamShort<b>(</b>loadProps<b>,</b> "fileId"<b>)</b><b>;</b>
<b>}</b> <b>catch</b> <b>(</b>Exception e<b>)</b> <b>{</b><b>}</b>
<b>;</b>
<b>try</b> <b>{</b>
instanceFileId = getParamShort<b>(</b>loadProps<b>,</b> "instanceFileId"<b>)</b><b>;</b>
<b>}</b> <b>catch</b> <b>(</b>Exception e<b>)</b> <b>{</b><b>}</b>
<b>;</b>
<b>try</b> <b>{</b>
<b>if</b> <b>(</b>getParamByte<b>(</b>loadProps<b>,</b> "aid"<b>)</b> != <b>null</b><b>)</b> <b>{</b>
aid = getParamByte<b>(</b>loadProps<b>,</b> "aid"<b>)</b><b>;</b>
<b>}</b>
<b>}</b> <b>catch</b> <b>(</b>Exception e<b>)</b> <b>{</b>
System<b>.</b>err
<b>.</b>println<b>(</b>"Error Loading AID Using 01020304050607089090A0B0C0D0E0F"<b>)</b><b>;</b>
<b>}</b>
<b>try</b> <b>{</b>
appletDESKey = getParamByte<b>(</b>loadProps<b>,</b> "appletDESKey"<b>)</b><b>;</b>
<b>}</b> <b>catch</b> <b>(</b>Exception e<b>)</b> <b>{</b>
System<b>.</b>err<b>.</b>println<b>(</b>"Error Loading DES Key"<b>)</b><b>;</b>
<b>}</b>
System<b>.</b>out<b>.</b>println<b>(</b>"fileIdContainerSize:" + fileIdContainerSize<b>)</b><b>;</b>
System<b>.</b>out<b>.</b>println<b>(</b>"instanceDataSize:" + instanceDataSize<b>)</b><b>;</b>
System<b>.</b>out<b>.</b>println<b>(</b>"fileId:" + fileId<b>)</b><b>;</b>
System<b>.</b>out<b>.</b>println<b>(</b>"instanceFileId:" + instanceFileId<b>)</b><b>;</b>
System<b>.</b>out<b>.</b>println<b>(</b>"Aid:" + ByteArray<b>.</b>toHexString<b>(</b>aid<b>)</b><b>)</b><b>;</b>
<b>if</b> <b>(</b>appletDESKey != <b>null</b><b>)</b> <b>{</b>
System<b>.</b>out<b>.</b>println<b>(</b>"appletDESKey:"
+ ByteArray<b>.</b>toHexString<b>(</b>appletDESKey<b>)</b><b>)</b><b>;</b>
<b>}</b>
<b>try</b> <b>{</b>
status = "Preparing Directory Structure"<b>;</b>
allocateCardChannel<b>(</b><b>)</b><b>;</b>
resp = sendAPDU<b>(</b>selectDefaultLoader<b>)</b><b>;</b>
<b>if</b> <b>(</b>resp<b>.</b>sw<b>(</b><b>)</b> != 0x9000<b>)</b> <b>{</b>
results<b>.</b>put<b>(</b>"error"<b>,</b> "Unable to Select Default Loader"<b>)</b><b>;</b>
results<b>.</b>put<b>(</b>"errorData"<b>,</b> resp<b>)</b><b>;</b>
status = "Load Error:" + resp<b>;</b>
<b>return</b> <b>(</b>results<b>)</b><b>;</b>
<b>}</b>
<i>// APDUs for downloading and verifying a program.
</i> <i>// Step 1:
</i> <i>// Select the card root 3F00
</i>
resp = sendAPDU<b>(</b>selectRoot<b>)</b><b>;</b>
<b>if</b> <b>(</b>resp<b>.</b>sw<b>(</b><b>)</b> != 0x9000<b>)</b> <b>{</b>
results<b>.</b>put<b>(</b>"error"<b>,</b> "Unable to Select Root"<b>)</b><b>;</b>
results<b>.</b>put<b>(</b>"errorData"<b>,</b> resp<b>)</b><b>;</b>
status = "Load Error:" + resp<b>;</b>
<b>return</b> <b>(</b>results<b>)</b><b>;</b>
<b>}</b>
<i>// Step 2:
</i> <i>// Verify yourself to the card (Key 0)
</i>
resp = sendAPDU<b>(</b>verifyKey<b>)</b><b>;</b>
<b>if</b> <b>(</b>resp<b>.</b>sw<b>(</b><b>)</b> != 0x9000<b>)</b> <b>{</b>
results<b>.</b>put<b>(</b>"error"<b>,</b> "Unable to Authenticate"<b>)</b><b>;</b>
results<b>.</b>put<b>(</b>"errorData"<b>,</b> resp<b>)</b><b>;</b>
status = "Load Error:" + resp<b>;</b>
<b>return</b> <b>(</b>results<b>)</b><b>;</b>
<b>}</b>
<i>// Select the File ID and delete it
</i>
resp = sendAPDU<b>(</b>selectFileId<b>)</b><b>;</b>
<b>if</b> <b>(</b>resp<b>.</b>sw<b>(</b><b>)</b> == 0x9000<b>)</b> <b>{</b>
<i>// reset the program in case previous load failed
</i> <i>// and program was left in an unknwon state.
</i>
resp = sendAPDU<b>(</b>resetProgram<b>)</b><b>;</b>
resp = sendAPDU<b>(</b>deleteFileId<b>)</b><b>;</b>
<b>if</b> <b>(</b>resp<b>.</b>sw<b>(</b><b>)</b> != 0x9000<b>)</b> <b>{</b>
results<b>.</b>put<b>(</b>"error"<b>,</b> "Unable to Delete existing file"<b>)</b><b>;</b>
results<b>.</b>put<b>(</b>"errorData"<b>,</b> resp<b>)</b><b>;</b>
status = "Load Error:" + resp<b>;</b>
<b>return</b> <b>(</b>results<b>)</b><b>;</b>
<b>}</b>
<b>}</b>
<i>// Select the File ID and delete it
</i>
resp = sendAPDU<b>(</b>selectInstance<b>)</b><b>;</b>
<b>if</b> <b>(</b>resp<b>.</b>sw<b>(</b><b>)</b> == 0x9000<b>)</b> <b>{</b>
<i>// reset the program in case previous load failed
</i> <i>// and program was left in an unknwon state.
</i>
resp = sendAPDU<b>(</b>resetInstance<b>)</b><b>;</b>
resp = sendAPDU<b>(</b>deleteInstance<b>)</b><b>;</b>
<b>if</b> <b>(</b>resp<b>.</b>sw<b>(</b><b>)</b> != 0x9000<b>)</b> <b>{</b>
results<b>.</b>put<b>(</b>"error"<b>,</b>
"Unable to Delete existing Instance"<b>)</b><b>;</b>
results<b>.</b>put<b>(</b>"errorData"<b>,</b> resp<b>)</b><b>;</b>
status = "Load Error:" + resp<b>;</b>
<b>return</b> <b>(</b>results<b>)</b><b>;</b>
<b>}</b>
<b>}</b>
resp = sendAPDU<b>(</b>selectRoot<b>)</b><b>;</b>
<b>if</b> <b>(</b>resp<b>.</b>sw<b>(</b><b>)</b> != 0x9000<b>)</b> <b>{</b>
results<b>.</b>put<b>(</b>"error"<b>,</b> "Unable to Select Root"<b>)</b><b>;</b>
results<b>.</b>put<b>(</b>"errorData"<b>,</b> resp<b>)</b><b>;</b>
status = "Load Error:" + resp<b>;</b>
<b>return</b> <b>(</b>results<b>)</b><b>;</b>
<b>}</b>
resp = sendAPDU<b>(</b>createFileId<b>)</b><b>;</b>
<b>if</b> <b>(</b>resp<b>.</b>sw<b>(</b><b>)</b> != 0x9000<b>)</b> <b>{</b>
results<b>.</b>put<b>(</b>"error"<b>,</b> "Unable to Create File"<b>)</b><b>;</b>
results<b>.</b>put<b>(</b>"errorData"<b>,</b> resp<b>)</b><b>;</b>
status = "Load Error:" + resp<b>;</b>
<b>return</b> <b>(</b>results<b>)</b><b>;</b>
<b>}</b>
<i>// Select file
</i>
resp = sendAPDU<b>(</b>selectFileId<b>)</b><b>;</b>
<b>if</b> <b>(</b>resp<b>.</b>sw<b>(</b><b>)</b> != 0x9000<b>)</b> <b>{</b>
results<b>.</b>put<b>(</b>"error"<b>,</b> "Unable to Select File"<b>)</b><b>;</b>
results<b>.</b>put<b>(</b>"errorData"<b>,</b> resp<b>)</b><b>;</b>
status = "Load Error:" + resp<b>;</b>
<b>return</b> <b>(</b>results<b>)</b><b>;</b>
<b>}</b>
resp = sendAPDU<b>(</b>selectDefaultLoader<b>)</b><b>;</b>
<b>if</b> <b>(</b>resp<b>.</b>sw<b>(</b><b>)</b> != 0x9000<b>)</b> <b>{</b>
results<b>.</b>put<b>(</b>"error"<b>,</b> "Unable to Select Default Loader"<b>)</b><b>;</b>
results<b>.</b>put<b>(</b>"errorData"<b>,</b> resp<b>)</b><b>;</b>
status = "Load Error:" + resp<b>;</b>
<b>return</b> <b>(</b>results<b>)</b><b>;</b>
<b>}</b>
resp = sendAPDU<b>(</b>resetProgram<b>)</b><b>;</b>
<i>// Step 6
</i> <i>// Download the processed class to file 2222 as follows:
</i> <i>// P1 and P2 form a 16-bit offset in the file; P1 is the most significant
</i> <i>// byte, P2 is the least. Le is the length of the block you are sending. I
</i> <i>// use a block size of 120 bytes of data. Just use something convenient, such
</i> <i>// as 100, 120, or 128. Remember that the larger the size, the fewer APDUs
</i> <i>// are needed, and the faster the download will be. I would not go above 128,
</i> <i>// though, since this might cause buffering problems.
</i> <i>// A0 D6 P1 P2 Le XX XX ...
</i> <i>// Keep sending APDUs as necessary to get the entire file downloaded.
</i>
<b>int</b> blockSize = 120<b>;</b>
ByteArrayOutputStream xmitBuffer = <b>new</b> ByteArrayOutputStream<b>(</b><b>)</b><b>;</b>
<b>long</b> fullBlocks = appletSize / blockSize<b>;</b>
<b>long</b> lastBlock = appletSize % blockSize<b>;</b>
System<b>.</b>out<b>.</b>println<b>(</b>"The size of the applet: " + appletSize<b>)</b><b>;</b>
System<b>.</b>out<b>.</b>println<b>(</b>"The number of full blocks is: " + fullBlocks<b>)</b><b>;</b>
System<b>.</b>out<b>.</b>println<b>(</b>"The bytes in the last block: " + lastBlock<b>)</b><b>;</b>
offset = 0<b>;</b>
<b>int</b> blockNumber = 0<b>;</b>
<b>byte</b> dataBlock<b>[</b><b>]</b> = <b>new</b> <b>byte</b><b>[</b>blockSize<b>]</b><b>;</b>
status = "Transmitting Program"<b>;</b>
<b>while</b> <b>(</b>fullBlocks-- > 0<b>)</b> <b>{</b>
xmitBuffer<b>.</b>reset<b>(</b><b>)</b><b>;</b>
xmitBuffer<b>.</b>write<b>(</b>BZ<b>)</b><b>;</b>
xmitBuffer<b>.</b>write<b>(</b>0xD6<b>)</b><b>;</b>
xmitBuffer<b>.</b>write<b>(</b>left<b>(</b>offset<b>)</b><b>)</b><b>;</b>
xmitBuffer<b>.</b>write<b>(</b>right<b>(</b>offset<b>)</b><b>)</b><b>;</b>
xmitBuffer<b>.</b>write<b>(</b>blockSize<b>)</b><b>;</b>
<b>int</b> amtread = bis<b>.</b>read<b>(</b>dataBlock<b>,</b> 0<b>,</b> blockSize<b>)</b><b>;</b>
xmitBuffer<b>.</b>write<b>(</b>dataBlock<b>,</b> 0<b>,</b> blockSize<b>)</b><b>;</b>
System<b>.</b>out<b>.</b>println<b>(</b>"Sending block: " + blockNumber++<b>)</b><b>;</b>
ResponseAPDU rapdu = sendAPDU<b>(</b>xmitBuffer<b>.</b>toByteArray<b>(</b><b>)</b><b>)</b><b>;</b>
<b>if</b> <b>(</b>rapdu<b>.</b>sw<b>(</b><b>)</b> != 0x9000<b>)</b> <b>{</b>
results<b>.</b>put<b>(</b>"error"<b>,</b> rapdu<b>.</b>toString<b>(</b><b>)</b><b>)</b><b>;</b>
status = "Load Error:" + resp<b>;</b>
<b>return</b> <b>(</b>results<b>)</b><b>;</b>
<b>}</b>
offset += blockSize<b>;</b>
<b>}</b>
xmitBuffer<b>.</b>reset<b>(</b><b>)</b><b>;</b>
xmitBuffer<b>.</b>write<b>(</b>BZ<b>)</b><b>;</b>
xmitBuffer<b>.</b>write<b>(</b>0xD6<b>)</b><b>;</b>
xmitBuffer<b>.</b>write<b>(</b>left<b>(</b>offset<b>)</b><b>)</b><b>;</b>
xmitBuffer<b>.</b>write<b>(</b>right<b>(</b>offset<b>)</b><b>)</b><b>;</b>
xmitBuffer<b>.</b>write<b>(</b><b>(</b><b>int</b><b>)</b> lastBlock<b>)</b><b>;</b>
<b>int</b> amtread = bis<b>.</b>read<b>(</b>dataBlock<b>,</b> 0<b>,</b> blockSize<b>)</b><b>;</b>
System<b>.</b>out<b>.</b>println<b>(</b>"Sending last Block:"<b>)</b><b>;</b>
xmitBuffer<b>.</b>write<b>(</b>dataBlock<b>,</b> 0<b>,</b> <b>(</b><b>int</b><b>)</b> lastBlock<b>)</b><b>;</b>
sendAPDU<b>(</b>xmitBuffer<b>.</b>toByteArray<b>(</b><b>)</b><b>)</b><b>;</b>
offset += lastBlock<b>;</b>
System<b>.</b>out<b>.</b>println<b>(</b>"Validate Applet"<b>)</b><b>;</b>
status = "Validate Applet"<b>;</b>
xmitBuffer<b>.</b>reset<b>(</b><b>)</b><b>;</b>
xmitBuffer<b>.</b>write<b>(</b>validateAppletDES<b>,</b> 0<b>,</b> 5<b>)</b><b>;</b>
<b>if</b> <b>(</b>appletDESKey == <b>null</b><b>)</b> <b>{</b>
xmitBuffer<b>.</b>write<b>(</b>nonCryptoKey<b>,</b> 0<b>,</b> 8<b>)</b><b>;</b>
<b>}</b> <b>else</b> <b>{</b>
xmitBuffer<b>.</b>write<b>(</b>appletDESKey<b>,</b> 0<b>,</b> 8<b>)</b><b>;</b>
<b>}</b>
resp = sendAPDU<b>(</b>xmitBuffer<b>.</b>toByteArray<b>(</b><b>)</b><b>)</b><b>;</b>
<b>if</b> <b>(</b>resp<b>.</b>sw<b>(</b><b>)</b> != 0x9000<b>)</b> <b>{</b>
results<b>.</b>put<b>(</b>"error"<b>,</b> "Unable to Authenticate Applet"<b>)</b><b>;</b>
results<b>.</b>put<b>(</b>"errorData"<b>,</b> resp<b>)</b><b>;</b>
status = "Load Error:" + resp<b>;</b>
<b>}</b>
<b>try</b> <b>{</b>
Thread<b>.</b>sleep<b>(</b>5 * 1000<b>)</b><b>;</b>
<b>}</b> <b>catch</b> <b>(</b>Exception e<b>)</b> <b>{</b><b>}</b>
System<b>.</b>out<b>.</b>println<b>(</b>"Select Root"<b>)</b><b>;</b>
resp = sendAPDU<b>(</b>selectRoot<b>)</b><b>;</b>
<b>if</b> <b>(</b>resp<b>.</b>sw<b>(</b><b>)</b> != 0x9000<b>)</b> <b>{</b>
results<b>.</b>put<b>(</b>"error"<b>,</b> "Unable to Select Root"<b>)</b><b>;</b>
results<b>.</b>put<b>(</b>"errorData"<b>,</b> resp<b>)</b><b>;</b>
status = "Load Error:" + resp<b>;</b>
<b>return</b> <b>(</b>results<b>)</b><b>;</b>
<b>}</b>
System<b>.</b>out<b>.</b>println<b>(</b>"Select File"<b>)</b><b>;</b>
resp = sendAPDU<b>(</b>selectFileId<b>)</b><b>;</b>
<b>if</b> <b>(</b>resp<b>.</b>sw<b>(</b><b>)</b> != 0x9000<b>)</b> <b>{</b>
results<b>.</b>put<b>(</b>"error"<b>,</b> "Unable to Select File"<b>)</b><b>;</b>
results<b>.</b>put<b>(</b>"errorData"<b>,</b> resp<b>)</b><b>;</b>
status = "Load Error:" + resp<b>;</b>
The figure below represents a smart card communicating through a client to a server. The user can download a file from the server onto the smart card via a browser. When requested by the user, the file containing the Java Card application is loaded by the browser and transferred to the UPI server at the local host, as represented by the arrow marked C. Once an application loads onto a card, it must communicate with a proxy service, which can send the data to the user’s browser. The applets in the user’s browser can send the data back to the server. There, the data can be used for other functions, such as authentication and ebusiness.
We have adopted a convention of calling the host program a proxy service and the applet that runs on the card the card applet. The source code for JavaCardCCProxyservice
is in JavaCardCCProxyservice.html
. When an application wishes to communicate with the card and get the ID, for instance, it communicates via the JavaCardCCProxyService
class.
The card applet
The card applet contains the methods that the proxy can communicate with via the OpenCard card terminal. We provide a rather large card applet and hope readers will take what they need from it to construct new applets that are useful to them. In order to provide a concise example, however, we focus on a limited set of abilities. The source code for the CorporateCard applet can be found in CorporateCard.html
.
The SecureTokenServices handler
You will likely want to spend most of your time developing smart card applications that take advantage of the cards’ strength, storing secrets in a very portable format. Of course, you’ll still need to develop additional applications to communicate with the card. One of the primary advantages of the UPI is that, once you load an application onto a card, you can easily communicate with this application by using either PassThroughService
, which we touched on earlier, or by coding a simple handler that takes URLs and maps them to transactions that the Java Card performs.
The UPI uses the model of a card terminal (also known as a card reader) and smart card as a Web server that understands a number of URLs. Sun developed this technology to simplify the deployment of smart card solutions to millions of users. Some early prototypes for GemPlus cards have been built by platform seven (see Resources). Schlumberger developed the first OpenCard smart card loader service that uses the UPI.
Source code for the SecureTokenServices
handlers supports URLs that allow you to implement complete end-to-end solutions for smart card micro- or macropayments, personal profiling, form filling and authentication can be found in SecureTokenDeviceHandler.html
.
Conclusion
With the information in this article, and Java Cards from your favorite manufacturer, you can develop and test smart card applications that allow you to download an application to a user’s card. Although this article focused on Schlumberger’s Cyberflex Access card, equivalent solutions have been realized with Java iButtons from Dallas Semiconductor and GemExpresso cards from GemPlus. Loaders for those devices are available in the Resources section.