Search code examples
androidauthenticationpermissionsnfcmifare

Write and read data to Mifare Classic 1k NFC tag


I am trying to read and write data on a Mifare Classic 1k NFC tag.

I found the keys and the access conditions of the card thanks to this app :

Keys:

Keys

Access Conditions:

Access Conditions

<uses-permission android:name="android.permission.NFC" /> is present in my manifest.

Here is my code:

Tag tag = intent.getParcelableExtra(NfcAdapter.EXTRA_TAG);

if(tag != null) {
    Log.i("hey", Arrays.toString(tag.getTechList()));
    MifareClassic mfc = MifareClassic.get(tag) ;

    try {
        mfc.connect();
        boolean authA = mfc.authenticateSectorWithKeyA(2, MifareClassic.KEY_NFC_FORUM) ;
        boolean authB = mfc.authenticateSectorWithKeyB(2, MifareClassic.KEY_DEFAULT) ;
        Log.i("hey", "authA : " + authA) ;
        Log.i("hey", "authB : " + authB) ;

        if (authB && authA) {
            byte[] bWrite = new byte[16];
            byte[] hello = "hello".getBytes(StandardCharsets.US_ASCII);
            System.arraycopy(hello, 0, bWrite, 0, hello.length);
            mfc.writeBlock(0, bWrite);
            Log.i("hey", "write : " + Arrays.toString(bWrite));

            byte[] bRead = mfc.readBlock(0);
            String str = new String(bRead, StandardCharsets.US_ASCII);
            Log.i("hey", "read bytes : " + Arrays.toString(bRead));
            Log.i("hey", "read string : " + str);
            Toast.makeText(this, "read : " + str, Toast.LENGTH_SHORT).show();
            Log.i("hey", "expected : " + new String(bWrite, StandardCharsets.US_ASCII));
        }



        mfc.close();

    } catch (IOException e) {
        Toast.makeText(this, "Error", Toast.LENGTH_SHORT).show();
        e.printStackTrace();
        Log.i("hey", "Error") ;
    }


}

When I try to write and read like that, what I read is not what I wrote.

Here is the logcat:

I/hey: [android.nfc.tech.NfcA, android.nfc.tech.MifareClassic, android.nfc.tech.NdefFormatable]
I/hey: authA : true
    authB : true
I/hey: write : [104, 101, 108, 108, 111, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
I/hey: read byte : [-78, 0, 0, 0, 0, 0, -1, 7, -128, 105, -1, -1, -1, -1, -1, -1]
    read string : �������������i������
I/hey: expected : hello����������������������

I tried with different charsets but it didn't change anything.

I also tried to only read the Sector 0 by commenting the write part and changing authentication key A to MifareClassic.KEY_MIFARE_APPLICATION_DIRECTORY and this time I get this Logcat:

I/hey: [android.nfc.tech.NfcA, android.nfc.tech.MifareClassic, android.nfc.tech.NdefFormatable] 
I/hey: authA : true
        authB : true 
I/hey: read bytes : [-123, -121, 0, 16, 18, 8, 4, 0, 1, -64, 62, -70, 74, 124, 78, 29]
        read string : �������>�J|N

Any idea about how can I fix it to write and display text properly?


Solution

  • You first authenticate to sector 2:

    boolean authA = mfc.authenticateSectorWithKeyA(2, MifareClassic.KEY_NFC_FORUM);
    boolean authB = mfc.authenticateSectorWithKeyB(2, MifareClassic.KEY_DEFAULT);
    

    Then, you try to write and read block 0:

    mfc.writeBlock(0, bWrite);
    byte[] bRead = mfc.readBlock(0);
    

    There are several problems with this:

    1. It does not make sense to authenticate using both key A and key B. Only the last authentication determines the authentication state of the tag. Since all sectors seem to be writable using key B, you can safely use the second line (mfc.authenticateSectorWithKeyB() only).

    2. You authenticate to sector 2, which consists of blocks 8, 9, 10, and 11. Writing and reading block 0 does not make sense in that authentication state. Note that read/write operations typically use global block numbering. Sectors are only used as logical units for authentication/access control purposes. You can easily calculate block numbers from sector numbering using mfc.sectorToBlock() and mfc.getBlockCountInSector().

    3. Block 0 (in sector 0) is a special block, the manufacturer block. It typically contains the UID, SAK and manufacturer information burned into the tag during the production process. This block is read-only and cannot be changed. Consequently, writing to that block does not have any effect.