Click here to Skip to main content
15,886,110 members
Articles / Programming Languages / C#
Article

A .NET MiFare Helper Class

Rate me:
Please Sign up or sign in to vote.
4.00/5 (6 votes)
8 Jan 2011CPOL3 min read 70.9K   6.8K   19   22
This article introduces a .NET class that makes the use of MiFare card easier.

Introduction

MIFare is a technology that allows a contactless transmission of data between a battery-less memory card and a card reader. MiFare is widely used for ticketing (Oyster card at London underground) or for access control. The helper class introduced in this article aims to simplify the use of MiFare memory cards in a .NET application. In particular, the helper class implements the following features:

  1. Read/write of MiFare card data blocks
  2. Set up of read/write permissions and card keys
  3. Application directory (MAD) management

Background

There are several flavors of MiFare memory cards. This article addresses the so-called MiFare classic, which are shipped in two sizes: 1 kilobytes and 4 kilobytes. The 1K memory card is organized in 16 sectors. Each sector is made up of 4 datablocks, and each datablock is 16 bytes in size. The 4th datablock stores read and write permissions for each of the remaining 3 datablocks. The 4th datablock also stores the access keys. According to MiFare specifications, two keys exists, named keyA and keyB. Each key can be granted a different set of operations on the datablocks. Datablocks in a sector can be read or written only after the application as successfully logged in into the sector with the proper key. The 4K memory card is organized in a similar way. There are 40 sectors. The first 32 sectors have 4 datablocks, the remaining 8 have 16 datablocks. The first datablock of sector 0 stores some read-only manufacturer information. The other 2 datablocks in sector 0 store the MiFare application directory (MAD). An application directory says which application used each sector on the card. The application is identified by a unique identifier registered to a Mifare authority. MAD in sector 0 handles applications in sectors from 1 to 15. There is another MAD (called MAD2) stored in sector 16 that handle sectors from 17 to 39.

Using the Code

The MiFare helper class aims to simplify the use of a MiFare in a .NET application.

Instantiation

To instantiate a MiFARECard class, just call the constructor passing an instance to an object that implements the ICardReader interface. The ICardReader interface decouples the MiFareCard from the physical implementation of the MiFare card reader. The ICardReader interface defines the following methods:

C#
MiFARECard card = new MiFARECard(new FileCardReader("test.txt"));

The FileCardReader is a class that implements the ICardReader interface and simplifies the test of MiFARECard class. The FileReaderCard class reads and writes data from a text file on disk. This makes it easy to test the helper class even if a MiFare reader is not available.

Read/Write of Datablocks

To read data from cards, the MiFareCard class implements two mechanisms:

  1. Call the GetData/SetData methods of the MiFARECard class. This methods allows to write data of any length. The MiFARECard class will take care to login into the proper sectors are required. Trailer sectors are not overwritten.
    C#
    MiFARECard card = new MiFARECard(new FileCardReader("test.txt"));
    
    // read a block of data. The MiFARECard object will take care to login into
    // the proper sectors
    Byte[] data = card.GetData(sector, datablock, 64);
    
    // change data here..
    
    // write data into MiFARECard class internal members
    card.SetData(sector, datablock, data);
    
    // write changes on card
    card.Flush();
  2. Call the GetSector method on the MiFare card. This method returns a Sector object. On this object, you can then call the GetData/SetData method to get the content of a given datablock.
    C#
    MiFARECard card = new MiFARECard(new FileCardReader("test.txt"));
    
    // load the sector of interest
    Sector s = card.GetSector(sector);
    
    // read data from the datablock
    Byte[] data = s.GetData(datablock);
    
    // changes data. Because GetData do not make copies of internal data,
    // any changes to the data array is automatically reflected into the sector
    for (int i=0; i<data.Length; i++)
      data[i] = (byte)0x11;
    
    // write changes back to the card
    card.Flush();

    Note that to write back changes on card, call the Flush method. This method will take care to write on disk only the datablocks that have actually changed.

Set Up Sector Permissions

To set up sector permissions, the Sector class provides a property named AccessConditions. The AccessConditions class exposes a property (DataAreas) that allows the application to set, for each datablock, the permissions (Never, KeyA, KeyB, KeyAOrB) for each operation (Read, Write, Increment, Decrement). AccessConditions class also allows the application to set keys A and B, and also to set whether the MAD is in use or not.

C#
Sector sector0 = card.GetSector(0);
sector0.Access.DataAreas[0].Read = DataAreaAccessCondition.ConditionEnum.KeyAOrB;
sector0.Access.DataAreas[0].Write = DataAreaAccessCondition.ConditionEnum.KeyB;
sector0.Access.DataAreas[0].Increment = DataAreaAccessCondition.ConditionEnum.Never;
sector0.Access.DataAreas[0].Decrement = DataAreaAccessCondition.ConditionEnum.Never;

sector0.FlushTrailer("A0A1A2A3A4A5", "111111111111");

MAD and MAD2 Managements

To manage MAD and MAD2, the MiFARECard class implements two methods:

  1. GetSectors(int appId): This method returns a list of the sectors that are currently in use by the given application.
  2. AddAppId(int appId): This method insert reserves a sector to the given application and returns the number of the sector reserved.

    For example, the source code for an application that wants to reserve the use of a sector according to MAD specifications should look like this:

    C#
    MiFARECard card = new MiFARECard();
    int[] sectors = card.GetSectors(0x5210);
    int appSector;
    If ((sectors != null) && (sectors.Length > 0))
    {
    	appSector = sectors[0];
    }
    else
    {
    	appSector = card.AddAppId(0x5210);
    If (appSector == -1)
    		Throw new NoSectorsAvailableException();
    }
    
    // get data from reserved sector
    Sector s = card.GetSector(appSector);

