jCardSim provides an API for rapid prototyping of Java Card applications and unit testing. There are three different ways it can be used:
- the
CardSimulator
class - the
CardTerminalSimulator
class (for applications that usejavax.smartcardio
) - the
JavaCardRemoteInterface
interface
Using the CardSimulator
class
// 1. Create simulator
CardSimulator simulator = new CardSimulator();
// 2. Install applet
AID appletAID = AIDUtil.create("F000000001");
simulator.installApplet(appletAID, HelloWorldApplet.class);
// 3. Select applet
simulator.selectApplet(appletAID);
// 4. Send APDU
CommandAPDU commandAPDU = new CommandAPDU(0x00, 0x01, 0x00, 0x00);
ResponseAPDU response = simulator.transmitCommand(commandAPDU);
// 5. Check response status word
assertEquals(0x9000, response.getSW());
The example below uses HelloWorldApplet
, which responds to command APDUs as follows:
CLA=0x00 INS=0x02 P1=0x00 P2=0x00 LC=0x00
: do nothingCLA=0x00 INS=0x01 P1=0x00 P2=0x00 LC=0x00
: return the bytes in "Hello world !"CLA=0x00 INS=0x01 P1=0x01 P2=0x00 LC=<length> DATA=<data>
: return the command data (echo)CardSimulator simulator = new CardSimulator();
byte[] appletAIDBytes = new byte[]{1, 2, 3, 4, 5, 6, 7, 8, 9}; AID appletAID = new AID(appletAIDBytes, (short) 0, (byte) appletAIDBytes.length);
simulator.installApplet(appletAID, HelloWorldApplet.class); simulator.selectApplet(appletAID);
// test NOP ResponseAPDU response = simulator.transmitCommand(new CommandAPDU(0x00, 0x02, 0x00, 0x00)); assertEquals(0x9000, response.getSW());
// test hello world from card response = simulator.transmitCommand(new CommandAPDU(0x00, 0x01, 0x00, 0x00)); assertEquals(0x9000, response.getSW()); assertEquals("Hello world !", new String(response.getData()));
// test echo CommandAPDU echo = new CommandAPDU(0x00, 0x01, 0x01, 0x00, ("Hello javacard world !").getBytes()); response = simulator.transmitCommand(echo); assertEquals(0x9000, response.getSW()); assertEquals("Hello javacard world !", new String(response.getData()));
It is also possible to work with plain byte
arrays, for example:
// test NOP
byte[] response = simulator.transmitCommand(new byte[]{0,2,0,0});
ByteUtil.requireSW(response, 0x9000);
To simplify the handling of AIDs and byte arrays we provide AIDUtil
and ByteUtil
:
// AID from byte array
AID applet1AID = AIDUtil.create(new byte[]{1, 2, 3, 4, 5, 6, 7, 8, 9});
// AID form String
AID applet2AID = AIDUtil.create("010203040506070809");
assertEquals(applet1AID, applet2AID);
// String to byte array
String hexString = ByteUtil.hexString(new byte[]{0,2,0,0});
// byte array from String
byte[] bytes = ByteUtil.byteArray("00 02 00 00");
assertEquals("00020000", hexString);
assertEquals("00020000", ByteUtil.hexString(bytes));
Using the CardTerminalSimulator
class
To simplify unit testing of applications that use javax.smartcardio
, we provide a CardTerminalSimulator
class that simulates javax.smartcardio.TerminalFactory
.
// 1. Create simulator and install applet
CardSimulator simulator = new CardSimulator();
AID appletAID = AIDUtil.create("F000000001");
simulator.installApplet(appletAID, HelloWorldApplet.class);
// 2. Create Terminal
CardTerminal terminal = CardTerminalSimulator.terminal(simulator);
// 3. Connect to Card
Card card = terminal.connect("T=1");
CardChannel channel = card.getBasicChannel();
// 4. Select applet
CommandAPDU selectCommand = new CommandAPDU(AIDUtil.select(appletAID));
channel.transmit(selectCommand);
// 5. Send APDU
CommandAPDU commandAPDU = new CommandAPDU(0x00, 0x01, 0x00, 0x00);
ResponseAPDU response = simulator.transmitCommand(commandAPDU);
// 6. Check response status word
assertEquals(0x9000, response.getSW());
Note: Pre-installed applets can be configured using system properties: System.setProperty(...)
.
Properties follow the same format used in the CLI configuration file.
It is also possible to simulate multiple terminals using javax.smartcardio.CardTerminals
.
In this case each javax.smartcardio.CardTerminal
starts in an empty state (isCardPresent()
returns false
).
Cards can be inserted via CardSimulator#assignToTerminal(terminal)
and removed via
CardSimulator#assignToTerminal(null)
.
// Obtain CardTerminal
CardTerminals cardTerminals = CardTerminalSimulator.terminals("My terminal 1", "My terminal 2");
CardTerminal terminal1 = cardTerminals.getTerminal("My terminal 1");
CardTerminal terminal2 = cardTerminals.getTerminal("My terminal 2");
assertEquals(false, terminal1.isCardPresent());
assertEquals(false, terminal2.isCardPresent());
// Create simulator and install applet
CardSimulator simulator = new CardSimulator();
AID appletAID = AIDUtil.create("F000000001");
simulator.installApplet(appletAID, HelloWorldApplet.class);
// Insert Card into "My terminal 1"
simulator.assignToTerminal(terminal1);
assertEquals(true, terminal1.isCardPresent());
assertEquals(false, terminal2.isCardPresent());
Creating a terminal via javax.smartcardio.TerminalFactory
:
// Register security provider
if (Security.getProvider("CardTerminalSimulator") == null) {
Security.addProvider(new CardTerminalSimulator.SecurityProvider());
}
// Get TerminalFactory
Object params = null;
TerminalFactory factory = TerminalFactory.getInstance("CardTerminalSimulator", params);
// Get CardTerminal
CardTerminals cardTerminals = factory.terminals();
CardTerminal terminal = cardTerminals.getTerminal("jCardSim.Terminal");
assertNotNull(terminal);
// Insert Card
simulator.assignToTerminal(terminal);
Creating multiple terminals via javax.smartcardio.TerminalFactory
:
String[] names = new String[] {"My terminal 1", "My terminal 2"};
TerminalFactory factory = TerminalFactory.getInstance("CardTerminalSimulator", names);
CardTerminals cardTerminals = factory.terminals();
assertNotNull(cardTerminals.getTerminal("My terminal 1"));
assertNotNull(cardTerminals.getTerminal("My terminal 2"));
Current version's limitations
- The
javax.smartcardio.Card#openLogicalChannel
method is not supported. - The
javax.smartcardio.Card#transmitControlCommand
method is not supported.
Legacy TerminalFactory
Previous versions of jCardSim provided a limited TerminalFactory
implementation (JCSTerminal
). An example is provided in JCardSimProviderTest.java.
Using the JavaCardRemoteInterface
interface
It is possible to interact with a remote instance of jCardSim. For example you may run one or multiple instances of virtual Java Card and connect to it via TCP/IP.
An example is provided in JavaCardRemoteServerTest.java.