pikopong

it's all about knowledge

Reading Mifare 1K Card using Java in Linux

with 39 comments

At the end of last year, my friends and I were tasked to read our staff card. The main objective was pretty simple, to create a library where we don’t have to depend on the vendor everytime we want to read our own staff card and the library should also be working on Windows and Linux (that means Java to us).

Working Environment

  1. Slackware 12.2
  2. JDK 6 Update 11
  3. Netbeans 6.5
  4. libusb 0.1.12
  5. Windows Binary of JPC/SC Java API 0.8.0
  6. Your contactless smartcard reader’s driver

The Reader

Let’s talk about the reader first. You need to get a reader that supports Mifare 1K card (obviously) which is to be exact a reader that supports ISO14443A/B or ISO15693 (contactless standard). Now, to make your life easier, you should get a reader that can read contactless standards compliant contactless using the same framework as ISO7816 compliant contact cards. In simple terms, you can use APDU to get the data that you want.

So, for this little project, we use OMNIKEY CardMan 5321 RFID reader. It has everything that we need, Linux/Windows supports, APDU calls to the contactless card, it even comes a contact card’s reader too and of course, it’s kinda cheap.

Make sure you choose the correct reader or you won’t be able to use this tutorial. Some reader requires you to read using their own specific code, which means you can only use the code you wrote on that particular reader, unlike readers that support APDU calls to contactless cards.

The Card

1024 byte memory is organised as sixteen sectors, each of which is made up of four blocks and each block is 16 bytes long. The first block in the memory (Block 0) is read-only and is set in the factory to contain the four-byte serial number (UID), check bytes and manufacturers data. The last block of each sector (Blocks 3, 7, 11, 15……59, 63) is the Sector Trailer Block which contains the two security Key codes (KeyA and KeyB) and the Access bits that define how the sector can be accessed.

Taking into account the Serial Number/Manufacturers Block and the Sector Trailer Blocks then there are 752 bytes of free memory for user storage. For all Read and Write operations the Mifare card memory is addressed by Block number (in hexadecimal format).

The Installation

Before you begin coding your codes, you need to make sure your environment is ready for you. To do that, just follow steps from my previous tutorial which is pretty much the same from Step 1 tuntill Step 11. For Step 8, you should install the driver based on your reader. OMNIKEY 5321 users can get your driver here.

General Steps to Read the Card

  1. Load Mifare key
  2. Authenticate
  3. Read

Looks simple right ?

The Code

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
package my.husm.mifare;
 
import java.nio.ByteBuffer;
import java.util.List;
import javax.smartcardio.Card;
import javax.smartcardio.CardChannel;
import javax.smartcardio.CardException;
import javax.smartcardio.CardTerminal;
import javax.smartcardio.TerminalFactory;
 
/**
*
* @author mree
*/
public class Read {
 