History

  • 5th January, 2011: Initial version

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Sweden Sweden
This member has not yet provided a Biography. Assume it's interesting and varied, and probably something to do with programming.

Comments and Discussions

 
QuestionWhat does new FileCardReader("test.txt") refer to? Pin
Member 146735696-Dec-19 5:15
Member 146735696-Dec-19 5:15 
Generalnot useful, no dll's included Pin
craml0424-Nov-19 14:19
craml0424-Nov-19 14:19 
BugPending Library in Project Pin
gabrielv86-Apr-17 3:36
gabrielv86-Apr-17 3:36 
QuestionReference missing Pin
Member 1248775126-Apr-16 22:08
Member 1248775126-Apr-16 22:08 
Questionmissing dll's Pin
serkanorhon31-Mar-16 3:57
serkanorhon31-Mar-16 3:57 
QuestionFor all those who are looking for missing dlls Pin
Wahab Hussain20-Jul-14 19:10
Wahab Hussain20-Jul-14 19:10 
BugBug fix in AccessBits.cs Pin
nschmid4-Nov-13 2:15
nschmid4-Nov-13 2:15 
Hi there,

There is a small bug in AccessBits.cs. The bits are very confusing and you probably swaped a line or so. However, here's the fix.

C#
public static AccessConditions GetAccessConditions(Byte[] data)
{
    BitArray byte6 = new BitArray(new Byte[] { 0xFF });
    BitArray byte7 = new BitArray(new Byte[] { 0x07 });
    BitArray byte8 = new BitArray(new Byte[] { 0x80 });
    BitArray byte9 = new BitArray(new Byte[] { 0x69 });

    if (data != null)
    {
        byte6 = new BitArray(new Byte[] { data[6] });
        byte7 = new BitArray(new Byte[] { data[7] });
        byte8 = new BitArray(new Byte[] { data[8] });
        byte9 = new BitArray(new Byte[] { data[9] });
    }

    BitArray[] condBits = new BitArray[4];

    condBits[0] = new BitArray(new bool[] {
                    byte7.Get(4), // C1-0
                    byte8.Get(0), // C2-0
                    byte8.Get(4)  // C3-0
    });

    condBits[1] = new BitArray(new bool[]{
                    byte7.Get(5), // C1-1
                    byte8.Get(1), // C2-1
                    byte8.Get(5)  // C3-1
    });

    condBits[2] = new BitArray(new bool[]{
                    byte7.Get(6), // C1-2
                    byte8.Get(2), // C2-2
                    byte8.Get(6)  // C3-2
    });

    condBits[3] = new BitArray(new bool[] {
                    byte7.Get(7), // C1-3
                    byte8.Get(3), // C2-3
                    byte8.Get(7)  // C3-3
    });

    AccessConditions access = new AccessConditions();
    access.DataAreas[0].Initialize(condBits[0]);
    access.DataAreas[1].Initialize(condBits[1]);
    access.DataAreas[2].Initialize(condBits[2]);
    access.Trailer.Initialize(condBits[3]);

    access.MADVersion = AccessConditions.MADVersionEnum.NoMAD;
    if (byte9.Get(7))
    {
        if (byte9.Get(0))
            access.MADVersion = AccessConditions.MADVersionEnum.Version1;
        if (byte9.Get(1))
            access.MADVersion = AccessConditions.MADVersionEnum.Version2;
    }

    access.MultiApplicationCard = byte9.Get(6);

    return access;
}

QuestionSmall Bug in Access Bits Pin
nschmid4-Nov-13 2:08
nschmid4-Nov-13 2:08 
GeneralMy vote of 1 Pin
Member 102593727-Sep-13 11:34
Member 102593727-Sep-13 11:34 
QuestionDDL mising Pin
julioglez8821-Aug-12 7:33
julioglez8821-Aug-12 7:33 
QuestionWhere is DLL file? PinPopular
nowirch27-Mar-12 17:45
nowirch27-Mar-12 17:45 
QuestionIt`s about Setting permmissions Pin
amraa16-Feb-12 19:16
amraa16-Feb-12 19:16 
SuggestionMissing HexEncoding Pin
Giorgio Bernardi24-Jan-12 5:08
Giorgio Bernardi24-Jan-12 5:08 
GeneralMy vote of 5 Pin
Mark Hesketh18-Jan-12 9:51
Mark Hesketh18-Jan-12 9:51 
QuestionYear has passed, Pin
Monochromatique29-Dec-11 10:38
Monochromatique29-Dec-11 10:38 
Questiondahhh Pin
Cyrus-IRA23-Oct-11 14:29
Cyrus-IRA23-Oct-11 14:29 
Question[My vote of 1] dahhh Pin
Cyrus-IRA23-Oct-11 14:28
Cyrus-IRA23-Oct-11 14:28 
QuestionMissing references Pin
Member 238440830-Aug-11 21:57
Member 238440830-Aug-11 21:57 
QuestionNo referens Pin
VictorMariscal1-Jul-11 7:39
VictorMariscal1-Jul-11 7:39 
GeneralMissing references.. Pin
Member 778739826-Mar-11 13:03
Member 778739826-Mar-11 13:03 
GeneralMissing References Pin
anu200016-Jan-11 23:38
anu200016-Jan-11 23:38 
GeneralReferences Pin
marcos_sis10-Jan-11 1:31
marcos_sis10-Jan-11 1:31 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.