I am developing an application where i have to read information from ITSO smart card. And it supports below three technologies for filter ACTION_TECH_DISCOVERED.
android.nfc.tech.IsoDep, android.nfc.tech.NfcA
android.nfc.tech.NdefFormatable
Now when I using below code
byte[] apduCmd = {
0x00, //CLA
(byte)0xA4, //INS select
0x00, //P1
0x00, //P2
0x02 //LE
};
Tag tag = intent.getParcelableExtra(NfcAdapter.EXTRA_TAG);
IsoDep isoDep = IsoDep.get(tag);
try {
isoDep.connect();
byte[] result = isoDep.transceive(apduCmd);
Log.i(TAG, "SELECT: " + bytesToHex(result));
if (!(result[0] == (byte) 0x90 && result[1] == (byte) 0x00)) {
Toast.makeText(this, "Exception occurred!", Toast.LENGTH_SHORT).show();
}else{
Toast.makeText(this, "Read successfully!", Toast.LENGTH_SHORT).show();
}
} catch (IOException e) {
e.printStackTrace();
}
Above code gives me success bytes i.e. 9000
Now I am not sure what to do next? How to read complete Data/information from this ITSO card. (this card is type of bus ticket).
Any suggestion?
Thanks Michael, i have read the document ITSO specification, and got idea to create command to read the data from the card.
I execute the commands in below sequence.
1) SELECT DIRECTORY- 906A000000
2) SELECT APPLICATION- 905A0000031602A000
3) READ DATA- 90BD0000070400000000000000 (File number- 4)
4) ADDITIONAL FRAME- 90AF000000 (This will execute only if we have Additional frame in response.)
And i am getting response like below for each file ( below response if for file number 4)
Response: 230A00009469FA0798004600002698400100000000000000000000000000000000042C00C0001CDC131089EB4A583BC000000000000000000000000000000000
Now I am not sure how get the information from this?
Can i get all information at once in XML or JSON? Is there any easier way to do this?
Update 10/05/2016
I got the shell information in HEX String, Can you help me to decode the textual information from it. (Reference ITSO specification given above.
Related
I am very new to Android development and NFC in general.
I am trying to build an application to read the content of an NFC card where I don't know anything about this card (bus card), I want to see for example how many tickets I have left.
I have scanned the card with variety of NFC applications and know that this card is of type: IsoDep AND NfcB.
Right now I am trying to read its content using IsoDep with no success (errors 6A82, 91AE, 6E00 and so on).
I have an app that waiting for new intent of type ACTION_NDEF_DISCOVERED || ACTION_TECH_DISCOVERED || ACTION_TAG_DISCOVERED opens a new thread (since it is not possible to read and connect on UI's thread) and I am trying to read the content of the card.
I guess my problem is with the bytes I am passing to isoDep.transceive(NATIVE_SELECT_APP_COMMAND).
Should I keep trying on the IsoDep or should I move to try on the NfcB?
Do you guys have any tips?
here is my code sample:
final byte[] SELECT = {
(byte) 0x00, // CLA Class
(byte) 0xA4, // INS Instruction
(byte) 0x04, // P1 Parameter 1
(byte) 0x00, // P2 Parameter 2
(byte) 0x08, // Length
(byte) 0x31, (byte)0x54, (byte)0x49, (byte)0x43, (byte)0x2e,
(byte) 0x49, (byte)0x43, (byte)0x41, // AID 315449432e494341
};
Tag tagFromIntent = m_intent.getParcelableExtra(NfcAdapter.EXTRA_TAG);
IsoDep isoDep = IsoDep.get(tagFromIntent);
try {
isoDep.connect();
byte[] result = isoDep.transceive(SELECT);
String str = bytesToHex(result);
Log.i("test", "SELECT: " + str);
isoDep.close();
} catch (Exception e) {
String error = e.getMessage();
}
my bytes to hex function:
final protected static char[] hexArray = "0123456789ABCDEF".toCharArray();
public static String bytesToHex(byte[] bytes) {
char[] hexChars = new char[bytes.length * 2];
for ( int j = 0; j < bytes.length; j++ ) {
int v = bytes[j] & 0xFF;
hexChars[j * 2] = hexArray[v >>> 4];
hexChars[j * 2 + 1] = hexArray[v & 0x0F];
}
return new String(hexChars);
}
=============================================================
Edit:
I have just learned that this card is using Calypso authentication scheme.
In this question's answer helped me a little: Read data from NFC tag (IsoDep)
I found a good SELECT function, i have edited my question to hold the new "SELECT" command which is working- In return i get this string: "6F228408315449432E494341A516BF0C13C70800000000029780A55307060A07062004019000" which i have no idea what it means.
Then I use this command to try and read a string:
byte[] GET_STRING = {
(byte) 0x80, // CLA Class
0x04, // INS Instruction
0x00, // P1 Parameter 1
0x00, // P2 Parameter 2
0x10 // LE maximal number of bytes expected in result
};
But i get error: 6E00, any ideas on how to proceed?
You could communicate with card with IsoDep.
You want to access data on card without specifications of this card, so there's 2 ways:
Get specification of card (how to communicate with it)
Do reverse engineering, it will take lot of time and not sure about the result, and you could "lock" access to the card
UPDATE 1
To read Rav Kav card, threre's an open-source project : http://pannetrat.com/Cardpeek/ code for Rav Kav is here https://code.google.com/p/cardpeek/source/browse/trunk/dot_cardpeek_dir/scripts/calypso/c376n3.lua
You are sending the wrong command to read the application's records. 6E00 means incorrect INS byte.
You can have a look at this github which reads some counters out of a MOBIB card, which is based on the Calypso spec.
https://github.com/flamingokweker/Android-NFC-Mobib-Reader
I'm writing an Android application. I'm trying to send NfcA low-level commands (in my case: HALT and WAKE-UP) to my Mifare Plus S card. The NfcA tech is for "low-level" access to ISO 14443 Type A tags (i.e. the
proprietary protocol as mentioned in ISO 14443-3).
This is part of my code:
protected void onNewIntent(Intent intent) {
if (NfcAdapter.ACTION_TAG_DISCOVERED.equals(intent.getAction())) {
tagFromIntent = intent.getParcelableExtra(NfcAdapter.EXTRA_TAG);
UID = Utils.byteArrayToHexString(intent.getByteArrayExtra(NfcAdapter.EXTRA_ID));
}
NfcA nfca = null;
try {
Log.e(TAG, "WakeUpCMD and HaltCMD");
nfca = NfcA.get(tagFromIntent);
nfca.connect();
Short s = nfca.getSak();
byte[] a = nfca.getAtqa();
byte[] HaltCMD = {0x35, 0x30, 0x30,0x30, 0x00};
byte[] WakeUpCMD = {0x35, 0x32, 0x00};
byte[] data = null;
try {
data = nfca.transceive(HaltCMD);
length = data.length;
}
catch (Exception e){
Log.e(TAG, "HALT complete "+Utils.byteArrayToHexString(data));
}
Log.e(TAG, "Tag is connected: "+nfca.isConnected());
//Log.e(TAG, "Response from tag "+Utils.byteArrayToHexString(data));
nfca.setTimeout(100);
data = nfca.transceive(WakeUpCMD);
Log.e(TAG, "Response from tag"+Utils.byteArrayToHexString(data));
nfca.close();
} catch (Exception e) {
Log.e(TAG, "Error when reading tag");
}
}
WAKE-UP Command is sent by the PCD to put PICCs which have entered the HALT State back into the READY
State. They shall then participate in further anticollision and selection procedures.
|b7| |b6| |b5| |b4| |b3| |b2| |b1|
|1 | | 0 | | 1| | 0| | 0| | 1| | 0| {‘52’} = WAKE-UP
HALT Command consists of four bytes and shall be transmitted using the Standard Frame.
First bit transmitted
S | ‘50’ | ‘00’ | CRC_A | E
If the PICC responds with any modulation during a period of 1 ms after the end of the HALT Frame, this response shall be interpreted as 'not acknowledge'.
This is the descriptions of the two commands from ISO 14443-3 which I try to send to my card.
When I start my app, I get a "Tag Lost" Exception. Can you help me? What's wrong? How can I send these commands?
It seems as if you are converting the command codes into null-terminated ASCII character strings before sending them with NfcA.transceive():
byte[] HaltCMD = {0x35, 0x30, 0x30,0x30, 0x00};
byte[] WakeUpCMD = {0x35, 0x32, 0x00};
0x35 0x30 0x30 0x30 gives "5000"
0x35 0x32 gives "52"
This does not make any sense as the commands (50 00 for HLTA, and 52 for WUPA) are hexadecimal representations of the command values already.
For the HLTA command, you would therefore need to send 50 00:
data = nfca.transceive(new byte[] { (byte)0x50, (byte)0x00 });
Note that S (start of communication), E (end of communication), and CRC_A will be automatically added by the NFC controller (or the NFC stack).
For the WUPA command, you could probably try to send 52:
data = nfca.transceive(new byte[] { (byte)0x52 });
However, it is very likely that the NFC stack does not permit you to send 7-bit commands using the transceive method. Instead, the stack may automatically use this command value as one full byte and add a CRC_A.
General note on sending ISO/IEC 14443-3 initialization and anti-collision commands
Sending such commands may or may not work (depending on the NFC stack implementation and the NFC controller). In general I would strongly recommend that you do not send such commands. Particularly the HLTA command will confuse the internal state keeping of the NFC stack on some devices and will lead to unexpected results. Normally, you don't need to exchange such commands, as anti-collision, initialization and activation are handled automatically by the NFC device.
I am trying to implement IsoDep reading capabilities to chariotsolutions phongap-nfc plugin, so far (even though i am a complete newbie to Java/android and cordova/phonegap) I have managed to make it work with cordova, connect to the card, issue an APDU command and return it back to the web view. (hooray for me right?)
Well the simple problem is that I did not manage to get a 9000 response so far by selecting the application.
I am using LG L Fino#4.4.2.
Additions to NFCplugin.java + import nfc.tech.IsoDep, NFCa, NFCb
.... else if (action.equalsIgnoreCase(ISODEP)) {
registerIsoDep(callbackContext);
} else if (action.equalsIgnoreCase(READISODEP)){
readIsoDep(callbackContext);
} else .....
This should be the tag listener - i think so :-)
private void registerIsoDep(CallbackContext callbackContext) throws JSONException {
addTechList(new String[]{IsoDep.class.getName()});
callbackContext.success();
}
This reads the IsoDep tag, found out that it has to be called within the callback of the previous function (will get back in to that in the Js part)
private void readIsoDep(final CallbackContext callbackContext) throws JSONException {
final Tag tag = savedIntent.getParcelableExtra(NfcAdapter.EXTRA_TAG);
cordova.getThreadPool().execute(new Runnable() {
#Override
public void run() {
IsoDep isoDep = IsoDep.get(tag);
try {
isoDep.connect();
isoDep.setTimeout(5000);
byte[] SELECT = {
(byte) 0x00, // CLA Class
(byte) 0xA4, // INS Instruction
(byte) 0x04, // P1 Parameter 1
(byte) 0x00, // P2 Parameter 2
(byte) 0x07, // Length
(byte) 0xA0, (byte) 0x00,
(byte) 0x00, (byte) 0x00,
(byte) 0x04, (byte) 0x10,
(byte) 0x10, // AID
(byte) 0x00 // Lenght
};
try {
byte[] result = isoDep.transceive(SELECT);
isoDep.close();
callbackContext.success(result[0] + "|" + result[1]);
}catch (IOException e) {
callbackContext.error("error" + e);
}
} catch (IOException e) {
callbackContext.error("error" + e);
}
}
});
}
Cordova implementation
,addIDDiscoverListener: function(callback, win, fail) {
document.addEventListener("tag", callback, false);
cordova.exec(win, fail, "NfcPlugin", "registerIsoDep", []);
}
};
var isoDep = {
readIsoDep: function(win, fail) {
cordova.exec(win, fail, "NfcPlugin", "readIsoDep", []);
}
}
And lastly "app.js"
nfc.addIDDiscoverListener(function(nfcEvent) {
//listenes for the isodep tag
isoDep.readIsoDep(function(tag) {
console.log(tag); //returns ADPU response codes
}, function(reason) {
console.log('error! ' + reason);
});
},
function() {
console.log('registered reader');
},
function(reason) {
console.log('error! ' + reason);
});
My problem is that no matter what app id I select whether it is MasterCard (A0000000041010) with the above one or PPES (proximity payment system environment), I get this error. Since this is my first attempt to do Java I do not know if it is an implementation issue or command issue (which should be fine) most of this code was written by looking at the chariot solutions code and literally thinking yea this should work, this is here so it should be probably be also here ("cordova execute runnable" - i know sh*t what it does, just assumed it should probably be in my code also).
Also there is probably another small bug - it reads the card only ONCE until I quit the app and start it again.
PS.: I have read a ton of SO post, googled like hell for couple of days and tried a lot of things (learned a couple also) but none of them were able to help me.
PSS.: And yes when it is done I want to merge it the the original project.
Thank for any help and sorry for the long post.
The 6F XX that you are seeing is not the status word. In fact 6F ... is exactly what you should receive when your SELECT (by DF name) command succeeds. 6F is the tag of an FCI template.
When you try to retrieve the status word, you are actually reading the first two bytes of the array that you get from the transceive() method. However, a response APDU looks like
+---------+--------+--------+
| DATA | SW1 | SW2 |
| n bytes | 1 byte | 1 byte |
+---------+--------+--------+
So you would need to check the last two bytes of result for the status word:
result[result.length - 2] + "|" + result[result.length - 1]
I am continuously getting data from remote bluetooth device which I am storing in a buffer readBuf.
I copy this readBuf to buf.
System.arraycopy(readBuf, 0, buf, 0, readBuf.length);
Now my buf contains data such that -
buf[0] == 0x7D
buf[1] == 0x51
buf[2] == 0x42
...and so on...
I want to log this data to know what is coming from remote bluetooth device.
I tried,
Log.i(TAG, "Buffer Data---- "+Arrays.toString(buf));
But it is not giving data correctly to be 7D 51 42 and so on....
How to get the data in order to log ?
This is working fine -
StringBuffer bufData = new StringBuffer();
for(byte b : readBuf)
{
bufData.append(String.format("%02X", b));
bufData.append(" ");
}
Log.i(TAG, "Data Coming from Remote Device---"+bufData.toString());
Arrays.toString(byte[])
Works fine, although what you are seeing is an signed integer representation of each byte. That's because in Java all integers are signed with a Two's Complement .
You can learn a lot on how to convert bytes to hex in this answer.
I am newbie in Android NFC API.
Currently, I have a NFC tag, I am making an Android app to read data from it. My simple App is launched when my phone get closer enough to the NFC Tag. But I have no idea how to read the data inside the NFC Tag. The tag uses IsoDep technology.
My current code:
#Override
protected void onResume (){
super.onResume();
Intent intent = getIntent();
Tag tag = intent.getParcelableExtra(NfcAdapter.EXTRA_TAG);
IsoDep isoDep = IsoDep.get(tag);
// How to read data from IsoDep instance?
I googled on internet, I notice people are sending commands to IsoDep to get response from NFC Tag, I suppose from the response, we can parse the data in the tag, I saw people doing this:
//What is the 'command' ? How to define the command?
//e.g.:
byte command = (byte) 0x6A
isoDep.transceive(command)
But, the command is just a byte, as a newbie, it is too difficult to understand what is happening. I have no idea how to define the command to read data? Anyone can explain to me? or is there a document I can learn about the command?
Generally, I need some guidance on how to define commands & how to parse data from response, I would like to read the data stored in the Tag & show the data in String format in UI element (e.g. TextView).
*AND***
I have no problem with those configurations(e.g. AnroidManifest.xml), please don't guide me on how to configure :)
IsoDep allows you to communicate over a ISO-14443-4 connection with the transceive operation. Over this protocol application data units (APDUs) are exchanged. The format is specified, you find a description on Wikipedia.
For exaple, to select an application on a smart card with a particular application identifier (AID) you would execute the following APDU command. The result simply indicates ok (9000) or an error.
byte[] SELECT = {
(byte) 0x00, // CLA Class
(byte) 0xA4, // INS Instruction
(byte) 0x04, // P1 Parameter 1
(byte) 0x00, // P2 Parameter 2
(byte) 0x0A, // Length
0x63,0x64,0x63,0x00,0x00,0x00,0x00,0x32,0x32,0x31 // AID
};
Tag tagFromIntent = intent.getParcelableExtra(NfcAdapter.EXTRA_TAG);
IsoDep tag = IsoDep.get(tagFromIntent);
tag.connect();
byte[] result = tag.transceive(SELECT);
if (!(result[0] == (byte) 0x90 && result[1] == (byte) 0x00))
throw new IOException("could not select applet");
After the application has been selected, you can execute application specific commands. The programs are typically written in JavaCard which follows the GlobalPlatorm spec. The following example executes on the above selected application the method 4 (0x04) which returns a byte array of at most 11 bytes. This result is then converted to a string.
byte[] GET_STRING = {
(byte) 0x80, // CLA Class
0x04, // INS Instruction
0x00, // P1 Parameter 1
0x00, // P2 Parameter 2
0x10 // LE maximal number of bytes expected in result
};
result = tag.transceive(GET_STRING);
int len = result.length;
if (!(result[len-2]==(byte)0x90&&result[len-1]==(byte) 0x00))
throw new RuntimeException("could not retrieve msisdn");
byte[] data = new byte[len-2];
System.arraycopy(result, 0, data, 0, len-2);
String str = new String(data).trim();
tag.close();