Home | Products | Partners | FAQ | Library | Links | Company |
Our Articles Simple and powerfull GSM + LTE Authentication Calculator: TUAK, Milenage, COMP128-1, 2, 3, Xor Visualyze and Analyze all APDUs between handset and RUIM, (U)SIM All you need to work with SIM, USIM, R-UIM card: build card tree, read, write, export GSM 03.48 compliant solutions for Over-The-Air campaign DES, 3DES, AES, MD5, and other encryptions and hashes Parse an ISO 7816-3 ATR online A collection of Java Card projects in source A simple tool to convert CAP files into IJC format
|
Chapter 3Developing RMI Applications for the Java Card PlatformThis 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. Steps for Developing an RMI Applet for the Java Card PlatformThe main steps for developing an RMI applet for the Java Card platform include: For a simple applet, the main class of the applet could also be the class implementing the remote interface. Generating Stubs
The Java Card RMI Client framework requires stubs only when the
In this example, Sun Microsystems, Inc.’s standard RMI Compiler (
The command line to run the where:
The
The
Note – Some versions of the
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 Running a Java Card RMI AppletThe 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. Running the Java Card RMI Client Program
The client program can be compiled using Running the client program requires that:
For a description of the
For a sample command line to run a client program, refer to the file
The
To configure the client to use the card reader, change the
where Basic ExampleThe 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. The Main ProgramAs 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: Implement a Remote InterfaceThe 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
The class also extends the Define the Constructor for the Remote ObjectThe 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
If a remote object does not extend To review: The implementation class for a remote object needs to: Provide an Implementation for Each Remote MethodThe 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: On the other hand, object passing in Java Card RMI follows the normal RMI rules:
Note – Even though the semantics of the Java Card platform transient arrays is somewhat similar to transient fields in the Java programming language, different rules apply; its contents are copied in Java Card RMI and passed by value when it is returned from a remote method.
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. Building an Applet
In version 2.2.1 of the Java Card platform (as in version 2.1), all applications must include a class that inherits from 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) ; } } Preparing and Registering the Remote Object
The 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 Processing the Incoming CommandsThe 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. Writing a ClientThe client application runs on a terminal supporting a Java Virtual machine environment such as J2SE or J2ME.
The
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”) The OCF can be downloaded from the OCF Web site: http://www.opencard.org/index-downloads.html
The following shows a very simple 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() ; } } } } Initializing and Shutting Down OCFThe 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() ; } } Obtaining and Using the OCFCardAccessor Object
To access the Java Card applet using remote methods, the client application needs to obtain an instance of the
The code below shows how the client interacts with the OCF services. Note that the client specifies that it is requesting a // wait for a smartcard CardRequest cr = new CardRequest (CardRequest.NEWCARD,null,
The // create an RMI connection object for the Java Card platform JavaCardRMIConnect jcRMI = new JavaCardRMIConnect( myCS ) ; Selecting the Java Card Applet and Obtaining the Initial Reference
In order to invoke methods on the remote objects of the Java Card applet // 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 // obtain the initial reference to the Purse interface Purse myPurse = (Purse) jcRMI.getInitialReference() ; Using Remote Objects in Remote Method Invocations
The client can now invoke remote methods on the initial reference object. The remote methods are declared in the // debit the requested amount try { short balance = myPurse.debit ( debitAmount ) ; }catch ( UserException jce ) { short reasonCode = jce.getReason() ; // process on card exception reason information } Generate the Stubs
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
The stub class Solaris and Linux platforms: Microsoft Windows 2000 platform:
This produces a stub class called
Thus, for Card Terminal Interaction
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
FIGURE 1 – Smart card sends an ATR to the terminal
When the
FIGURE 2 – Terminal sends a SELECT command to the smart card. The card returns an FCI to the terminal
Later, when the
FIGURE 3 – Terminal sends an INVOKE command to the smart card. The card returns a value to the terminal.
Adding SecurityThis 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
The 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. Initialize a Security Service
In this example, basic security services (principal identification and authentication, secure communication channel) are provided by an object that implements the
The Use the Service to Check the Current Security StatusIn the example, this required security behavior for the applet is assumed:
The 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. Implementing a Security Servicepackage 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 } } Building an AppletThe 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
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 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. Writing a Client
The driver client application itself only changes minimally to account for the authentication and integrity needs of
Here is the new 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 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
The rest of Writing a Custom SecureOCFCardAccessor Class
The 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
This custom Java Card RMI Card Accessor class also re-implements the
|