/** * 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) ; } }