   public Read() {
 
       try {
 
           CardTerminal terminal = null;
 
           // show the list of available terminals
           TerminalFactory factory = TerminalFactory.getDefault();
           List<CardTerminal> terminals = factory.terminals().list();
 
           String readerName = "";
 
           for (int i = 0; i < terminals.size(); i++) {
 
               readerName = terminals.get(i).toString().substring(
                                   terminals.get(i).toString().length() - 2);
 
               if (readerName.equalsIgnoreCase("01")) {
                   terminal = terminals.get(i);
               }
           }
 
           // Establish a connection with the card
           System.out.println("Waiting for a card..");
           terminal.waitForCardPresent(0);
 
           Card card = terminal.connect("T=0");
           CardChannel channel = card.getBasicChannel();
 
           // Start with something simple, read UID, kinda like Hello World!
           byte[] baReadUID = new byte[5];
 
           baReadUID = new byte[]{(byte) 0xFF, (byte) 0xCA, (byte) 0x00,
                                  (byte) 0x00, (byte) 0x00};
 
           System.out.println("UID: " + send(baReadUID, channel));
           // If successfull, the output will end with 9000
 
           // OK, now, the real work
           // Get Serial Number
           // Load key
           byte[] baLoadKey = new byte[12];
 
           baLoadKey = new byte[]{(byte) 0xFF, (byte) 0x82, (byte) 0x20,
                                     (byte) 0x1A, (byte) 0x06, (byte) 0xFF,
                                     (byte) 0xFF, (byte) 0xFF, (byte) 0xFF,
                                     (byte) 0xFF, (byte) 0xFF};
 
           System.out.println("LOAD KEY: " + send(baLoadKey, channel));
           // If successfull, will output 9000
 
           // Authenticate
           byte[] baAuth = new byte[7];
 
           baAuth = new byte[]{(byte) 0xFF, (byte) 0x88, (byte) 0x00,
                               (byte) 0x09, (byte) 0x60, (byte) 0x00};
 
           System.out.println("AUTHENTICATE: " + send(baAuth, channel));
           // If successfull, will output 9000
 
           // Read Serial
           byte[] baRead = new byte[6];
 
           baRead = new byte[]{(byte) 0xFF, (byte) 0xB0, (byte) 0x00,
                               (byte) 0x09, (byte) 0x10};
 
           System.out.println("READ: " + send(baRead, channel));
           // If successfull, the output will end with 9000
 
       } catch (Exception ex) {
           ex.printStackTrace();
       }
   }
 
   public String send(byte[] cmd, CardChannel channel) {
 
       String res = "";
 
       byte[] baResp = new byte[258];
       ByteBuffer bufCmd = ByteBuffer.wrap(cmd);
       ByteBuffer bufResp = ByteBuffer.wrap(baResp);
 
       // output = The length of the received response APDU
       int output = 0;
 
       try {
 
           output = channel.transmit(bufCmd, bufResp);
 
       } catch (CardException ex) {
           ex.printStackTrace();
       }
 
       for (int i = 0; i < output; i++) {
           res += String.format("%02X", baResp[i]);
           // The result is formatted as a hexadecimal integer
       }
 
       return res;
   }
 
   public static void main(String[] args) {
       new Read();
   }
}

Make sure you set the right connection protocol at line 43, as pointed out by Animesh.

These codes use Java API to read the smartcard, but I suggest you use the jpcsc library, it’s much more robust when it comes to error handling. These codes will generate error response, so, you need to change the commands based on your smartcard.

The APDU Commands

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
# Load Key
+--------+--------+--------+--------+--------+--------+--------+--------+--------+--------+--------+
|   FF   |   82   |   20   |   1A   |   06   |   FF   |   FF   |   FF   |   FF   |   FF   |   FF   |
+--------+--------+--------+--------+--------+--------+--------+--------+--------+--------+--------+
|   1    |   2    |   3    |   4    |   5    |   6    |   7    |   8    |   9    |   10   |   11   |
+--------+--------+--------+--------+--------+--------+--------+--------+--------+--------+--------+
 
1   = CLA : (Fixed)
2   = INS : (Fixed)
3   = P1  : (Fixed)
4   = P2  : Key type, could be 00, 1A, 1B
5   = LC  : Key length, usually 6 for 6 byte
6   = XX  : ----+
7   = XX  :     |
8   = XX  :     |__ The Key (eg. FF FF FF FF FF FF)
9   = XX  :     |
10  = XX  :     |
11  = XX  : ----+
 
# Autenthicate
+--------+--------+--------+--------+--------+--------+
|   FF   |   88   |   00   |   01   |   60   |   00   |
+--------+--------+--------+--------+--------+--------+
|   1    |   2    |   3    |   4    |   5    |   6    |
+--------+--------+--------+--------+--------+--------+
 
1   = CLA : (Fixed)
2   = INS : (Fixed)
3   = P1  : (Fixed)
4   = P2  : Block Number you want to authenticate in Hex
5   = P3  : Mifare Block Number LSB
6   = XX  : Key type, could be 00, 1A, 1B
 
