MIFARE Classic¶
MIFARE Classic is a very widely used type of NFC tag.
Before starting, we recommend you to read the NFC basics.
Setup¶
The first thing is to declare the NFC permission in your AndroidManifest.xml
:
<uses-permission android:name="android.permission.NFC" />
Then, you have to define a listener on ACTION_TECH_DISCOVERED
in your app.
More details are available in the official doc.
Reading a MIFARE Classic tag¶
In your intent receiver, you can simply use the official Android class MifareClassic.
private void resolveIntent(Intent intent) {
if (NfcAdapter.ACTION_TECH_DISCOVERED.equals(intent.getAction())) {
final Tag tagFromIntent = intent.getParcelableExtra(NfcAdapter.EXTRA_TAG);
final MifareClassic tag = MifareClassic.get(tagFromIntent);
...
}
}
Then, connect to the tag and get the general information:
// class fields
private int mSectorCount = 0;
private int mTagSize = 0;
private String[] readMifareClassicTag(MifareClassic tag) {
int totalBlockRead = 0;
tag.connect();
mSectorCount = tag.getSectorCount();
mTagSize = tag.getSize();
...
}
The next step is to setup the keys to be used for sector authentication:
final List<byte[]> defaultKeys = new ArrayList<>();
defaultKeys.add(MifareClassic.KEY_DEFAULT);
defaultKeys.add(...);
Common known keys for MIFARE classic:
0x00 0x00 0x00 0x00 0x00 0x00
0xA0 0xB0 0xC0 0xD0 0xE0 0xF0
0xA1 0xB1 0xC1 0xD1 0xE1 0xF1
0xB0 0xB1 0xB2 0xB3 0xB4 0xB5
0xAA 0xBB 0xCC 0xDD 0xEE 0xFF
Then, loop on each sector and try to authenticate each time before reading :
boolean auth = false;
byte[] key;
for (int secId = 0; secId < mSectorCount; secId++) {
final Enumeration<byte[]> keys = Collections.enumeration(defaultKeys);
while (keys.hasMoreElements()) {
key = keys.nextElement();
auth = tag.authenticateSectorWithKeyA(secId, key);
if (auth) {
Log.d(TAG, "KeyUsed is KEY A");
break;
}
auth = tag.authenticateSectorWithKeyB(secId, key);
if (auth) {
Log.d(TAG, "KeyUsed is KEY B");
break;
}
}
if (auth) {
// Success, you an now read the blocks here
} else {
// Sector authentication failed
}
}
Finally, read the blocks you need when authentication is succesful:
final SectorDescriptor sectorDescriptor =
new SectorDescriptor(secId, tag.getBlockCountInSector(secId));
final int sectorBlockCount = tag.getBlockCountInSector(secId);
for (int blkId = 0; blkId < sectorBlockCount; blkId++) {
byte[] data = {(byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF,
(byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF,
(byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF,
(byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF};
try {
data = tag.readBlock(blkId + totalBlockRead);
} catch (IOException e) {
Log.e(TAG, e.getMessage(), e);
} finally {
sectorDescriptor.addBlock(blkId, data);
}
}
totalBlockRead += sectorDescriptor.getBlockNumber();
Notes:
- Each sector can have a different number of blocks