Reading Mifare 1K Card using Java in Linux
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
- Slackware 12.2
- JDK 6 Update 11
- Netbeans 6.5
- libusb 0.1.12
- Windows Binary of JPC/SC Java API 0.8.0
- 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
- Load Mifare key
- Authenticate
- 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 !











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 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 Sitaraman
19 Feb 09 at 12:04 AM
@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.
amree
19 Feb 09 at 08:04 AM
Hello, thanks for the post.
Is this coding applicable on windows platform?
just to know as u mentioned “in linux”
thanks
dota
26 Feb 09 at 05:24 AM
@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.
amree
26 Feb 09 at 04:19 PM
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
dota
27 Feb 09 at 02:16 AM
@dota
You can find my contact email at the front page, but don’t expect a quick reply :)
amree
27 Feb 09 at 08:13 PM
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.
bilelo
19 Mar 09 at 07:08 PM
@bilelo
May I know which command did you send ?
amree
22 Mar 09 at 07:02 PM
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.
bilelo
23 Mar 09 at 11:01 PM
@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.
amree
24 Mar 09 at 08:47 AM
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
Animesh
6 Apr 09 at 09:57 PM
@Animesh
Thanks for pointing that out, for more information on the connection protocol, readers can find out more about it here.
Post updated accordingly.
amree
7 Apr 09 at 12:05 PM
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
Mina
13 Apr 09 at 10:31 PM
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.
bilgaus
17 Apr 09 at 07:16 AM
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 .
obama
20 Apr 09 at 11:54 PM
@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.
amree
21 Apr 09 at 10:03 AM
@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.
obama
22 Apr 09 at 04:24 PM
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!
vforever
17 Jun 09 at 06:00 AM
@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
amree
25 Jun 09 at 02:17 PM
Hi,
Do you know how to read touch \’n go balance from MyKad or TnG card?
Thanks
Amir
29 Jun 09 at 10:19 AM
@Amir
Not at the moment, most probably TnG data is encrypted
amree
30 Jun 09 at 03:55 PM
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 Sharma
24 Jul 09 at 05:39 PM
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.
Felipe
11 Aug 09 at 01:51 AM
@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)
amree
16 Aug 09 at 12:08 PM
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
Fernando
19 Aug 09 at 11:38 PM
@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.
amree
30 Aug 09 at 06:19 PM
@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
Fernando
2 Sep 09 at 11:03 PM
@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.
amree
4 Sep 09 at 06:36 AM
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.
Luke
28 Oct 09 at 06:40 AM
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.
Luke
29 Oct 09 at 04:06 AM
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
nod
18 Nov 09 at 02:43 AM
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 ?
balaji
2 Dec 09 at 03:04 PM
@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.
amree
5 Dec 09 at 09:03 PM
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.
Galih
8 Dec 09 at 09:11 AM
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
Basti
18 Jan 10 at 06:03 AM
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?
caramel
2 Mar 10 at 03:34 PM
Hi i have successfully run the application with ACR 122 without the line
if i have this active my reader gets an error:
what does this error mean?
Mikael
4 Mar 10 at 07:00 PM
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?
Bilss
22 Apr 10 at 06:36 PM
@Bilss
I’m not sure why, but waitForCardPresent(0) only works in Linux not Windows, for Windows I use these codes:
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.
amree
22 Apr 10 at 08:12 PM