You need to have:
A MiFare Classic 1k Tag - ( hopefully you know the keys for its Blocks :=) )
Android SDK and IDE
Preferable a Nexus S (Make sure if the Android version is 2.3.3 and above).
Some Basics about the card:
MiFare classic cards store data in its Sectors. In MiFare classic 1k card there are 16 of them. Each Sector contains 4 blocks. You can store 16 bytes in each block. Making about 1024 bytes of storage space..that explains the 1K part of the card. You can perform common tasks like reading, writing data on these blocks, authentification, navigating the card sectors by incrementing the blocks count. The first sector contains manufacturer’s details and a unique id for the card. This is a read only part.
Each sector on the Mifare card is secured by two 48-bit keys: A and B. The last block in the sector contains these keys, as well as a configuration that defines what each key can do with each block, i.e block 0 could be configured so that
key A could read and write, but if a reader authenticates with key B, the reader would only be able to read that block.
The rest of the memory storage can be read or written using keys A and B. Fresh, empty Mifare cards have all their sectors locked with a pair of default keys FFFFFFFFFFFF or 000000000000.
Default Keys from experiments
About the NFC part of Android
Since ver 2.3.3 Gingerbread - Android exposes an API to read a list of card technologies. To perform operations on a tag, there are three things to be noted.
1) The cards store data in a format,
2) Reading and Writing data is done using a protocol
3) Cards support a technology that defines what they are
hence reading and writing to these cards can be done only when the data is arranged in that format. MiFare 1K cards support the NDEF format. It also supports NFC - protocol on the communication level. Precisely - ISO 14443 - 3A specification in short NFCA and it uses the MiFare technology.
Now we need to let the Android know what kind of cards we would be using in our application. This is often defined in an XML file stored in the resource folder ( I have named the file - filter_nfc.xml and stored it in a folder named xml). This resource file contains for example,
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<tech-list>
<tech>android.nfc.tech.NfcA</tech>
<tech>android.nfc.tech.MifareClassic</tech>
</tech-list>
</resources>
Here we have declared a tech-list. This list has to be used in the Manifest file. Imagine you would like to start an activity when a tag is touched. The Manifest file is the right place to let the launcher know what activity is to be called when a particular tag is touched.
In the Manifest file, you would have an element - activity. This would declare the name of the activity, a title for it and some metadata. Ideally you would let the system know that you want to start this activity when you touch a MiFare classic card. You can define you own filters for different activities for a variety of tag and protocol combinations.
<activity android:label="@string/event_verify" android:name="verifytagscanact">
<intent-filter>
<action android:name="android.nfc.action.TECH_DISCOVERED"/>
</intent-filter>
<meta-data android:name="android.nfc.action.TECH_DISCOVERED" android:resource="@xml/filter_nfc"/>
</activity>
You would then set the permissions on your Manifest file.
<uses-permission android:name="android.permission.NFC"/>
You can also do this in your onCreate method by using an NfcAdapter,
NfcAdapter mAdapter = NfcAdapter.getDefaultAdapter(this);
When a MiFare tag is discovered, the NFC stack would get the details of the tag and deliver it to a new Intent of this same activity. Hence to handle this, we would need an instance of the PendingIntent from the current activity.
PendingIntent mPendingIntent = PendingIntent.getActivity(this, 0,
new Intent(this, getClass()).addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP), 0);
Then we could set up our filter which defines the data format and technology type.
IntentFilter ndef = new IntentFilter(NfcAdapter.ACTION_TECH_DISCOVERED);
try {
ndef.addDataType("*/*");
} catch (MalformedMimeTypeException e) {
throw new RuntimeException("fail", e);
}
mFilters = new IntentFilter[] {
ndef,
};
// Setup a tech list for all NfcF tags
mTechLists = new String[][] { new String[] { MifareClassic.class.getName() } };
Intent intent = getIntent();
Finally when the pending intent calls the activity again, we like to read the tag. I have put all the steps in the method resolveIntent which would do only the reading part of the tag.
resolveIntent(intent);
Reading the tag
The method looks like
private void resolveIntent(Intent intent){
1) Parse the intent and get the action that triggered this intent
2) Check if it was triggered by a tag discovered interruption.
3) Get an instance of the TAG from the NfcAdapter
4) Get an instance of the Mifare classic card from this TAG instance
5) Connect to card and get the number of sectors this card has..and loop thru these sectors
6) In each sector - get the block count, loop thru the blocks, authenticate the sector and read the data
7) Convert the data into a string from Hex format.
}
filling up with the Android NFC API's
void resolveIntent(Intent intent) {
// 1) Parse the intent and get the action that triggered this intent
String action = intent.getAction();
// 2) Check if it was triggered by a tag discovered interruption.
if (NfcAdapter.ACTION_TECH_DISCOVERED.equals(action)) {
// 3) Get an instance of the TAG from the NfcAdapter
Tag tagFromIntent = intent.getParcelableExtra(NfcAdapter.EXTRA_TAG);
// 4) Get an instance of the Mifare classic card from this TAG intent
MifareClassic mfc = MifareClassic.get(tagFromIntent);
byte[] data;
try { // 5.1) Connect to card
mfc.connect();
boolean auth = false;
String cardData = null;
// 5.2) and get the number of sectors this card has..and loop thru these sectors
int secCount = mfc.getSectorCount();
int bCount = 0;
int bIndex = 0;
for(int j = 0; j < secCount; j++){
// 6.1) authenticate the sector
auth = mfc.authenticateSectorWithKeyA(j, MifareClassic.KEY_DEFAULT);
if(auth){
// 6.2) In each sector - get the block count
bCount = mfc.getBlockCountInSector(j);
bIndex = 0;
for(int i = 0; i < bCount; i++){
bIndex = mfc.sectorToBlock(j);
// 6.3) Read the block
data = mfc.readBlock(bIndex);
// 7) Convert the data into a string from Hex format.
Log.i(TAG, getHexString(data, data.length));
bIndex++;
}
}else{ // Authentication failed - Handle it
}
}
}catch (IOException e) {
Log.e(TAG, e.getLocalizedMessage());
showAlert(3);
}
}// End of method
you would have to think about how you like to handle the lifecycle of this activity. I Like to call the reading part of the activity when I detect a tag and if on pause.. would like to disable this. It has been very neatly explained in the Android samples on Foreground dispatch.. I have added this part of the code here too,
@Override
public void onResume() {
super.onResume();
mAdapter.enableForegroundDispatch(this, mPendingIntent, mFilters, mTechLists);
}
@Override
public void onNewIntent(Intent intent) {
Log.i("Foreground dispatch", "Discovered tag with intent: " + intent);
resolveIntent(intent);
}
@Override
public void onPause() {
super.onPause();
mAdapter.disableForegroundDispatch(this);
}
Hope this was useful for you. I would try writing to tag on my next blog. If you would find better ideas, please do share.
Cheers
Domnic
PS: Hope you enjoy this sample code :)
Ooops...No words to express the achievement...I understand,it is just a peanut for you and your milestones..Congrats..
ReplyDeleteThank you a lot for the post!! I'm trying to write some data on sector 2, but it's really imposible!! If you are right with this, please post it! However, if I get the correct way, I'll told you...
ReplyDeleteThis comment has been removed by the author.
ReplyDeleteNice Turtorial, very usefull...
ReplyDeleteA simple Question:
Is it normal, that the App reads just from Sektor0 Block 0 and 1?
And how can in input the right Key A and B from our System?
Thanks a lot for the Turtorial!
When your next blog with the writing of an Tag is ready?? ;)
could you please post the complete code
ReplyDeleteThanks for making it so simple to understand. I have a question , what is the logic behind the below lines in the getHexString() function .
ReplyDeletehex[index++] = HEX_CHAR_TABLE[v >>> 4];
hex[index++] = HEX_CHAR_TABLE[v & 0xF];
Hello JuRa,
ReplyDeleteThe code is written to read Block 0 and 1. You can read other blocks too..provided you know the respective keys.
To put in your keys, create an array of strings where you would put in your keys in that array..and then pass that array instead of MifareClassic.KEY_DEFAULT
Hello Shekhar Josh,
ReplyDeletethe ISO 14443 defines ways of storing and retriving data from MiFare or NFC. usually they are Hex bits on the low level. The snippet tries to do a simple bitshifting and masking in order to get these LSB and MSB using a table.
Hey Dominic,
ReplyDeleteThanks for the Answer. Now i can read the hole cards...
But i can't write anithyng to the card...how does this work?
Can you please explain it for me?
Thank you very much,
JuRa
Thank you so much for this post.
ReplyDeleteIs the sent data(between the device and the Android device)is encrypted?
Thanks.
Thank you soo much. I been through the code . Though a lil beginner in the Java Programming . This is great and kool. I have run into a problem though.
ReplyDeleteWhen I run this code on the Android Emulator with API level 10 and 2.3.3 SDK it gives an error :
The Application Reading example has stopped unexpectedly .Please Try again.
Any way to make this work.
Thanks and Regards
Vaishali
Thanx for your post!
ReplyDeleteVery helpfull, indeed.
Little remark: maybe, bIndex = mfc.sectorToBlock(j);
should be called before cycle,not inside. Or programm reads only first block of sector several times...
I need to do something else: to use Nexus S as a TAG, therefore I need to write to its MiFair Classic 1k chipset. Can I do this? (I read that the chipset supports all three NFC modes, therefore it should be ok).
ReplyDeleteI didn't find any way to do this (probaly I need to know the Mifair keys?
auth = mfc.authenticateSectorWithKeyA(j, MifareClassic.KEY_DEFAULT);
ReplyDeleteif(auth)
{...}
It is not entering into if block. always showing authentication error. Where i'am wrong? Please help me.
Domnic Hello, congratulations for your post on the blog, I found it useful, however do when reading a label (filled with text) I get the following error: Authentication Failed on Block 0
ReplyDeleteAt what caused this error?
Thank you very much for your help.
Dominic, thanks for posting this example it's been very useful to me.
ReplyDeleteI have encountered a problem, when i try to use my own keys defining a byte array Eclipse tells me that it cannot be resolved. Can you tell me why?
Im sorry for my terrible english
Thanks for the sample code, really helps. The problem for me now is when I scan a F08 tag( mifare classic compatible), it shows(authentication failed on block 0), obviously it is because of the key A, when I use TagInfo to scan the tag, I get the data of the last block of the first sector is as follows 000000000000787788c1000000000000. Key A should be all zeros if I am not wrong. Thats not working either, do you know what is going wrong? thx
ReplyDeleteThank you a lot for the guide!! Please write down also a guide how to write in some sector/block. It would be very helpful and I hope, not so hard :-) Thank you a lot again.
ReplyDeleteHi, I've the same problem... it doesn't enter to the if(auth)... I try woth different keys but it's not working.
ReplyDeletefor(int i = 0; i < bCount; i++){
ReplyDeletebIndex = mfc.sectorToBlock(j);
// 6.3) Read the block
data = mfc.readBlock(bIndex);
// 7) Convert the data into a string from Hex format.
Log.i(TAG, getHexString(data, data.length));
bIndex++;
}
***********************
variable "bIndex" always = mfc.sectorToBlock(j)
move line "bIndex = mfc.sectorToBlock(j)" before bucle "for"
Why using KeyB program runs OK but no stop and start again??? bucle reading card.
ReplyDeleteHow i can put this app in default selection list "Select an action" or "Complete action using"? As soon as i touch the card, "Select an action" ask where i want to show this app also with NFC TagReader or NXP tag reader.
ReplyDeleteHi Dominic Many thanks for the tutorial it helps a lot.
ReplyDeleteI have a problem reading sectors 1-15. I was able to successfully read sector 0 blocks 0-3. I have used authenticate sector with key A and used KEY_MIFARE_APPLICATION_DIRECTORY. Can you recommend how i can read from sectors 1-15. Thanks again.
It’s hard to find knowledgeable people online card readings, but you sound like you know what you’re talking about! Thanks for sharing this with others.
ReplyDeletethanks for code of Reading a MiFare Classic tag...
ReplyDeleteit's so usefull for me.
it saves my time. Thanks big.
ReplyDeletehey. the source code is down....
ReplyDeletecould you please reupp it?
hy i have this code i execute but the problem when the emulator gives me this msg "a fortunately ,reading exaple has stopped "how can i solve this problem pleas?
ReplyDeletehai samira, can you emel me the source code. i can't download it anymore from this site. i'm using phonegap nfc plugin, but didn't found easy way to read mifare classic.
DeleteHi,
DeleteThis code might not work with Phonegap out of the box. You need to use the Phonegap plugin and may be you have to adapt it to your needs.
cheers
Hi, could you please reupload the source code?
ReplyDeleteThanks.
hi samira bou, can i get the code ?
ReplyDelete