![]() ![]() ![]() ![]() |
This chapter describes how to write RMI applications for the Java Card platform. In this release, you can run and debug Java Card RMI applications in the C-language Java Card RE and the Java Card WDE.
The main steps for developing an RMI applet for the Java Card platform include:
main
class for the appletFor a simple applet, the main class of the applet could also be the class implementing the remote interface.
The Java Card RMI Client framework requires stubs only when the remote_ref_with_class
format is used for passing remote references. These stubs of remote classes of applets must be pre-generated and available on the client. When the remote_ref_with_interfaces
format is used, stubs are not necessary.
In this example, Sun Microsystems, Inc.’s standard RMI Compiler (rmic
) is used to generate these stubs.
The command line to run the rmic
is:
where:
<
path>
includes the path to the remote class
<
output_dir>
is the directory in which to place the resulting stubs
<
class_name>
is the name of the remote class
The -v1.2
flag is required by the RMI client framework for the Java Card platform.
The rmic
must be called for each remote class in your applet.
rmic
do not generate stubs for remote classes which do not list remote interfaces in their implements
clause. If you encounter this situation, list at least one remote interface in the implements
clause for each remote class in your project. It is permissible for such a remote class to use a remote interface from a superclass.
The file javacardframework.jar
is provided in version 2.2.1 of the Development Kit for the Java Card platform (“Java Card Development Kit”). This jar file contains compiled implementations of packages javacard.framework
, javacard.framework.service
, and javacard.security
. Classes in these packages might be referenced by Java Card RMI applets and thus might be needed by the rmic
to generate stubs.
The server part (that is, the Java Card RMI-enabled applet) can be run on both CREF and Java Card platform Workstation Development Environment (“Java Card WDE”).
To run the applet on CREF, the standard procedures apply: the applet must be installed first, using the installer applet. After the applet is installed, the EEPROM state can be saved and used to run CREF against the Java Card RMI client.
The simplest way to run a Java Card RMI-enabled applet on the Java Card WDE is to add it to the WDE configuration file on the first line. This uses the fact that the Java Card WDE automatically installs the first applet on “power up”. The Java Card WDE is a very convenient environment to debug Java Card RMI applets. Of course, all of the standard limitations (such as absence of firewall support) apply.
The client program can be compiled using javac
or your favorite IDE. The compiler will require that the remote interfaces for your applet be present in your classpath.
Running the client program requires that:
base-core.jar
and base-opt.jar
must be present in the CLASSPATH.opencard.properties
must be present in one of the directories specified in the OpenCard Framework 1.2 Programmers Guide.jcrmiclientframework.jar
must be present in the classpath. This file contains all the client framework and necessary classes from the card framework.
For a description of the opencard.properties
file, refer to OCF documentation. A sample opencard.properties
file is located in the samples/src/demo
directory Java Card Development Kit, version 2.2.1, and can be used without modification.
For a sample command line to run a client program, refer to the file rmidemo
or rmidemo.bat
in this directory.
The opencard.properties
file supplied in the samples/src/demo
directory configures the client to use the Java Card WDE or CREF. The RMI client can also use a TLP224-compliant card reader connected to a serial port.
To configure the client to use the card reader, change the OpenCard.terminals
line in the opencard.properties
file to the following:
where <
port#>
is the name of the serial port, such as COM1
.
The basic example that we will use is the Java Card platform equivalent of “Hello World”, which is a program that manages a counter remotely, and is able to decrement the value of the counter, increment the value of the counter, and return the value of the counter.
As for any Java Card RMI program, the first thing that we need to do is to define the interface that will be used as contract between the server (that is, the Java Card technology-based application) and its clients (that is, the terminal applications):
package examples.purse ; import java.rmi.* ; import javacard.framework.* ; public interface Purse extends Remote { public static final short MAX_AMOUNT = 400 ; public static final short REQUEST_FAILED = 0x0102 ; public short debit(short amount) throws RemoteException, UserException; public short credit(short amount) throws RemoteException, UserException ; public short getBalance() throws RemoteException, UserException ; }
This interface is a typical Java Card RMI interface:
java.rmi.Remote
interface. This interface is a tagging interface that identifies the interface as defining a remotely accessible object.RemoteException
or one of its superclasses (IOException
or Exception
). This exception is required in order to encapsulate all the communication problems that may occur during a remote invocation of the method. In addition the credit
, debit
and getBalance
methods also throw the UserException
to indicate application-specific errors.Purse
interface defines a constant MAX_AMOUNT
that represents the maximum allowed value for the transaction amount parameter. It also defines a reason code REQUEST_FAILED
for the UserException
qualifier.The next step consists in providing an implementation for this interface. This implementation will run on a Java Card platform, and it therefore needs to use only features that are supported by a Java Card platform:
package examples.purse ; import javacard.framework.* ; import javacard.framework.service.* ; import java.rmi.* ; public class PurseImpl extends CardRemoteObject implements Purse { private short balance ; PurseImpl() { super() ; balance = 0 ; } public short debit(short amount) throws RemoteException, UserException { if (( amount < 0 )||( amount > MAX_AMOUNT )) UserException.throwIt(REQUEST_FAILED) ; balance -= amount ; return balance ; } public short credit(short amount) throws RemoteException, UserException { if (( amount < 0 )||( balance < amount )) UserException.throwIt(REQUEST_FAILED) ; balance -= amount ; return balance ; } public short getBalance() throws RemoteException, UserException { return balance ; } }
Here, the remote interface is the Purse
interface, which declares the remotely accessible methods. By implementing this interface, the class establishes a contract between itself and the compiler, by which the class promises that it will provide method bodies for all the methods declared in the interface:
The class also extends the javacard.framework.service.CardRemoteObject
class. This class provides our class with basic support for remote objects, and in particular the ability to export and/or unexport an object.
The constructor for a remote class provides the same functionality as the constructor of a non-remote class: it initializes the variables of each newly created instance of the class.
In addition, the remote object instance will need to be “exported”. Exporting a remote object makes it available to accept incoming remote method requests. By extending CardRemoteObject
, a class guarantees that its instances are exported automatically upon creation on the card.
If a remote object does not extend CardRemoteObject
(directly or indirectly), you will need to explicitly export the remote object by calling the CardRemoteObject.export
method in the constructor of your class (or in any appropriate initialization method). Of course, this class must still implement a remote interface.
To review:
The implementation class for a remote object needs to:
The implementation class for a remote object contains the code that implements each of the remote methods specified in the remote interface. For example, here is the implementation of the method that debits the purse:
public short debit(short amount) throws RemoteException, UserException if (( amount < 0 )||( balance < amount ) UserException.throwIt(REQUEST_FAILED) ; balance -= amount ; return balance ; }
An operation is only allowed if the value of its parameter is compatible with the current state of the purse object. In this particular case, the application only checks that the amounts handled are positive and that the balance of the purse always remains positive.
In Java Card RMI, the arguments to, and return values from, remote methods are restricted. The main reason for this limitation is that the Java Card platform does not support object serialization. The rules for the Java Card platform are:
boolean
, byte
, short
and int
), or any single-dimensional arrays of these integral types. (Note: The int
type is optionally supported on the Java Card platform, so applications that use this type may not run on all platforms.)void
.On the other hand, object passing in Java Card RMI follows the normal RMI rules:
static
or transient
. In the case of the Java Card platform, this rule is trivial to apply, since the only objects concerned are arrays of integral types.remote_ref_with_class
is used for passing remote references. When another format, such as remote_ref_with_interfaces
, is used, stubs are not necessary. Stubs are described in "Generate the Stubs" .A class can define methods not specified in a remote interface, but they can only be invoked on-card within the Java Card VM and cannot be invoked remotely.
In version 2.2.1 of the Java Card platform (as in version 2.1), all applications must include a class that inherits from javacard.framework.Applet
, which will provide an interface with the outside world. This also applies to applications which are based on remote objects, for two main reasons:
install
method.process
method.For conversion, an applet should be assigned with an AID known on the client side.
This is the basic code for such an applet:
package examples.purse ; import javacard.framework.* ; import javacard.framework.service.* ; import java.rmi.*; public class PurseApplet extends Applet { private Dispatcher dispatcher ; private PurseApplet() { // Allocates an RMI service and sets for the Java Card platform // the initial reference RemoteService rmi = new RMIService( new PurseImpl() ) ; // Allocates a dispatcher for the remote service dispatcher = new Dispatcher((short)1) ; dispatcher.addService(rmi, Dispatcher.PROCESS_COMMAND) ; } public static void install(byte[] buffer, short offset, byte length) { // Allocates and registers the applet (new PurseApplet()).register() ; } public void process(APDU apdu) { dispatcher.process(apdu) ; } }
The PurseApplet
constructor contains the initialization code for the remote object. First, a javacard.framework.service.RMIService
object needs to be allocated. This service is an object that knows how to handle all the incoming APDU commands related to the Java Card RMI protocol. The service needs to be initialized to allow remote methods on an instance of the PurseImpl
class. A new instance of PurseImpl
is created, and is specified as the initial reference parameter to the RMIService
constructor as shown in the code snippet below. The initial reference is the reference that is made public by an applet to all its clients. It is used as a bootstrap for a client session, and is similar to that registered by a Java RMI server to the Java Card RMI registry.
Then, a dispatcher is created and initialized. A dispatcher is the glue amongst several services. In our example, the initialization is quite simple, since there is a single service to initialize:
Finally, the applet needs to register itself to the Java Card RE, to be made selectable. This is done in the install
method, where the applet constructor is invoked and immediately registered:
The processing of the incoming commands is entirely delegated to the Java Card RMI service, which knows how to handle all the incoming requests. The service will also implement a default behavior for the handling of any request that it does not recognize. In Java Card RMI, there are two kinds of requests that can be handled:
To perform these actions, the service will need privileged access to some resources that are owned by the Java Card RE (in particular to perform the method invocation). The applet delegates processing to the Java Card RMI service from its process method as follows:
This corresponds to the simplest case, in which the handling of commands is entirely delegated to the dispatcher.
The client application runs on a terminal supporting a Java Virtual machine environment such as J2SE or J2ME.
The PurseClient
application interacts with the remote stub classes generated by a stub generation tool and the Java Card platform-specific information managed by the Java Card platform client-side framework com.sun.javacard.javax.smartcard.rmiclient
package.
The example we describe here uses standard Java RMIC compiler generated client side stubs. The client application as well as the Java Card client-side framework rely on the Open Card Framework (OCF) for managing and communicating with the card reader and the card on which the Java Card technology-based applet (“Java Card applet”) PurseApplet
resides. This makes the client application very portable on J2SE platforms.
The OCF can be downloaded from the OCF Web site:
http://www.opencard.org/index-downloads.html
The following shows a very simple PurseClient
application which is the client application of the Java Card technology-based program PurseApplet
:
import opencard.core.service.* ; import examples.purse.* ; import com.sun.javacard.javax.smartcard.rmiclient.* ; import com.sun.javacard.ocfrmiclientimpl.* ; import javacard.framework.UserException ; public class PurseClient extends java.lang.Object { /** Creates new PurseClient */ public PurseClient() { } public static void main(java.lang.String[] argv) { // arg[0] contains the debit amount short debitAmount = (short) Integer.parseInt(argv[0]) ; try { // initialize OCF SmartCard.start() ; // wait for a smartcard CardRequest cr = new CardRequest (CardRequest.NEWCARD, null,OCFCardAccessor.class); SmartCard myCard = SmartCard.waitForCard ( cr ) ; // obtain an RMI Card Accessor CardService for the // Java Card platform CardAccessor myCS = (CardAccessor) myCard.getCardService(OCFCardAccessor.class, true) ; // create an RMI connector instance for the Java Card platform JavaCardRMIConnect jcRMI = new JavaCardRMIConnect( myCS ) ; // select the Java Card applet byte[] appAID = new byte[] {0x01,0x02,0x03,0x04,0x05,0x06,0x07, 0x08}; jcRMI.selectApplet( appAID ) ; // obtain the initial reference to the Purse interface Purse myPurse = (Purse) jcRMI.getInitialReference() ; // debit the requested amount try { short balance = myPurse.debit ( debitAmount ) ; }catch ( UserException jce ) { short reasonCode = jce.getReason() ; // process UserException reason information } // display the balance to user }catch (Exception e) { e.printStackTrace() ; } finally { try { SmartCard.shutdown() ; }catch (Exception e) { e.printStackTrace() ; } } } }
The client application needs to initialize the OCF classes on the terminal. The following code shows this as well as how it is shut down:
try { // initialize OCF SmartCard.start() ; // the main client work is performed here // ... }catch (Exception e) { e.printStackTrace() ; } finally { try{ SmartCard.shutdown() ; }catch (Exception e) { e.printStackTrace() ; } }
To access the Java Card applet using remote methods, the client application needs to obtain an instance of the CardAccessor
interface. The OCFCardAccessor
class implements the CardAccessor
interface and is an OCF card service.
The code below shows how the client interacts with the OCF services. Note that the client specifies that it is requesting a SmartCard
object on any newly inserted card which supports the OCFCardAccessor
class:
// wait for a smartcard CardRequest cr = new CardRequest (CardRequest.NEWCARD,null,OCFCardAccessor
.class) ; SmartCard myCard = SmartCard.waitForCard ( cr ) ; // obtain a Java Card API Accessor for OCFCardAccessor
myCS = (CardAccessor
) myCard.getCardService(OCFCardAccessor
.class, true) ;
The CardAccessor
interface is a platform and framework independent interface which is used by the RMI framework for the Java Card platform to communicate with the card. The CardAccessor
object is then provided as a parameter during construction of the JavaCardRMIConnect
class to initiate an RMI dialogue for the Java Card platform as shown below:
// create an RMI connection object for the Java Card platform JavaCardRMIConnect jcRMI = new JavaCardRMIConnect( myCS ) ;
In order to invoke methods on the remote objects of the Java Card applet PurseApplet
on the card it must first be selected using the AID:
// select the Java Card applet byte[] appAID = new byte[] {0x01,0x02,0x03,0x04,0x05,0x06,0x07, 0x08} ; jcRMI.selectApplet( appAID ) ;
Then, the client needs to obtain the initial reference remote object for PurseApplet
. JavaCardRMIConnect
returns an instance of a stub class corresponding to the PurseImpl
class on the card which implements the Purse
interface. The client application knows beforehand that the PurseApplet
’s initial remote reference implements the Purse
interface and therefore casts it appropriately:
// obtain the initial reference to the Purse interface Purse myPurse = (Purse) jcRMI.getInitialReference() ;
The client can now invoke remote methods on the initial reference object. The remote methods are declared in the Purse
interface. The code below shows the client invoking the debit
method. Note how an UserException
exception thrown by the remote method is caught by the client code in a normal Java program style.
// debit the requested amount try { short balance = myPurse.debit ( debitAmount ) ; }catch ( UserException jce ) { short reasonCode = jce.getReason() ; // process on card exception reason information }
The client side scenario outlined above uses RMIC generated stubs for the remote classes. RMIC is the Java RMI stub compiler. For the client application PurseClient
to execute correctly on the terminal, it needs these remote stub classes and the remote interface class files it uses to be accessible in its classpath.
The stub class PurseImpl_Stub.class
for the PurseImpl
class is produced by running the standard JDK1.2 or JDK1.3 RMIC compiler. For example, when in the examples/purse
directory, enter:
Solaris and Linux platforms:
Microsoft Windows 2000 platform:
This produces a stub class called examples.purse.PurseImpl_Stub
.
Thus, for PurseClient
to run correctly on the terminal, the following files must be present in the examples/purse
directory and accessible via its classpath or from class loaders:
When a Java Card technology-enabled smartcard is first inserted into the card reader, the card sends an ATR (Answer to Reset) to the terminal. The Open Card Framework uses this to help locate the Card Service which may be suitable. In our case, the OCFCardAccessorFactory
, which has been previously registered with OCF, responds with OCFCardAccessor
support. FIGURE 1 illustrates this schematically.
When the PurseClient
application calls the selectApplet
method of JavaCardRMIConnect
, it sends a SELECT APDU Command to the card via the OCFCardAccessor
object. This results in a File Control Information (FCI) APDU response from the RMIService
instance of PurseApplet
on the card in a TLV (Tag Length Value) format which includes the initial reference remote object information. FIGURE 2 illustrates this schematically.
Later, when the PurseClient
application calls the debit
method of the remote interface Purse
, the PurseImpl_Stub
object sends an invoke command to the card via the OCFCardAccessor
object, identifying the remote object reference, interface, method and parameter data for method invocation. The RMIService
instance of PurseApplet
unmarshalls this information and invokes the debit
method of the PurseImpl
instance and returns the return value in the response APDU. FIGURE 3 illustrates this schematically.
This first example is extremely simple and is not realistic. In particular, it does not include any kind of security. Users are not authenticated, and no transport security is provided. Of course, every smart card that implements the Java Card platform will include such security mechanisms, since they are central to Java Card technology.
In the following section, you will see how to add security support to the Purse
example.
The Purse
interface in the package examples.securepurse
is similar to the Purse
interface in the previous code sample. In addition, it might include reason codes for exceptions to report security violations to the terminal. It should be replaced with examples.securepurse
. The interface does not include any implementation, which means that, in particular, it does not include any support for security.
package examples.securepurse ; import javacard.framework.* ; import javacard.framework.service.* ; import java.rmi.* ; public class SecurePurseImpl implements Purse { private short balance ; private SecurityService security ; SecurePurseImpl(SecurityService security) { this.security = security ; } public short debit(short amount) throws RemoteException, UserException { if ((!security.isCommandSecure(SecurityService.PROPERTY_INPUT_INTEGRITY)) || (!security.isAuthenticated(SecurityService.PRINCIPAL_CARDHOLDER))) UserException.throwIt(REQUEST_FAILED) ; if (( amount < 0 )|| ( balance < amount )) UserException.throwIt(REQUEST_FAILED) ; balance -= amount ; return balance ; } public short credit(short amount) throws RemoteException, UserException { if ((!security.isCommandSecure(SecurityService.PROPERTY_INPUT_INTEGRITY)) || (!security.isAuthenticated(SecurityService.PRINCIPAL_APP_PROVIDER))) UserException.throwIt(REQUEST_FAILED) ; if (( amount < 0 )||( amount > MAX_AMOUNT )) UserException.throwIt(REQUEST_FAILED) ; balance += amount ; return balance ; } public short getBalance() throws RemoteException, UserException { if ((!security. isAuthenticated(SecurityService.PRINCIPAL_CARDHOLDER)) && (!security.isAuthenticated(SecurityService.PRINCIPAL_APP_PROVIDER))) UserException.throwIt(REQUEST_FAILED) ; return balance ; } }
The applet keeps its original organization, but it also includes additional code that is dedicated to the management of security.
In this example, basic security services (principal identification and authentication, secure communication channel) are provided by an object that implements the SecurityService
interface. Since a generic remote object should not be dependent on a particular kind of security service, it should take a reference to this object as a parameter to its constructor. This is exactly what happens here, where the reference to the object is stored in a dedicated private field:
The SecurityService
interface is part of the extended application development framework and offers an API that can then be used to check on the current security status.
In the example, this required security behavior for the applet is assumed:
debit
method should only be authorized if it is sent through a secure channel that ensures at least the integrity of input data, and if the cardholder has been successfully authenticated.credit
method should only be authorized if it is sent through a secure channel that ensures at least the integrity of input data, and if the application issuer has been successfully authenticated.getBalance
method should only be authorized if the cardholder or the application issuer has been successfully authenticated.
The SecurityService
provides methods and constants that allow the implementation to perform such checks. For instance, the code for the checks on the debit
method is:
if ((!security.isCommandSecure(SecurityService.PROPERTY_INPUT_INTEGRITY)) || (security.isAuthenticated(SecurityService.ID_CARDHOLDER))) UserException.throwIt(REQUEST_FAILED) ;
This code is quite self-explanatory. If one of the two conditions is not satisfied, then the remote object throws an exception. This exception is caught by the dispatcher and forwarded to the client.
package com.sun.javacard.samples.SecureRMIDemo ; import javacard.framework.* ; import javacard.framework.service.* ; public class MySecurityService extends BasicService implements SecurityService { // list IDs of known parties... private static final byte[] PRINCIPAL_APP_PROVIDER_ID = {0x12, 0x34} ; private static final byte[] PRINCIPAL_CARDHOLDER_ID = {0x43, 0x21} ; private OwnerPIN provider_pin, cardholder_pin = null ; // and the security-related session flags ... public MySecurityService() { // initialize the PINs ... } public boolean processDataIn(APDU apdu) { if(selectingApplet()) { // reset all flags ... } else { return preprocessCommandAPDU(apdu); } } public boolean isCommandSecure(byte properties) throws ServiceException { // return the value of appropriate flag .... } public boolean isAuthenticated(short principal) throws ServiceException { // return the value of appropriate flag .... } private byte authenticated ; private boolean preprocessCommandAPDU(APDU apdu) { receiveInData(apdu) ; if(checkAndRemoveChecksum(apdu)) { // set DATA_INTEGRITY flag } else { // reset DATA_INTEGRITY flag } return false; // other services may also preprocess the data } private boolean checkAndRemoveChecksum(APDU apdu) { // remove the checksum // return true if checksum OK, false otherwise } public boolean processCommand(APDU apdu) { if(isAuthenticate(apdu)) { receiveInData(apdu) ; // check PIN // set AUTHENTICATED flags return true; // processing of the command is finished } else { return false ; // this command was addressed to another // service - no processing is done } } public boolean processDataOut(APDU apdu) { // add checksum to outgoing data return false; // other services may also postprocess outgoing data } private boolean isAuthenticate(APDU command) { // check values of CLA and INS bytes } }
The supporting applet also needs to undergo some significant changes, in particular regarding the initialization of the remote object:
package examples.securepurse ; import javacard.framework.* ; import javacard.framework.service.* ; import java.rmi.* ; import com.sun.javacard.samples.SecureRMIDemo.MySecurityService ; public class SecurePurseApplet extends Applet { Dispatcher dispatcher ; private SecurePurseApplet() { SecurityService sec ; // First get a security service sec = new MySecurityService() ; // Allocates an RMI service for the Java Card platform and // sets the initial reference RemoteService rmi = new RMIService( new SecurePurseImpl(sec) ) ; // Allocates and initializes a dispatcher for the remote object dispatcher = new Dispatcher((short)2) ; dispatcher.addService(rmi, Dispatcher.PROCESS_COMMAND) ; dispatcher.addService(sec, Dispatcher.PROCESS_INPUT_DATA) ; } public static void install(byte[] buffer, short offset, byte length) { // Allocates and registers the applet (new SecurePurseApplet()).register() ; } public void process(APDU apdu) { dispatcher.process(apdu) ; } }
The security service that is used by the remote object needs to be initialized at some point. Here, this is done in the constructor for the SecurePurseApplet
:
The initialization then goes on with the initialization of the Java Card RMI service. The only new thing here is that the remote object being allocated and set as the initial reference is now a SecurePurseImpl
:
Next, we need to initialize the dispatcher. Here, it needs to not only dispatch simple Java Card RMI requests, but also to dispatch security-related requests (such as EXTERNAL AUTHENTICATE). In fact, the security service will handle these requests directly. We first need to allocate a dispatcher and to inform it that it will delegate commands to two different services:
Then, the services are “registered” with the dispatcher: the security service as a service that performs pre-processing operations on incoming commands, and the Java Card RMI service as a service that processes the command requested:
dispatcher.addService(rmi, Dispatcher.PROCESS_COMMAND) ; dispatcher.addService(sec, Dispatcher.PROCESS_INPUT_DATA) ;
The rest of the class (that is, the install and process methods) remain unchanged.
The driver client application itself only changes minimally to account for the authentication and integrity needs of SecurePurseApplet
. It does additionally need to interact with the user for identification. Hence, a subclass of OCFCardAccessor
needs to be developed which provides these additional interactions and the transport filtering required.
Here is the new SecurePurseClient
application:
package examples.securepurseclient ; import com.sun.javacard.javax.smartcard.rmiclient.* ; import com.sun.javacard.clientsamples.securepurseclient.SecureOCFCardAccessor ; import opencard.core.service.* ; import examples.securepurse.* ; import javacard.framework.UserException ; public class SecurePurseClient extends java.lang.Object { // need to authenticate user with the secure purse applet // prompt user for account and password information // to initialize user key and card info from database private final static short PRINCIPAL_CARDHOLDER_ID=0x4321 /** Creates new SecurePurseClient */ public SecurePurseClient() { } public static void main(java.lang.String[] argv) { // arg[0] contains the debit amount short debitAmount = (short) Integer.parseInt(argv[0]) ; try { // initialize OCF SmartCard.start() ; // wait for a smartcard CardRequest cr = new CardRequest ( CardRequest.NEWCARD, null, SecureOCFCardAccessor.class ) ; SmartCard myCard = SmartCard.waitForCard ( cr ); // obtain a custom RMI CardAccessor class for // the Java Card platform: SecureOCFCardAccessor SecureOCFCardAccessor myCS = (SecureOCFCardAccessor) myCard.getCardService( SecureOCFCardAccessor.class, true ) ; //create RMI connection for the Java Card platform JavaCardRMIConnect jcRMI = new JavaCardRMIConnect(myCS) ; // select the Java Card applet byte[] appAID = new byte[] {0x01,0x02,0x03,0x04,0x05,0x07} ; jcRMI.selectApplet( appAID ); if (! myCS.authenticateUser( PRINCIPAL_CARDHOLDER_ID )) { // handle error } // obtain the initial reference to the Purse interface Purse myPurse = (Purse) jcRMI.getInitialReference() ; // debit the requested amount try { // the debit invocation command will be signed // by SecureOCFCardAccessor short balance = myPurse.debit ( debitAmount ) ; }catch ( UserException jce ) { short reasonCode = jce.getReason() ; // process on card exception reason information } // display the balance to user }catch (Exception e) { e.printStackTrace(); } finally { try{ SmartCard.shutdown(); }catch (Exception e) { e.printStackTrace(); } } } }
Note how the SecureOCFCardAccessor
instance is now obtained instead of OCFCardAccessor
:
CardRequest cr = new CardRequest ( CardRequest.NEWCARD, null, SecurePurseClientCardService.class ) ; SmartCard myCard = SmartCard.waitForCard ( cr ); // obtain a customized Card Accessor class: SecureOCFCardAccessor SecureOCFCardAccessor myCS = (SecureOCFCardAccessor) myCard.getCardService( SecureOCFCardAccessor.class, true ) ;
An extra step to authenticate with the SecurePurseApplet
after selectApplet
has been added. This invokes a new method in SecureOCFCardAccessor
to interact with the card using the users credentials:
The rest of SecurePurseClient
is the same as PurseClient
.
The SecurePurseClient
application uses a subclass of OCFCardAccessor
called SecureOCFCardAccessor
to perform user authentication functions and to sign every message sent thereafter for integrity purposes:
package examples.securepurseclient; import opencard.core.terminal.* ; public class SecureOCFCardAccessor extends com.sun.javacard.ocfrmiclientimpl.OCFCardAccessor { /** Creates new SecureOCFCardAccessor */ public SecureOCFCardAccessor() { } public byte[] exchangeAPDU( byte[] sendData ) throws java.io.IOException { byte[] macSignature = null ; byte[] dataWithMAC = new byte[ sendData.length + 4 ] ; // sign the sendData data using session key // sign the data in commandBuffer using the user's session key // add generated MAC signature to data in buffer before sending return super.exchangeAPDU( dataWithMAC ) ; } boolean authenticateUser( short userKey ) { byte[] externalAuthCommand = null ; // build and send the appropriate commands to the // applet to authenticate the user using the user Key // and additional info provided try { byte[] response = super.exchangeAPDU ( externalAuthCommand ) ; // ... }catch (Exception e) { // analyze return false ; } // Then compute the session key for later use return true; //successful authentication } }
As shown above, the SecureOCFCardAccessor
class introduces the authenticateUser
method to send APDU Commands to the SecurePurseApplet
on the card to authenticate the user described by the userKey
parameter and other parameters and to compute a transport key. It invokes super.sendCommandAPDU
method to send the command without modification.
This custom Java Card RMI Card Accessor class also re-implements the exchangeAPDU
method declared in a superclass OCFCardAccessor
to sign each message before it is sent out by super.exchangeAPDU
.
![]() ![]() ![]() ![]() |
Application Programming Notes Java Card Platform, Version 2.2.1 |
Copyright © 2003 Sun Microsystems, Inc. All rights reserved.