I’m trying to get a simple applet I’ve written to work on a JavaCard — it seems to work perfectly fine on the simulator, but not on the actual card. It uploads and SELECTs successfully, but returns nonsense SWs for most requests.
Here's the applet code (built using Oracle JavaCard 3.0.5 SDK, JDK 11) — just some simple commands to check what works and what does not
public class UnsignedByte {
public static short decode(byte bits) { return (short)(bits & 0xFF); }
public static byte encode(short bits) { return (byte)(bits & 0xFF); }
}
public class MyApplet extends Applet {
private MyApplet() { register(); }
public static void install(byte[] p1, short p2, short p3) throws ISOException { new MyApplet(); }
@Override
public boolean select() { return true; }
@Override
public void process(APDU apdu) throws ISOException {
byte[] buffer = apdu.getBuffer();
final short CLA = UnsignedByte.decode(buffer[ISO7816.OFFSET_CLA]);
final short INS = UnsignedByte.decode(buffer[ISO7816.OFFSET_INS]);
// Ignore SELECT APDU
if (selectingApplet()) {
return;
}
switch(CLA) {
case 0x00:
if (INS == 0x0A) {
apdu.setOutgoingAndSend((short) 0, (short) 0);
} else if (INS == 0x40) {
buffer[0] = UnsignedByte.encode((short) 0x40);
apdu.setOutgoingAndSend((short) 0, (short) 1);
} else {
ISOException.throwIt(ISO7816.SW_INS_NOT_SUPPORTED);
}
break;
case 0x0A:
final short P1 = UnsignedByte.decode(buffer[ISO7816.OFFSET_P1]);
final short P2 = UnsignedByte.decode(buffer[ISO7816.OFFSET_P2]);
if (P1 == 0xAA) {
buffer[0] = UnsignedByte.encode(P2);
apdu.setOutgoingAndSend((short) 0, (short) 1);
} else {
ISOException.throwIt(ISO7816.SW_WRONG_P1P2);
}
break;
case 0xF0:
if (INS != 0x42) {
ISOException.throwIt(ISO7816.SW_INS_NOT_SUPPORTED);
}
buffer[0] = UnsignedByte.encode((short) 0xF0);
buffer[1] = UnsignedByte.encode((short) 0x42);
buffer[2] = UnsignedByte.encode((short) 0x00);
apdu.setOutgoingAndSend((short) 0, (short) 3);
break;
default:
ISOException.throwIt(ISO7816.SW_CLA_NOT_SUPPORTED);
break;
}
}
}
This works as expected on a simulator (JCardSim):
00 0A 00 00
⇒ 90 00
;00 40 00 00
⇒ 40 90 00
;0A 42 AA FF
⇒ FF 90 00
;F0 42 00 00
⇒ F0 42 00 90 00
;However, if I upload it to the card, these are the responses:
00 0A 00 00
⇒ 90 00
(good);00 40 00 00
⇒ 69 85
;0A 42 AA FF
⇒ 6E 00
;F0 42 00 00
⇒ 69 85
;68 81
for some of the commands;The code never throws 68 81
an 69 85
, and it would seem pretty unlikely that the switch fails in some strange way to throw 6E 00
, so, presumably, something happens before the APDU gets to the applet or another applet somehow intercepts it.
The card is an NXP JCOP4 J3R150, Java Card 3.0.5 Classic, Global Platform 2.0, ATR = 3B 6A 00 FF 00 31 C1 73 C8 40 00 00 90 00
; I'm uploading the applet using GlobalPlatformPro; there is one preinstalled applet on the card, AID = D2 76 00 00 85 30 4A 43 4F 90 00
; the card spec says it supports both T=1
and T=0
, but it seems to only connect via T=0
if that matters.
Edit: Have I maybe not initialised the card properly — do I need to do anything with it before uploading an applet? I've also found something about the CLA
bits determining the logical channels used — could this be the problem? — I've used three different values to avoid that, though.
The results you got from the card appear quite reasonable, while the simulator seems to take some shortcuts. The class byte is not a good target for arbitrary values, you should leave these bits at zero:
If you have b8 = 0 and b7 = 1 an entirely different structure applies, so I recommend taking a look into ISO 7816-4. Note, that commands with class FF are likely to be intercepted by the reader, so this is another value to avoid.