Using TapLinx SDK for Java, NOT Android

cancel
Showing results for 
Show  only  | Search instead for 
Did you mean: 

Using TapLinx SDK for Java, NOT Android

Jump to solution
726 Views
IkarovoPero
Contributor II

Greetings!

I have developed successful solutions for reading DESFire smart cards used in Croatian public services, both for:

  1. Dot Net. Multi platform, using pcsc-sharp and "raw" APDU approach.
  2. Android. Using TapLinx SDK for Android and high level-approach. For example: myDesFire.getUID() instead of raw APDU commands.

Right now, I'm trying to develop a multi platform solution in Java, using TapLinx SDK for Java and high-level approach.

I rely on javax.smartcardio for polling connected card terminals to detect when smart card is inserted or removed, and it works perfectly. In my testing app (JavaFX if that matters), in onCardInserted event, I call 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());

// WHAT NOW, I WONDER ???

desFire.getReader().connect(); // This does not work, of course. HOW TO associate the context of the javaCard object with getting a concrete reader, so that...

byte[] uid = desFire.getUID(); //... I can do this, and more, 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 apps.

I also tried raw APDU approach in Java, without TapLinx SDK at all, 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.

Please help.

Labels (1)
1 Solution
705 Views
IkarovoPero
Contributor II

I've found the solution. So, in case someone encounters similar problem
code goes like this. BTW, the key is in the setTransceive method!?!?!!!


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 MyCardApduHandler(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 MyCardReader(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();
}

Too bad that most of that is not anywhere near the sample apps...

View solution in original post

1 Reply
706 Views
IkarovoPero
Contributor II

I've found the solution. So, in case someone encounters similar problem
code goes like this. BTW, the key is in the setTransceive method!?!?!!!


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 MyCardApduHandler(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 MyCardReader(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();
}

Too bad that most of that is not anywhere near the sample apps...