/**
* Copyright (c) 1999 GEMPLUS group. All Rights Reserved.
*------------------------------------------------------------------------------
* Project name: GemXpresso RAD 211 Environment
* - PurseClient a OP/VOP client example for Purse applet
*
* Platform : Java virtual machine
* Language : JAVA 1.1.x
* Devl tool : JBuilder 3.5
*
* Original author: Gemplus Java Card Group Software Environment
*------------------------------------------------------------------------------
*/
/*
* Package name
*/
package com.gemplus.examples.purse.client;
/*
* Imported packages
*/
import java.io.*;
// standard Java imports
//import java.util.Random;
// standard OCF imports
import opencard.core.service.*;
import opencard.core.terminal.*;
import opencard.opt.applet.AppletID;
// gemplus OCF imports
//import com.gemplus.opencard.service.*;
//import com.gemplus.opencard.terminal.*;
// gemplus VISA imports
import com.gemplus.opencard.service.op.*;
import com.gemplus.opencard.service.op.vop.*;
import com.gemplus.opencard.service.op.vop.vop211.*;
import com.gemplus.opencard.service.op.vop.vop200.*;
import com.gemplus.tools.gemxpresso.*;
// GSE imports
import com.gemplus.javacard.gse.Simulator;
import com.gemplus.javacard.gse.util.APDUPrinter;
import com.gemplus.tools.gemxpresso.util.GxpSystem;
/**
* The client application that manages and uses the 'Purse' applet in the Card or GSE.
*
* This implementation uses the OCF 1.1.1 framework and the target specific
* CardTerminal to communicate with the 'Purse' applet .
*
* This client loads a package, installs an applet, selects it and sends it some
* APDU commands.
*/
class PurseClient
{
/**
* global declarations
*/
// OP/VOP OCF CardService and its high level API library
private CardServiceOPCore serv;
private boolean v2 = true;
private GemXpressoService libService;
// OCF CardTerminal for Card or GSE communication
private CardTerminal terminal;
// OCF objects for APDU transport
private CommandAPDU cmd;
private ResponseAPDU resp;
// print utility object
private APDUPrinter printer = new APDUPrinter();
// rad directory location
// !!!!! WARNING !!!!!
// HERE IS DEFAULT "GemXpresso RAD 211" INSTALL LOCATION
// CHANGE TO YOUR CUSTOM INSTALL DIRECTORY
// Windows platform (c:\gemplus\gemxpresso.rad2)
private String homeDir = "c:" + File.separator + "gemplus" + File.separator + "gemxpresso.rad3";
//targets path (\resources\targets)
private String keyFileDir;
//sample dir (\examples\Purse)
private String sampleDir;
//card profile dir (\resouces\cardprofile)
private String profileDir;
//the package path
private String appletPackagePath = "com" + File.separator + "gemplus" + File.separator + "examples" +
File.separator + "purse";
//jar full filename (\examples\Purse\\\javacard\Purse.jar)
private String jarFileName;
//sap full filename (\examples\Purse\\\Purse.sap)
private String sapFileName;
/**
* GSE target name
*/
// the name defined in the CardTerminal section of the "opencard.properties" file
private String gseTarget = "Simulator";
// the GSE key file
private String gseKeyFile = "GSE211v2.properties";
// the key set version available in the GSE at startup
private int gseKeyVersion = 13;
/**
* Card target names
*/
// the name defined in the CardTerminal section of the "opencard.properties" file
private String cardTarget = "gcr410_com1";
// the Card key file (GXP211v2 is default)
private String cardKeyFile = "GXP211v2.properties";
// the target flag, if true the GSE target is used, card otherwise
private static boolean simulation = true;
// the load flag, if true package is loaded in card or GSE
private boolean loadPackage = true;
// the type of GSE or Card (GXP211v2 is default)
private String FrameworkType = "GXP211v2";
/**
* System authentication informations
*/
// the Security Domain AID used for security
private byte[] securityDomainAID = null;
// the system key File for management
private String keyFile ;
// the system key set version
private int keySetVersion = 13;
// the system key set index (0 is default)
private int keyIndex = 0;
// the security level definition flags
private boolean isEnciphered = false;
private boolean isMacing = false;
/**
* Application authentication informations
*/
// the application key Files
private String appKeyFile ;
// the application key set version
private int appKeySetVersion = 13;
// the application key set index
private int appKeyIndex = 0;
// the package name
private String packageName;
// the fully qualified Applet name
private String appletName;
// the OCF object containing the "load file" AID (package AID)
private AppletID packaid;
// the OCF object containing the "within load file" AID (applet default AID)
private AppletID aid;
/**
* PurseClient default constructor
*/
public PurseClient()
{
}
/**
* OCF layer initialisation
*/
public void initOCF()
{
// start of initialisation
try
{
// start the OCF layer
SmartCard.start();
// fixe the target
String target = simulation ? gseTarget : cardTarget;
// print found terminals
printAvailableTerminals();
// select the target specific CardTerminal
terminal = CardTerminalRegistry.getRegistry().cardTerminalForName(target);
// check if required terminal name was found
if(terminal == null)
{
throw new Exception("terminal not found : " + target);
}
// retreive the CardTerminal type
String type = terminal.getType();
if((type.compareTo("SOCKETJC21SIMULATOR") == 0) && !simulation)
{
throw new Exception("terminal " + target + " is a simulator instance");
}
if(!(type.compareTo("SOCKETJC21SIMULATOR")==0)&& simulation)
{
throw new Exception("terminal " + target + " is not a simulator instance");
}
// create a new card request object
CardRequest cr = new CardRequest();
// set a specific card terminal to the request
cr.setCardTerminal(terminal);
if(simulation)
{
// GE creation and start
Simulator gse = new Simulator();
// GSE starts
gse.start(5000,"GXP211_PK");
// print the GSE build version and date
gse.printVersion();
// personalize with the GSE key set version
keySetVersion = gseKeyVersion;
// build the complete key file for the gse target
keyFile = appKeyFile = keyFileDir + File.separator + gseKeyFile;
}
else
{
// build the complete key file for the card target
keyFile = appKeyFile = keyFileDir + File.separator + cardKeyFile;
}
// wait for card insertion
SmartCard sc = SmartCard.waitForCard(cr);
// get the OP/VOP specific CardService
serv = (CardServiceVOP211)sc.
getCardService(CardServiceVOP211.class, true);
// create the high level API library object
libService = new GemXpressoService();
// set the OP/VOP CardService to the service library for communication
libService.setCardService(serv);
// the service authentication object use for authentication configuration
VOPAuthenticationInput authenticationInput;
// service authentication object creation
authenticationInput = new VOPAuthenticationInput();
// look if key set version is defined
if(keySetVersion > 0)
{
// key set version configuration
authenticationInput.setKeySetVersion(keySetVersion);
// do not use the key set version defined in the target file
authenticationInput.setDefaultKeySetVersion(false);
}
else
{
// no key set version defined
// use the key set version defined in the target file
authenticationInput.setDefaultKeySetVersion(true);
}
// key set version index configuration
authenticationInput.setKeyIndex(keyIndex);
// security configuration
authenticationInput.setEnciphered(isEnciphered);
authenticationInput.setMacing(isMacing);
// define the target specific key file to use
authenticationInput.setKeyfile(keyFile);
// define if the security domain has to be select (yes)
authenticationInput.setSecurityDomainSelection(true);
// define the Security Domain AID
// null force the use of the AID present in the target key file
authenticationInput.setSecurityDomainAID(null);
try
{
// process mutual authentication
// initialize/update and external/authenticate are done
Result result = serv.openSecureChanel(authenticationInput);
// check the result object for authentication status
if((result!=null) && !result.isOK())
{
throw new Exception("authentication error : " + result.getResultMessage());
}
}
catch(Exception ex)
{
// authentication fails
throw new Exception("authentication error : " + ex.getMessage() );
}
// authentication succeed
System.out.println("Authentication OK");
System.out.println("");
}
catch(Exception ex)
{
// exception generated during OCF initialization
System.out.println("Exception caught in initOCF : " + ex.getMessage() );
// terminate client application
System.exit( -1 );
}
}
/**
* PurseClient default constructor
*/
public void setHomeDir(String radHome)
{
homeDir = radHome;
}
/**
* Utility that print the available OCF CardTerminal for the client
*/
private void printAvailableTerminals()
{
// get an enumeration from the registry
java.util.Enumeration terminals = CardTerminalRegistry.getRegistry().
getCardTerminals();
// the CardTerminal we are retreiving information
CardTerminal inFocusTerminal;
// analyse loop
while (terminals.hasMoreElements())
{
inFocusTerminal = (CardTerminal)terminals.nextElement();
System.out.println("Found OCF Card Terminal:");
// print the user defined name
System.out.println("\t- Name = " + inFocusTerminal.getName());
// print the legal type
System.out.println("\t- Type = " + inFocusTerminal.getType());
// print the corresponding adress
System.out.println("\t- Adress = " + inFocusTerminal.getAddress() + "\n");
}
}
/**
* Method containing administrative commands
*/
public void runAdminCommands()
{
try
{
/*
* Administration commands preparating the package.
* IDs are created for Package and Applet(s).
* A downloadable Package formated for the simulator is created,
* containing applet(s) defined by the client.
*/
// create a "load file" (package) application ID
packaid = new AppletID(new byte[]
{
(byte)0xA0,(byte)0x00,(byte)0x00,(byte)0x00,
(byte)0x18,(byte)0xFF,(byte)0x00,(byte)0x00,
(byte)0x00,(byte)0x00,(byte)0x00,(byte)0x00,
(byte)0x00,(byte)0x00,(byte)0x00,(byte)0x01
});
/**
* create a new "load file" (package)
*/
// initialize the package name
packageName = "com.gemplus.examples.purse";
// create a CardService specific object for "LoadFile" type description
OPLoadFile pack = new OPLoadFile(packageName);
// personalize the load file AID
pack.setAID(packaid.getBytes());
// set Application Path
pack.setApplicationPath(sampleDir + File.separator + "out");
// personalize the Security Domain the applet will use for its security
pack.setSecurityDomainAID(null);
// personalize the load parameter (OP specific)
pack.setLoadParameters(null);
// create the "within load file" (applet) application ID
aid = new AppletID(new byte[]
{
(byte)0xA0,(byte)0x00,(byte)0x00,(byte)0x00,
(byte)0x18,(byte)0xFF,(byte)0x00,(byte)0x00,
(byte)0x00,(byte)0x00,(byte)0x00,(byte)0x00,
(byte)0x00,(byte)0x00,(byte)0x00,(byte)0x02
});
/**
* create a new "within load file" (add applet in the package)
*/
// initialize the package name
appletName = "Purse";
// create a CardService specific object for "withinLoadFile" type description
OPApplication applet = new OPApplication(pack,appletName);
// personalize the within load file AID (default AID)
applet.setAID(aid.getBytes());
// personalize the application instance AID (registered applet AID)
applet.setInstanceAID(aid.getBytes());
// define if the applet has to be installed after the load
applet.setIsToInstallFlag(true);
// define if the application is a Security Domain. This information
// will be present in the application privilege flag
applet.setSecurityDomainFlag(false);
// define if the application state will be set to "Selectable" after install
applet.setSelectableFlag(true);
// add the "within load file" (applet) in the "load file" (package)
pack.addApplication(applet);
// looks if the "load file" has to be loaded
if(loadPackage)
{
// load it
loadPackage(pack);
}
/* select the applet for use
* applet 'select' method is called. If selection is successful, 'Install Applet'
* is deselected and commands are sent to the selected applet 'process' method.
* For new administration commands Install Applet must be selected again.
* Its AID can be found in the 'GSEConst' class. Its value is GSEConst.SYSTEM_APPLET_AID.
* See Javacard 2.1 specifications for details.
*/
System.out.println("Select application... ");
SelectResult selectResult = serv.select(aid);
if(!((selectResult!=null)&&(selectResult.isOK())))
throw new Exception("Select application error : " + selectResult.getResultMessage());
System.out.println("Select application " + appletName + " OK");
}
catch(Exception ex)
{
System.out.println("Exception caught in runAdminCommands : " + ex.getMessage() );
// terminate client application
java.lang.System.exit( -1 );
}
}
public void loadPackage(OPLoadFile pack)
throws Exception
{
System.out.println("Starting load process...");
// the result of the load file operation
LoadFileResult loadFileResult = null;
// define the maximum length of data in each "load" APDU
int maxBlockSize = 200;
// the Security Domain OCF AID object
AppletID securityDomainID = null;
if(pack.getSecurityDomainAID()!=null)
{
// create the Security Domain OCF AID object
securityDomainID = new AppletID(pack.getSecurityDomainAID());
}
if(simulation)
// load in the GSE
{
// build the file in the defined path
GemXpressoService.CreateSapfileFromOPLoadFile(pack,sapFileName);
// load the "load file" in the GSE
System.out.println("Load in the GSE... ");
//
loadFileResult = libService.loadFromSapFile(
packaid,
securityDomainID,
pack.getLoadParameters(),
maxBlockSize,
sapFileName
);
}
// end of load in the GSE
else
// load in card
{
// load the JAR file in the card
System.out.println("Load in the card...");
// check valid JAR file
if(!jarFileName.equals(""))
{
loadFileResult = libService.loadFromJarFile(
profileDir + File.separator + FrameworkType + ".gsc",
packaid,
securityDomainID,
pack.getLoadParameters(),
maxBlockSize,
jarFileName
);
}
}
// end of load in the card
// check the load result state
// case of null result
if(loadFileResult == null)
throw new Exception("Load error : null loadFileResult");
// case of wrong result
if(!loadFileResult.isOK())
throw new Exception("Load error : " + loadFileResult.getResultMessage());
// loading succeed
System.out.println("Loading " + packageName + " succeed");
// install the applet with parameters
System.out.println("Install applet... ");
// the result of the load file operation
InstallApplicationResult installApplicationResult;
if(v2) {
installApplicationResult = ((CardServiceVOP211)serv).installApplication(
aid, // application AID
aid, // application instance AID
packaid, // load File AID
null, // application properties byte array
// install parameter (length Min 0, length Max 32)
new byte[] {(byte)0x31,(byte)0x32,(byte)0x33,(byte)0x34},
null, // non volatile code space limit parameters
null, // volatile data space limit parameters
null, // non volatile data space limit parameters
null, // others parameters
null, // install token (none)
true, // make selectable flag
null // DAP (not present)
);
}
else {
installApplicationResult = ((CardServiceVOP200)serv).installApplication(
aid, // application AID
aid, // application instance AID
packaid, // load File AID
null, // application properties byte array
// install parameter (length Min 0, length Max 32)
new byte[] {(byte)0x31,(byte)0x32,(byte)0x33,(byte)0x34},
null, // install token (none)
true, // make selectable flag
null // DAP (not present)
);
}
// check install result
if(!((installApplicationResult!=null)&&(installApplicationResult.isOK())))
throw new Exception("Install error : " + installApplicationResult.getResultMessage());
// install succeed
System.out.println("Installing application " + appletName + " succeed");
}
/**
* Method containing applicative commands exchanging APDU with the applet.
* All APDUs are forwarded by the simulator to the applet 'process' method
* until a new select command is received.
*/
public void runAppliCommands()
{
// the buffer used for APDU sending
byte[] sendBuffer;
//
try
{
// Perform a getBalance
System.out.println( "Performing a getBalance()..." );
cmd = new CommandAPDU(new byte[]
{(byte)0x90, (byte)0x10, (byte)0x00, (byte)0x00, (byte)0x02 } );
resp = serv.sendAPDU(cmd);
displayAPDU(cmd, resp);
// Perform a debit(10)
System.out.println( "Performing a debit(10)..." );
cmd = new CommandAPDU(new byte[]
{(byte)0x90, (byte)0x12, (byte)0x00, (byte)0x00,
(byte)0x02, (byte)0x00, (byte)0x0A ,(byte)0x02} );
resp = serv.sendAPDU(cmd);
displayAPDU(cmd, resp);
// Perform a pin code presentation in order to credit the card
System.out.println( "Present the correct pin code ..." );
cmd = new CommandAPDU(new byte[]
{(byte)0x90, (byte)0x16, (byte)0x00, (byte)0x00, (byte)0x4,
(byte)0x31, (byte)0x32, (byte)0x33, (byte)0x34 } );
resp = serv.sendAPDU(cmd);
displayAPDU(cmd, resp);
// Perform a credit(570)
System.out.println( "Performing a credit(570)..." );
cmd = new CommandAPDU(new byte[]
{(byte)0x90, (byte)0x14, (byte)0x00, (byte)0x00,
(byte)0x02, (byte)0x02, (byte)0x3A, (byte)0x02} );
resp = serv.sendAPDU(cmd);
displayAPDU(cmd, resp);
// Perform a getBalance
System.out.println( "Performing a getBalance()..." );
cmd = new CommandAPDU(new byte[]
{(byte)0x90, (byte)0x10, (byte)0x00, (byte)0x00, (byte)0x02 } );
resp = serv.sendAPDU(cmd);
displayAPDU(cmd, resp);
}
catch(Exception ex)
{
System.out.println("Exception caught in runAppliCommands : " + ex.getMessage() );
ex.printStackTrace();
// terminate client application
java.lang.System.exit( -1 );
}
}
/**
* Free OCF layer used by the client
*/
public void stopOCF()
{
try
{
// free the OCF layer before terminating
SmartCard.shutdown();
}
catch(Exception ex)
{
System.out.println("Exception caught in stopOCF : " + ex.getMessage() );
// terminate client application with an exception
java.lang.System.exit( -1 );
}
}
/**
* Display utility for APDU command
* @param termCmd a CommandAPDU type command
* @param cardResp a ResponseAPDU type response
*/
private void displayAPDUCmd(CommandAPDU termCmd)
{
printer.printAPDUCmd(termCmd.getBuffer());
}
/**
* Display utility for APDU response
* @param termCmd a CommandAPDU type command
* @param cardResp a ResponseAPDU type response
*/
private void displayAPDUResp(ResponseAPDU cardResp)
{
printer.printAPDUResp(cardResp.getBuffer());
}
/**
* Display utility for APDU command & response
* @param termCmd a CommandAPDU type command
* @param cardResp a ResponseAPDU type response
*/
private void displayAPDU(CommandAPDU termCmd, ResponseAPDU cardResp)
{
printer.printAPDU(termCmd.getBuffer(), cardResp.getBuffer());
}
private static void displayHelp() {
System.out.println("Purse client applet - usage :");
System.out.println("Use -help to display this help");
System.out.println("PurseClient -card[gse] -[radhome] -[cardtype]");
System.out.println(" -card[gse] : indicates the Purse client");
System.out.println(" the target.");
System.out.println(" -gse is for simulator (default if not specified).");
System.out.println(" -card is for card.");
System.out.println(" -[radhome] : defines a custom radhome directory");
System.out.println(" default is c:\\gemplus\\gemxpresso.rad3");
System.out.println(" -cardtype : defines the card or gse type");
System.out.println(" default is GXP211v2");
System.out.println(" Available values are :");
System.out.println(" -GXP211v2 : GemXpresso 211 V2 card.");
System.out.println(" -GXP211v2_IS : GemXpresso 211 V2 IS card.");
System.out.println(" -GXP211_PK : GemXpresso 211 PK card.");
System.out.println(" -GXP211_PK_IS : GemXpresso 211 PK IS card.");
System.out.println(" -GXP211v1 : GemXpresso 211 V1 card.");
System.out.println(" -GXP211v1_IS : GemXpresso 211 V1 IS card.");
System.out.println("");
}
/**
* Application main entry point.
* @param argv not used by default application.
*/
public static void main(String[] argv)
{
String radHome = null;
String cardT = null;
// parse arguments
for(int i=0; i\conf directory for OCF initialization (initOCF)
//OCF automatically search the opencard.properties file in the user.dir (current) directory.
System.setProperty("user.dir", System.getProperty("gemplus.gemxpresso.rad.home.conf"));
System.out.println("----- start of PurseClient client application -----");
// initialise OCF layer
client.initOCF();
// run administration commands
client.runAdminCommands();
// run application commands
client.runAppliCommands();
// free OCF layer
client.stopOCF();
System.out.println("----- end of PurseClient client application -----");
// terminate client application normally
java.lang.System.exit(0) ;
}
}