/** * Copyright or © or Copr. SSD Research Team 2011 * (XLIM Labs, University of Limoges, France). * * Contact: ssd@xlim.fr * * This software is a Java 6 library that implements Global Platform 2.x card * specification. OPAL is able to upload and manage Java Card applet lifecycle * on Java Card while dealing of the authentication of the user and encryption * of the communication between the user and the card. OPAL is also able * to manage different implementations of the specification via a pluggable * interface. * * This software is governed by the CeCILL-C license under French law and * abiding by the rules of distribution of free software. You can use, * modify and/ or redistribute the software under the terms of the CeCILL-C * license as circulated by CEA, CNRS and INRIA at the following URL * "http://www.cecill.info". * * As a counterpart to the access to the source code and rights to copy, * modify and redistribute granted by the license, users are provided only * with a limited warranty and the software's author, the holder of the * economic rights, and the successive licensors have only limited * liability. * * In this respect, the user's attention is drawn to the risks associated * with loading, using, modifying and/or developing or reproducing the * software by the user in light of its specific status of free software, * that may mean that it is complicated to manipulate, and that also * therefore means that it is reserved for developers and experienced * professionals having in-depth computer knowledge. Users are therefore * encouraged to load and test the software's suitability as regards their * requirements in conditions enabling the security of their systems and/or * data to be ensured and, more generally, to use and operate it in the * same conditions as regards security. * * The fact that you are presently reading this means that you have had * knowledge of the CeCILL-C license and that you accept its terms. */ import fr.xlim.ssd.opal.library.CardConfigFactory; import fr.xlim.ssd.opal.library.applet.SecurityDomain; import fr.xlim.ssd.opal.library.commands.SecLevel; import fr.xlim.ssd.opal.library.config.CardConfig; import fr.xlim.ssd.opal.library.utilities.CapConverter; import fr.xlim.ssd.opal.library.utilities.Conversion; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import javax.smartcardio.*; import javax.smartcardio.CardTerminals.State; import java.io.*; import java.util.List; import java.util.jar.JarInputStream; import java.util.zip.ZipEntry; /** * A Simple sample to use OPAL library with the hello world applet * * @author Guillaume Bouffard */ public class Simple_OPAL_Main { /// the logger private final static Logger logger = LoggerFactory.getLogger(Simple_OPAL_Main.class); /// timeout to detect card presence private final static int TIMEOUT_CARD_PRESENT = 1000; /// applet ID of Hello World Applet private final static byte[] APPLET_ID = { (byte) 0x48, (byte) 0x65, (byte) 0x6c, (byte) 0x6c, (byte) 0x6f, (byte) 0x57, (byte) 0x6f, (byte) 0x72, (byte) 0x6c, (byte) 0x64, (byte) 0x41, (byte) 0x70, (byte) 0x70 }; /// package ID of Hello World Applet private final static byte[] PACKAGE_ID = { (byte) 0x48, (byte) 0x65, (byte) 0x6c, (byte) 0x6c, (byte) 0x6f, (byte) 0x57, (byte) 0x6f, (byte) 0x72, (byte) 0x6c, (byte) 0x64 }; /// channel to the card private static CardChannel channel; /// Security Level private static SecLevel secLevel = SecLevel.NO_SECURITY_LEVEL; public static void main(String[] args) throws CardException, ClassNotFoundException, IOException { uninstallApplet(PACKAGE_ID, APPLET_ID); installApplet(PACKAGE_ID, APPLET_ID, APPLET_ID, "#TODO:YOUR_HELLO_WORLD_FILE"); selectApplet(APPLET_ID); sendAPDU(new CommandAPDU( (byte) 0x00, // CLA (byte) 0x00, // INS (byte) 0x00, // P1 (byte) 0x00, // P2 new byte[] { (byte) 'H', (byte) 'E', (byte) 'L', (byte) 'L', (byte) 'O', (byte) ' ', (byte) 'Y', (byte) 'O',(byte) 'U', (byte) '!', }, // Data (byte) 0x0 // Le )); } /** * Uninstall an applet * * @param packageAID if non null, delete the package associated without cascade mode * @param appletAID if non null, delete the applet associated without cascade mode * @throws ClassNotFoundException * @throws CardException */ public static void uninstallApplet (byte[] packageAID, byte[] appletAID) throws ClassNotFoundException, CardException { channel = null; /// get the card config and card channel, detection of t=0 or t=1 is automatic CardConfig cardConfig = getCardChannel(1, "*"); if (channel == null) { logger.error("Cannot access to the card"); System.exit(-1); } // select the security domain logger.info("Selecting Security Domain"); cardConfig.getImplementation().setCardChannel(channel); SecurityDomain securityDomain = new SecurityDomain(cardConfig.getImplementation(), cardConfig.getIsd()); securityDomain.setOffCardKeys(cardConfig.getSCKeys()); try { securityDomain.select(); } catch (Exception ex) { logger.error("Unable to select applet: ", ex); } // initialize update logger.info("Initialize Update"); securityDomain.initializeUpdate(cardConfig.getDefaultInitUpdateP1(), cardConfig.getDefaultInitUpdateP2(), cardConfig.getScpMode()); // external authenticate logger.info("External Authenticate"); securityDomain.externalAuthenticate(secLevel); try { // Deleting Applet if (appletAID != null) { logger.info("Deleting applet"); securityDomain.deleteOnCardObj(appletAID, true); } } catch (CardException ce) { // To do nothing } try { // Deleting package if existed if (appletAID != null) { logger.info("Deleting package"); securityDomain.deleteOnCardObj(packageAID, true); } } catch (CardException ce) { // To do nothing } } /** * Method to install an applet * * @param packageAID Applet Package AID * @param appletAID Applet AID * @param instanceAID Applet Instance AID * @param capFile Path to the CAP file * @throws ClassNotFoundException * @throws CardException * @throws IOException */ public static void installApplet(byte[] packageAID, byte[] appletAID, byte[] instanceAID, String capFile) throws ClassNotFoundException, CardException, IOException { channel = null; /// get the card config and card channel, detection of t=0 or t=1 is automatic CardConfig cardConfig = getCardChannel(1, "*"); if (channel == null) { logger.error("Cannot access to the card"); System.exit(-1); } // select the security domain logger.info("Selecting Security Domain"); cardConfig.getImplementation().setCardChannel(channel); SecurityDomain securityDomain = new SecurityDomain(cardConfig.getImplementation(), cardConfig.getIsd()); securityDomain.setOffCardKeys(cardConfig.getSCKeys()); try { securityDomain.select(); } catch (Exception ex) { logger.error("Unable to select applet: ",ex); } // initialize update logger.info("Initialize Update"); securityDomain.initializeUpdate(cardConfig.getDefaultInitUpdateP1(), cardConfig.getDefaultInitUpdateP2(), cardConfig.getScpMode()); // external authenticate logger.info("External Authenticate"); securityDomain.externalAuthenticate(secLevel); // install Applet logger.info("Installing Applet"); logger.info("* Install For Load"); securityDomain.installForLoad(packageAID, null, null); File file = new File(capFile); InputStream is = new FileInputStream(file); byte[] buffer = new byte[(int) file.length()]; is.read(buffer); ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(buffer); // Check if the cap is a jar file or not // we check if the file is a jar file by trying to access to a new entry ZipEntry zipEntry = new JarInputStream(byteArrayInputStream).getNextEntry(); if (zipEntry != null) { buffer = CapConverter.convert(new ByteArrayInputStream(buffer)); } logger.info("* Loading file"); securityDomain.load(buffer, (byte) 0x10); logger.info("* Install for install"); securityDomain.installForInstallAndMakeSelectable( packageAID, appletAID, instanceAID, Conversion.hexToArray("00"), null); } /** * Select an applet * * @param appletAID applet AID to select * @throws ClassNotFoundException * @throws CardException */ public static void selectApplet (byte[] appletAID) throws ClassNotFoundException, CardException { channel = null; /// get the card config and card channel, detection of t=0 or t=1 is automatic CardConfig cardConfig = getCardChannel(1, "*"); if (channel == null) { logger.error("Cannot access to the card"); System.exit(-1); } cardConfig.getImplementation().setCardChannel(channel); SecurityDomain securityDomain = new SecurityDomain(cardConfig.getImplementation(), cardConfig.getIsd()); // Selecting Applet CommandAPDU select = new CommandAPDU((byte) 0x00 // CLA , (byte) 0xA4 // INS , (byte) 0x04 // P1 , (byte) 0x00 // P2 , appletAID // DATA ); logger.info("Selecting Applet (" + Conversion.arrayToHex(appletAID) + ")"); ResponseAPDU resp = securityDomain.send(select); logger.debug("Selecting Applet " + "(-> " + Conversion.arrayToHex(select.getBytes()) + ") " + "(<- " + Conversion.arrayToHex(resp.getBytes()) + ")"); } /** * Send an APDU command * @param cmd APDU command to send * @throws ClassNotFoundException * @throws CardException */ public static void sendAPDU (CommandAPDU cmd) throws ClassNotFoundException, CardException { channel = null; /// get the card config and card channel, detection of t=0 or t=1 is automatic CardConfig cardConfig = getCardChannel(1, "*"); if (channel == null) { logger.error("Cannot access to the card"); System.exit(-1); } cardConfig.getImplementation().setCardChannel(channel); SecurityDomain securityDomain = new SecurityDomain(cardConfig.getImplementation(), cardConfig.getIsd()); // Using Applet ResponseAPDU resp = securityDomain.send(cmd); logger.debug("Command sent" + "(-> " + Conversion.arrayToHex(cmd.getBytes()) + ") " + "(<- " + Conversion.arrayToHex(resp.getBytes()) + ")"); } private static CardConfig getCardChannel(int cardTerminalIndex, String transmissionProtocol) { TerminalFactory factory = TerminalFactory.getDefault(); List terminals; try { terminals = factory.terminals().list(State.ALL); } catch (CardException ex) { logger.error("Cannot get list of terminals", ex); return null; } if (terminals.isEmpty()) { logger.error("No card terminal found"); return null; } else { for (int i = 0; i < terminals.size(); i++) { logger.info("Card terminal found: " + i + " - " + terminals.get(i)); } } if (terminals.size() == 1) { cardTerminalIndex = 0; } else if (terminals.size() < cardTerminalIndex) { logger.error("Card terminal index not available: " + cardTerminalIndex); return null; } logger.info("Card terminal selected: " + terminals.get(cardTerminalIndex)); CardTerminal terminal = terminals.get(cardTerminalIndex); logger.info("Wait for card (during " + TIMEOUT_CARD_PRESENT + "ms)"); boolean cardFound; try { cardFound = terminal.waitForCardPresent(TIMEOUT_CARD_PRESENT); } catch (CardException ex) { logger.error("Cannot detect card presence", ex); return null; } if (!cardFound) { logger.error("Card not found"); return null; } logger.info("Connect to card with transmission protocol " + transmissionProtocol); Card card; try { card = terminal.connect(transmissionProtocol); } catch (CardException ex) { logger.error("Cannot connect to card", ex); return null; } logger.info("Card description: " + card); channel = card.getBasicChannel(); ATR atr = card.getATR(); logger.info("Card ATR: " + Conversion.arrayToHex(atr.getBytes())); CardConfigFactory ccFactory = new CardConfigFactory(); return ccFactory.getCardConfigByATR(atr.getBytes()); } }