Android e NFC – Leggere una scheda MIFARE CLASSIC da 1k

In questo articolo mostro come leggere una scheda MIFARE CLASSIC da 1k usando la tecnologia Near Field Communication, implementata in molti dispositivi Android.

Necessario:

  • Una scheda MiFare Classic 1k Tag;
  • Android SDK and IDE;
  • Un dispositivo Android dotato di NFC ( ad esempio Samsung Galaxy Nexus).

Alcune nozioni di base sulla carta:

Nella scheda Mifare Classic 1k ci sono 16 settori divisi in 4 blocchi. All’interno dei settori vengono memorizzate le informazioni. È possibile memorizzare 16 byte in ogni blocco per un totale di circa 1024 byte di spazio di archiviazione ( ciò spega perchè 1K ) .

È possibile eseguire attività comuni come la lettura, la scrittura dei dati su questi blocchi, l’autenticazione, la navigazione dei settori incrementando o decrementando il conteggio dei blocchi.

Il primo settore contiene i dettagli del produttore e un ID univoco per la scheda.

Ogni settore sulla carta Mifare è fissato con due chiavi a 48 bit: A e B, che sono memorizzate nel blocco 3 di ogni settore. All’interno del 3 blocco sono inoltre definite le politiche di accesso, e le operazioni permesse su quel settore, definendo inoltre quale delle 2 chiavi occorre usare per l’operazione relativa.

Una carta Mifare ha di default i settori bloccati con una coppia di chiavi predefinita FFFFFFFFFFFF o 000000000000.

In questo tutorial useremo una scheda MIFARE con impostazioni di default.

Detto ciò passiamo subito alla parte implementativa. Apriamo il nostro nuovo progetto Android ed andiamo a configurare per prima cosa il file Manifest.xml.

Dobbiamo informare Android su che tipo di carta utilizzeremo nella nostra applicazione. Questo è spesso definito in un file XML memorizzato nella cartella delle risorse. Chiamiamo questo file filter_nfc.xml, e lo salviamo nella cartella res/xml:

<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>

Andiamo ora a impostare questo file nel Manifest. Immaginate che si desidera avviare un’attività in cui un tag viene toccato. Il manifest è il posto giusto per informare Android che la nostra app può gestire il tag rilevato.

<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>

E’ possibile far ciò anche direttamente via codice usando un NfcAdapter:

NfcAdapter mAdapter = NfcAdapter.getDefaultAdapter(this);

Quando un MiFare tag è scoperto, lo stack NFC ottiene i dettagli del tag e spedisce questo ad un nuovo intent della stessa attività. Per gestirlo, occorre quindi un istanza PendingIntent dall’attività corrente:

PendingIntent mPendingIntent = PendingIntent.getActivity(this, 0,
new Intent(this, getClass()).addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP), 0);

Impostiamo quindi un filtro nel quale definiamo il formato dei dati e la tecnologia:

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();

Infine, quando il pending intent chiama nuovamente l’attività, andiamo a leggere il tag.

Per gestire l’evento di scoperta di un nuovo TAG all’interno del campo NFC, effettuiamo l’override del metodo onNewIntent:

@Override
 protected void onNewIntent(final Intent intent) {
 Toast.makeText(this, "Tag Detected", Toast.LENGTH_SHORT).show();
 createTag(intent);
 }

Nel metodo createTag andremo a controllare il tipo di tag scoperto, e se corretto, lanceremo il thread di gestione.

private void createTag(final Intent intent) {
 final Thread thread = new Thread(this);

 // The getParcelableExtra(String name), retrieves the extended data from the intent
 // and returns the value of an item that previously added with putExtra()or
 // returns null if no Parceable value was found.
 // In this case it returns a Tag Object
 mTagFromIntent = intent.getParcelableExtra(NfcAdapter.EXTRA_TAG);

 if (mTagFromIntent != null) {
 //The MifareClassic.get(Tag tag), gets an instance of MifareClassic for the given tag.
 // It does not cause any RF activity and does not block.
 mClassic = MifareClassic.get(mTagFromIntent);

 if (mClassic != null) {
 /* Create a new ProgessDialog */
 mProgressDialog = ProgressDialog.show(this, "Working..", "Reading Classic", true, false);
 /* Start the thread to read the DESFire */
 thread.start();
 }
 }
 }

Al run del thread:

@Override
 public void run() {
 mData = readCard();
 }

Il metodo readCard si occuperà di effettuare le operazioni che vogliamo all’interno della carta MIFARE:

ByteArrayBuffer b = new ByteArrayBuffer(mMaxSize);
 byte[] data = null;
 StringBuilder sb = new StringBuilder();

 int sector = 5;

 try {
writeAndRead();
}
 /* Catch the TagLostException */
 catch (final TagLostException tag) {
 tag.printStackTrace();
 sb.append("Tag Lost");
 }
 /* Catch the IOException */
 catch (final IOException e) {
 e.printStackTrace();
 sb.append("IOException "+ e.getMessage());
 }

 if (data != null)
 sb = convertHex(data);
 /* Convert the bytes to hex */

 return sb.toString();

Nel metodo writeAndRead effettuiamo una scrittura e lettura di un valore all’interno del primo settore disponibile:

String file_string ="";
ByteArrayBuffer b = new ByteArrayBuffer(mMaxSize);
byte[] data = null;
boolean success = authenticateOnBlock(sector);

Log.i("MIFARE CLASSIC", "authenticateOnBlock " + sector + String.valueOf(success));

if(success){
//Return the number of blocks in the given sector.
this.blockCountInSector = this.mClassic.getBlockCountInSector(sector);

int firstBlock = mClassic.sectorToBlock(sector);
Log.i("MIFARE CLASSIC", "first block of the given sector:" + firstBlock);

writeTag(firstBlock);

b = readSector(firstBlock);

data = b.toByteArray();
for(int i = 0; i < data.length; i++){
if(data[i]!=0)
file_string += (char) data[i];
}
Log.i("MIFARE CLASSIC", "TAG STORED " +file_string);
}
mClassic.close();

private Boolean authenticateOnBlock(int s) throws IOException{
/* Connect to the Tag */
this.mClassic.connect();
boolean success = this.mClassic.authenticateSectorWithKeyA(s, MifareClassic.KEY_DEFAULT );
return success;
}

Scarica il codice d’esempio cliccando su uno dei pulsanti social. Grazie!

[like_to_read]Qui[/like_to_read]

Michele Pierri

Sviluppatore android/web/desktop, blogger, nonchè sostenitore incallito dell'universo Cloud Computing, nel tempo libero amo fare sport e praticare arti marziali.