# Read
+--------+--------+--------+--------+--------+
|   FF   |   B0   |   00   |   01   |   10   |
+--------+--------+--------+--------+--------+
|   1    |   2    |   3    |   4    |   5    |
+--------+--------+--------+--------+--------+
 
1   = CLA : (Fixed)
2   = INS : (Fixed)
3   = P1  : (Fixed)
4   = P2  : Block Number that you want to read in Hex
5   = Le  : (Fixed)

So, that’s it, good luck trying !

This project has been abandoned. Thus, I may or may not be able to help if you encountered any problems but thanks for viewing :)

  • Digg
  • del.icio.us
  • Facebook
  • Google Bookmarks
  • PDF
  • Print
  • Reddit
  • Slashdot
  • Technorati
  • Twitter

Related Posts

Written by amree

February 11th, 2009 at 4:27 pm

Posted in programming

Tagged with , ,

39 Responses to 'Reading Mifare 1K Card using Java in Linux'

Subscribe to comments with RSS or TrackBack to 'Reading Mifare 1K Card using Java in Linux'.

  1. Hi,
    I am using an Omnikey 55×3 reader. I am able to read successfully of a mifare 4k chip (i am using the same set of APDU’s as you’ve specified). However, I am having a problem writing to the chip.

    For eg., let’s say i’m writing 0×00 – 0xff to sector 0, block 1 of the chip, I use the APDUs:

    Load Keys: FF82200006FFFFFFFFFFFF
    Authenticate: FF8800016000
    Update binary: FFD6000110000102030405060708090A0B0C0D0E0F

    Load keys and authenticate return successfully with 0×9000. However, the update binary command is returning with 0×6581 return (Memory failure: unsuccessful writing). Would you know what could be wrong?! I am able to write to the chip using the Pegoda reader, so i am pretty sure that the block is not locked out! Thanks.

    Srinath

    Srinath SitaramanNo Gravatar

    19 Feb 09 at 12:04 AM

  2. @Srinath Sitaraman

    I’m sorry that I can’t help you with the writing part as I’ve only tried reading the card. You can try looking in this document from OMNIKEY. It helped me a lot.

    amreeNo Gravatar

    19 Feb 09 at 08:04 AM

  3. Hello, thanks for the post.
    Is this coding applicable on windows platform?
    just to know as u mentioned “in linux”

    thanks

    dotaNo Gravatar

    26 Feb 09 at 05:24 AM

  4. @dota

    I haven’t tested this code in Windows, but it should be working just fine, you just need to configure the correct environment for jpcsc (which is a little different in Windows), you can refer to my previous post on configuring the environment. If I have the time, I will post a full article on how to read it in Windows.

    amreeNo Gravatar

    26 Feb 09 at 04:19 PM

  5. Hi amree

    thanks for the reply. anyway, what field are u working in now? i noticed on the previous comments that u are currently working in HUSM. in what department actually?

    i am a final year student in Software Engineering and needs to use mifare 1k card with usb reader to do my final year project.

    may i have your contact so that i can pm u through ym @ skype?

    u seems to be a knowledgable person and i just want us to share any information regarding programming ;)

    thanks

    dotaNo Gravatar

    27 Feb 09 at 02:16 AM

  6. @dota

    You can find my contact email at the front page, but don’t expect a quick reply :)

    amreeNo Gravatar

    27 Feb 09 at 08:13 PM

  7. thanks for this code , but I have problem; I have SDI010 lector and I have Mifare 1K Card , when I compile this code and I try ti load the key I don’t receive 9000 ( but i reveice 6893 ) , can you tell me where is the problem .
    Thanks for your help.

    bileloNo Gravatar

    19 Mar 09 at 07:08 PM

  8. @bilelo

    May I know which command did you send ?

    amreeNo Gravatar

    22 Mar 09 at 07:02 PM

  9. Thank you for answer me,

    I send the command responsible for load key :

    byte[] baLoadKey = new byte[12];

    baLoadKey = new byte[]{(byte) 0xFF, (byte) 0×82, (byte) 0×20,
    (byte) 0x1A, (byte) 0×06, (byte) 0xFF,
    (byte) 0xFF, (byte) 0xFF, (byte) 0xFF,
    (byte) 0xFF, (byte) 0xFF};

    System.out.println(“LOAD KEY: ” + send(baLoadKey, channel));
    // If successfull, will output 9000

    but he return 6893 , and I want to say that I campile this code on Windows.

    Thanks for your help.

    bileloNo Gravatar

    23 Mar 09 at 11:01 PM

  10. @bilelo

    I’m sorry, I’m unable to reproduce that error, maybe you should check PCSC specification and see what does that error code means.

    amreeNo Gravatar

    24 Mar 09 at 08:47 AM

  11. This is a great tutorial. Thanks!

    Minor point: The code did not work for my setup (winXP with NFC 122), and then I realized that using \"*\" instead of \"T=0\" worked. (Somehow mine was actually using T=1). Hope this helps some of your readers.

    bests,
    Animesh

    AnimeshNo Gravatar

    6 Apr 09 at 09:57 PM

  12. @Animesh

    Thanks for pointing that out, for more information on the connection protocol, readers can find out more about it here.

    Post updated accordingly.

    amreeNo Gravatar

    7 Apr 09 at 12:05 PM

  13. Thanks alot for this code…
    i seem to have one problem.
    i\’m actually working with eclipse on windows platform,but i can\’t import javax.smartcardio.*;
    i don\’t know where it is and i can\’t import it into eclipse libraries…. can u help me?
    i\’m really new to smart card programming,and this is a task in my bachelor project

    thanks in advance

    MinaNo Gravatar

    13 Apr 09 at 10:31 PM

  14. Hi,
    @Mina U have to use java sdk 1.6

    this code has help me a lot for my project but I have to work with mifare Desfire Card , Can someone tell me how to read from this card .
    Thanks for your help.

    bilgausNo Gravatar

    17 Apr 09 at 07:16 AM

  15. hi , in my project I have to write keys in the card then I load the key that I will use it for read or write .
    can U tell me the command ADPU that let me to write a key in a mifare card.
    Thanks for any help .

    obamaNo Gravatar

    20 Apr 09 at 11:54 PM

  16. @bilgaus

    Thanks for pointing that out to Mina.

    @obama

    I’m sorry, I don’t have any new empty Mifare card, so, I don’t really have the chance to explore it, maybe one of the readers can tell you about it.

    amreeNo Gravatar

    21 Apr 09 at 10:03 AM

  17. @amree
    thank you for answering me , but I really want the APDU command that let me to load key A and key b with the access condition in the tailor block.
    thakns for any help or any suggess.

    obamaNo Gravatar

    22 Apr 09 at 04:24 PM

  18. Hi there!
    That\’s a really useful code! Thanks for that!
    One thing: did any of you come across an error 0x8010002E about \"No readers found\"? I\’ve been trying to solve this problem almost for a week already, still no luck :( Tried to modify LOCAL SERVICE permissions in the registry, Smart Cards service is started automatically.
    Can anybody guess on the reason of this error?
    OS: Vista Home Premium
    Reader: ACR 120U (Manufacturer: ACS)
    Thanks a lot for reading!

    vforeverNo Gravatar

    17 Jun 09 at 06:00 AM

  19. @obama

    I’m not sure I follow you

    @vforever

    Maybe you didn’t install the driver correctly ? Google the error code, there’re lots of articles about it

    amreeNo Gravatar

    25 Jun 09 at 02:17 PM

  20. Hi,
    Do you know how to read touch \’n go balance from MyKad or TnG card?

    Thanks

    AmirNo Gravatar

    29 Jun 09 at 10:19 AM

  21. @Amir
    Not at the moment, most probably TnG data is encrypted

    amreeNo Gravatar

    30 Jun 09 at 03:55 PM

  22. Hi, Could u please tell me Steps how to configure Paths and all in windows.im working in windows and there is lots of chaos in setting variables..!! Its kinda urgent.
    Any help will be Appreciated.
    Thanks

    Vikas SharmaNo Gravatar

    24 Jul 09 at 05:39 PM

  23. Hello, you blog is great…
    thanks for the post, it was very very usefull for me, I have a couple of questions…
    I am developping a web site with JSP/Servlets and one of the requirements is to use this kind of cards to login, but reading distance should be limmited (15cm) to allow 2 users be at the same workstation without confusing the reader.

    How much is the distance allowed for the OMNIKEY 5321? I’ve read specifications but the just mention frequency, do you know if a 13.56 MHz reader like 5321 can be configured to read at a maximun of 15cm?

    if not (for OMNIKEY 5125 PROX), do your think this you code can be compatible with: http://www.hidglobal.com/documents/OK5125_ds_en.pdf

    Thanks in advance!!

    Felipe.

    FelipeNo Gravatar

    11 Aug 09 at 01:51 AM

  24. @Vikas Sharma

    For Java, use JRE/JDK installation from Sun and your path should be configured automatically and IIANM any DLL files should be copied into the system folder so that Windows could find it automatically.

    @Felipe

    I’m not sure about the distance, didn’t really test it, maybe you could find your answer here.

    For OMNIKEY 5125, I don’t think it’s possible with Mifare 1K Cards as it doesn’t support standards for contactless cards (ISO 14443 A/B and 15693)

    amreeNo Gravatar

    16 Aug 09 at 12:08 PM

  25. Hello,

    Im trying the code on windows vista with acs122U reader and java smartcard api. It return 6300 trying to read UID.
    When waitForCardPresent() is executed, it doesnt stop waiting a tag present and return inmediately continuing the execution.

    I install the acs windows drivers, need any more?
    Any help will be Appreciated

    Fernando

    FernandoNo Gravatar

    19 Aug 09 at 11:38 PM

  26. @Fernando

    Does ACS 122U supports sending command using APDU ? Can’t help much, I don’t have the reader to reproduce the error.

    Try referring to Omnikey Developer’s Guide for any error code you may encounter.

    amreeNo Gravatar

    30 Aug 09 at 06:19 PM

  27. @amree
    Hi amree, thanks for the answer. Finally ACS 122U works with a different set of APDUs, may be propietary.
    Also I try your code on a Omnikey reader and it works successfully.
    Regards

    FernandoNo Gravatar

    2 Sep 09 at 11:03 PM

  28. @Fernando

    I’m not sure about ACS 122U price (if you’re considering about the price), but I think it’s better to use something that is universal so that user won’t be locked in to a certain vendor.

    Maybe you can post your code somewhere, I’m sure there are people out there wanna know how you did it.

    amreeNo Gravatar

    4 Sep 09 at 06:36 AM

  29. I’m about to pull my hair out.. Got a SDK for Mifare with a reader. Where did you get a list of the ADPUs for the card?

    I want to write to the card. I looked in ISO 14443-4 and 7816-4 but your code, although works, doesn’t seem to make sense in the context of those standards. To me at least. Mifare is a bit different and I can’t seem to figure out where this is all documented.

    LukeNo Gravatar

    28 Oct 09 at 06:40 AM

  30. I was finally able to figure out that the nasty business of authenticating the card was actually done by my reader. The process is described in the PCSC spec – part 3. The only change I had to do was to make my key number 0×60 instead of 0x1A as shown in this example. I still don’t know why though, but it all works.

    LukeNo Gravatar

    29 Oct 09 at 04:06 AM

  31. I’m having a problem with writing and I suppose it is a smartcardio problem.

    The every time I try to write I got “javax.smartcardio.CardException: Could not obtain response” message.

    Could someone rewrite this code using jpcsc library ?

    Any help will be Appreciated

    nodNo Gravatar

    18 Nov 09 at 02:43 AM

  32. I’m having a problem with writing and I suppose it is a smartcardio problem.

    The every time I try to write I got “javax.smartcardio.CardException: Could not obtain response” message.

    Could someone rewrite this code using jpcsc library ?

    balajiNo Gravatar

    2 Dec 09 at 03:04 PM

  33. @nod/balaji

    Checkout MyKad reading code. It uses JPCSC library to send the APDU command, just replace it with the commands above. It’s pretty much the same in terms of finding reader, establishing connection and sending APDUs.

    amreeNo Gravatar

    5 Dec 09 at 09:03 PM

  34. Hello,

    Can u help me, if I want to read/write mifare in sector 10 block 1 which part of the apdu I must change in all section in the example.

    Thanx.

    GalihNo Gravatar

    8 Dec 09 at 09:11 AM

  35. Hello,
    I’ve runned the code. It succeeds for the UID (ends with 9000), but have problems sending the load key. As I’ve seen there is a couple of bytes that have to changed depending on the card. How do I ger the load key for my card?
    Thanks

    BastiNo Gravatar

    18 Jan 10 at 06:03 AM

  36. ive runned the code.but below is the error displayed.
    ive used gemplus 8kb combicard as a card and omnikey 5321 as reader.so,is that mean dat dis progrm cannt read my card?

    1
    2
    3
    4
    5
    6
    7
    
    List of PC/SC Readers connected:
    Reader: OMNIKEY CardMan 5x21 0
    Reader: OMNIKEY CardMan 5x21-CL 0
    Terminal fetched: OMNIKEY CardMan 5x21 0
    Terminal NOT connected: javax.smartcardio.CardException: connect() failed
    Error: null
    BUILD SUCCESSFUL (total time: 16 seconds)

    caramelNo Gravatar

    2 Mar 10 at 03:34 PM

  37. Hi i have successfully run the application with ACR 122 without the line

    terminal.waitForCardPresent(0);

    if i have this active my reader gets an error:

    1
    2
    3
    
    javax.smartcardio.CardException: wait mismatch
            at sun.security.smartcardio.TerminalImpl.waitForCard(TerminalImpl.java:103)
            at sun.security.smartcardio.TerminalImpl.waitForCardPresent(TerminalImpl.java:116)

    what does this error mean?

    MikaelNo Gravatar

    4 Mar 10 at 07:00 PM

  38. Mikael,

    I have the same exception.
    I’ve been looking around and found this :http://lxr.js-home.org/lxr/source/sun/security/smartcardio/TerminalImpl.java

    Apparently this happens when timeout has expired and when no card is present.
    Since you called waitForCardPresent(0) , it should be inifinite timeout…

    I am in the same situation, have you found a solution?

    BilssNo Gravatar

    22 Apr 10 at 06:36 PM

  39. @Bilss

    I’m not sure why, but waitForCardPresent(0) only works in Linux not Windows, for Windows I use these codes:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    
    #ifdef Q_OS_WIN32
        do {
     
            rgReaderStates.szReader = &mszReaders[iList[iReader]];
            rgReaderStates.dwCurrentState = SCARD_STATE_PRESENT;
            rgReaderStates.dwEventState = SCARD_STATE_PRESENT;
     
     
            rv = SCardGetStatusChange(hContext,
                                      INFINITE,
                                      &rgReaderStates,
                                      1);
     
        } while ((rgReaderStates.dwEventState & SCARD_STATE_EMPTY) == 0);
    #else
        rgReaderStates[0].szReader = &mszReaders[iList[iReader]];
        rgReaderStates[0].dwCurrentState = SCARD_STATE_PRESENT;
        rv = SCardGetStatusChange(hContext, INFINITE, rgReaderStates, 1);
    #endif

    As you can see, I had to use different codes for different OS. Those codes are in C++, but it’s pretty much the same for Java.

    amreeNo Gravatar

    22 Apr 10 at 08:12 PM

Leave a Reply