I have developed successful solutions for reading DESFire smart cards used in Croatian public services, both for:
myDesFire.getUID()
instead of raw APDU commands.Right now, I'm trying to develop multi platform solution in Java, using TapLinx SDK for Java and high-level approach.
I rely on javax.smartcardio
for polling connected card terminal to see when smart card is inserted or removed, and it works perfectly. In my testing app (JavaFX if that matters), in onCardInserted
event, I'm calling my own cardLogic
method. That method tries to connect to smart card.
In Android, it is very simple:
public static Card cardLogic(final Intent intent, NxpNfcLib nxpNfcLib, MyCardKeys keys) {
//... some code ommited for brevity
desFireEV = DESFireFactory.getInstance().getDESFireEV2(nxpNfcLib.getCustomModules());
desFireEV.getReader().connect();
byte[] uid = desFireEV.getUID();
// ...and so on.
In Java, similar method of mine is:
public static Card cardLogic(CardTerminal terminal, MyCardKeys keys) {
//... some code ommited for brevity
javax.smartcardio.Card javaCard = terminal.connect("*");
desFire = DESFireFactory.getInstance().getDESFireEV2(TapLinx.getCustomModules());
// **NOW WHAT!???**
desFire.getReader().connect(); // **This does not work, of course. HOW TO connect context of javaCard object with getting a concrete reader, so that...**
byte[] uid = desFire.getUID(); // **... I can do this without raising an exception?**
So, questions are those in comments of the above code snippets. I'm totally stucked. Probably missing something obvious. but I can not figure out the solution, not even when reading code of both sample desktop apps.
Please help.
I also tried raw APDU approach without TapLinx SDK, using just javax.smartcardio
, but since smartcardio
was actually developed to support contact cards, one can not make more complex operations targeting contactless cards. So, I can read DESFire's UID, select identity app, read app IDs, but I can not read BER TLV files, for example, even if not encrypted.
I've found the solution. So, in case someone encounters similar problem
code goes like this. BTW, the key is in the setTransceive
method!?!?!!!
And, it seems that I initially put this answer to the similar thread by mistake.
MyApp.TapLinx.getCustomModules().setTransceive(new MyCardApduHandler(new MyCardReader(terminal)));
desFire = DESFireFactory.getInstance().getDESFireEV2(MyApp.TapLinx.getCustomModules());
desFire.getReader().connect();
// Read UID.
byte[] uid = desFire.getUID();
// To do anything further, and unlike Android , you have to set Command Set to ISO.
desFire.setCommandSet(IDESFireEV1.CommandSet.ISO);
// Select ID app...
desFire.selectApplication(0);
// ...and so on
MyCardApduHandler
is barebone:
public class MyCardApduHandler implements IApduHandler {
IReader reader;
public SCardApduHandler(IReader reader) {
this.reader = reader;
}
@Override
public byte[] apduExchange(byte[] bytes) {
return reader.transceive(bytes);
}
@Override
public IReader getReader() {
return reader;
}
}
MyCardReader
is as follows:
public class MyCardReader implements IReader {
CardTerminal mTerminal;
CardChannel mKanal;
Card mJavaCard;
ProtocolDetails mProtokol;
boolean isConnected = false;
public SCardReader(CardTerminal terminal) {
mTerminal = terminal;
}
@Override
public byte[] transceive(byte[] bytes) {
ResponseAPDU res;
try {
res = mKanal.transmit(new CommandAPDU(bytes));
} catch (CardException e) {
throw new NxpNfcLibException(e, e.getMessage());
}
return res.getBytes();
}
@Override
public void connect() {
if (!isConnected) {
try {
mTerminal.waitForCardPresent(0);
mJavaCard = mTerminal.connect("*");
mKanal = mJavaCard.getBasicChannel();
mProtokol = new ProtocolDetails();
mProtokol.uid = Commands.uid(mKanal);
// TODO: Other components of the protocol.
isConnected = true;
} catch (CardException e) {
throw new NxpNfcLibException(e, e.getMessage());
}
}
}
@Override
public void close() {
if (isConnected) {
try {
if (mKanal.getChannelNumber() != 0) mKanal.close();
mJavaCard.disconnect(false);
isConnected = false;
} catch (Exception e) {
throw new NxpNfcLibException(e, e.getMessage());
}
}
}
@Override
public boolean isConnected() {
return isConnected;
}
@Override
public void setTimeout(long l) {
throw new NotSupportedException("SCardReader: metoda setTimeout nije podržana. ");
}
@Override
public long getTimeout() {
throw new NotSupportedException("SCardReader: metoda getTimeout nije podržana. ");
}
@Override
public ProtocolDetails getProtocolDetails() {
return mProtokol;
}
}
And, Commands.uid(mKanal)
is achieved with raw APDU:
public static byte[] uid(CardChannel kanal) throws CardException, RuntimeException {
CommandAPDU cmd = new CommandAPDU(new byte[] { (byte) 0xFF, (byte) 0xCA, (byte) 0x00, (byte) 0x00, (byte) 0x00 });
ResponseAPDU res = kanal.transmit(cmd);
if (res.getSW1() != 0x90 && res.getSW2() != 0x00) throw new RuntimeException(String.format("uid: greška SW1 SW2 = %02X %02X", res.getSW1(), res.getSW2()));
return res.getData();
}