Pikopong

It's all about knowledge

Archive for the ‘java’ tag

Reading Mifare 1K Card using Java in Linux

View 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 :)

Written by amree

February 11th, 2009 at 4:27 pm

Posted in programming

Tagged with , ,

Printing to PDF Using CUPS

View Comments

  1. Install CUPS-PDF based on your distro, you can download if from here.
  2. Click this link http://localhost:631 or type it into your browser.
  3. If the page requested a username and password, just enter your root username and password.
  4. Go the “Administration” tab and select “Add Printer” in the Printer section.
  5. For “Add Printer” page, put a name for your virtual PDF printer, any name will do. Then click “Continue”.
  6. For the second page, that is “Device for a”, you should select “CUPS-PDF (Virtual PDF Driver)”. If it’s not there, then you haven’t execute Step 1 successfully.
  7. In the 3rd page, choose “Generic” as the Make and then click “Continue”.
  8. 4th page, choose “Generic CUPS-PDF Printer (en)” as the model and then click “Add Printer”. You’re done, but we need to customize where the file will be located when you printed it.
  9. Edit /etc/cups/cups-pdf.conf in your favourite editor.
  10. Put these lines at the end of the file and save it (both can be customized):
  11. 1
    2
    Out /home/${USER}/Desktop
    Label 1
  1. The first line actually tell CUPS to print your file to the Desktop and the second line will make sure your file won’t be overwritten by the newer one.
  2. Restart your CUPS, for Slackware users, you can type this command /etc/rc.d/rc.cups restart
  3. So, that’s all and good luck !

Tested on Slackware 12.1, CUPS 1.3.7

Written by amree

September 10th, 2008 at 10:01 am

Posted in linux

Tagged with , , , ,

Java Printing Fix for Linux with CUPS

View Comments

Apparently, there’s a bug in Java where people using newer version of CUPS cannot print (can’t even display the print dialog) due to a null pointer exception and this is actually a known bug. So, how do we fix this? For people who use Gnome, you can refer to this page.

However, for people who uses non gui environment such as Fluxbox, Openbox and etc (Gnome and KDE user can use these steps too), you can fix it by editing your CUPS printers configuration file. You can get edit the file at /etc/cups/printers.conf

<DefaultPrinter Printer>
# Printer configuration file for CUPS v1.3.7
# Written by cupsd on 2008-09-08 11:24
.
.
.
Option orientation-requested 3
</Printer>

Make sure you add line 7 to every configuration for every printer you’ve installed. If you cannot find the file, you probably haven’t configured any printer yet.

This fix is simply to make sure CUPS will provide a page orientation setting to Java.

The exception:

Caused by: java.lang.NullPointerException: null attribute
        at sun.print.IPPPrintService.isAttributeValueSupported(IPPPrintService.java:1147)
        at sun.print.ServiceDialog$OrientationPanel.updateInfo(ServiceDialog.java:2121)
        at sun.print.ServiceDialog$PageSetupPanel.updateInfo(ServiceDialog.java:1263)
        at sun.print.ServiceDialog.updatePanels(ServiceDialog.java:437)
        at sun.print.ServiceDialog.initPrintDialog(ServiceDialog.java:195)
        at sun.print.ServiceDialog.(ServiceDialog.java:124)
        at javax.print.ServiceUI.printDialog(ServiceUI.java:188)
        at sun.print.RasterPrinterJob.printDialog(RasterPrinterJob.java:855)
        at sun.print.PSPrinterJob.printDialog(PSPrinterJob.java:421)

Refer to some of the discussions here:

UPDATE: I forgot to add, you need to restart your CUPS after editing the configuration (Thanks to Brandon Bell)

Tested on Slackware 12.1, JDK 1.6 Update 7, CUPS 1.3.7

Written by amree

September 9th, 2008 at 10:08 am

Posted in linux

Tagged with , , ,

Auto Resize JTable Column Width

View Comments

This code should resize your JTable column width based on the contents of the header and data.

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
    public JTable autoResizeColWidth(JTable table, DefaultTableModel model) {
        table.setAutoResizeMode(JTable.AUTO_RESIZE_OFF);
        table.setModel(model);

        int margin = 5;

        for (int i = 0; i < table.getColumnCount(); i++) {
            int                     vColIndex = i;
            DefaultTableColumnModel colModel  = (DefaultTableColumnModel) table.getColumnModel();
            TableColumn             col       = colModel.getColumn(vColIndex);
            int                     width     = 0;

            // Get width of column header
            TableCellRenderer renderer = col.getHeaderRenderer();

            if (renderer == null) {
                renderer = table.getTableHeader().getDefaultRenderer();
            }

            Component comp = renderer.getTableCellRendererComponent(table, col.getHeaderValue(), false, false, 0, 0);

            width = comp.getPreferredSize().width;

            // Get maximum width of column data
            for (int r = 0; r < table.getRowCount(); r++) {
                renderer = table.getCellRenderer(r, vColIndex);
                comp     = renderer.getTableCellRendererComponent(table, table.getValueAt(r, vColIndex), false, false,
                        r, vColIndex);
                width = Math.max(width, comp.getPreferredSize().width);
            }

            // Add margin
            width += 2 * margin;

            // Set the width
            col.setPreferredWidth(width);
        }

        ((DefaultTableCellRenderer) table.getTableHeader().getDefaultRenderer()).setHorizontalAlignment(
            SwingConstants.LEFT);

        // table.setAutoCreateRowSorter(true);
        table.getTableHeader().setReorderingAllowed(false);

        return table;
    }

Example:

1
2
3
// Must pass the model
DefaultTableModel model = new DefaultTableModel();
jTable = autoResizeColWidth(jTable, model);

Tested on JRE v5 and JRE v6

Written by amree

August 13th, 2008 at 2:49 pm

Posted in programming

Tagged with , ,

Printing to Zebra S4M Using Java and ZPL II

View Comments

Apparently there’re some codes scattered on the net telling people that you can print to a Zebra printer by sending ZPL II codes using PrintService. But the problem is, it’s not working, I don’t know why, maybe because of a different version of printer or model but I’m pretty sure the thing that came out from the printer is just ordinary text not barcode which is what the code was supposed to output.

New Version

Thanks to Oleg for pointing out on how to print using Zebra S4M connected either via USB or network.

The solution is pretty simple, all you have to do is do not install Zebra S4M as a Zebra S4M (sounds weird, I know), instead, just install it as a local raw printer (Linux) or generic text printer (Windows).

For CUPS user in Linux, this is the example for the correct configurations:

#/etc/cups/printers.conf
<Printer Zebra>
Info
Location
DeviceURI socket://10.1.1.5:9100
State Idle
StateTime 1223445299
Accepting Yes
Shared Yes
JobSheets none none
QuotaPeriod 0
PageLimit 0
KLimit 0
OpPolicy default
ErrorPolicy stop-printer
Option orientation-requested 3
</Printer>

You can always add the printer using the web interface, just make sure you choose the RAW as the Make/Manufacturer and Model/Driver.

For Windows user, please take a look at Oleg comment.

Test this Java code, it should print out a barcode:

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
import javax.print.Doc;
import javax.print.DocFlavor;
import javax.print.DocPrintJob;
import javax.print.PrintException;
import javax.print.PrintService;
import javax.print.PrintServiceLookup;
import javax.print.SimpleDoc;
import javax.print.attribute.PrintServiceAttribute;
import javax.print.attribute.standard.PrinterName;

public class SimplePrint {

   public static void main(String[] args) {
       
       try {
           
           PrintService psZebra = null;
           String sPrinterName = null;
           PrintService[] services = PrintServiceLookup.lookupPrintServices(null, null);
           
           for (int i = 0; i < services.length; i++) {
               
               PrintServiceAttribute attr = services[i].getAttribute(PrinterName.class);
               sPrinterName = ((PrinterName) attr).getValue();
               
               if (sPrinterName.toLowerCase().indexOf("zebra") >= 0) {
                   psZebra = services[i];
                   break;
               }
           }
           
           if (psZebra == null) {
               System.out.println("Zebra printer is not found.");
               return;
           }
           DocPrintJob job = psZebra.createPrintJob();

           String s = "^XA^FO100,40^BY3^B3,,30^FD123ABC^XZ";

           byte[] by = s.getBytes();
           DocFlavor flavor = DocFlavor.BYTE_ARRAY.AUTOSENSE;
           Doc doc = new SimpleDoc(by, flavor, null);
           job.print(doc, null);
           
       } catch (PrintException e) {
           e.printStackTrace();
       }      
   }
}
</pre>

<h3>Old Version</h3>
So, I started looking around for other methods. I even tried posting on java's forum and offered 10 duke points, but no one seems to answer my question. After googling around, I found out that I could upload a file containing the ZPL II to the printer, so, I tried and it did work. But another problem arise, Two people cannot ftp simultaneously due to the limited access set by the printer.

Printing using FTP (I'
m using <a href="http://commons.apache.org/net/">Apache Commons Net</a> library)
<code lang="java" line_numbers="true">
import java.io.FileInputStream;
import org.apache.commons.net.ftp.FTP;
import org.apache.commons.net.ftp.FTPClient;

public class FtpPrint {

   public static void main(String[] args) {

       try {

           FTPClient f = new FTPClient();            

           f.connect("10.1.127.3");
           f.login("anonymous", "");
           f.setFileType(FTP.ASCII_FILE_TYPE);                            

           FileInputStream in = new FileInputStream("/path/to/file");
           if (f.storeFile("filename", in)) {
               System.out.println("Upload ok");
           }                

           f.logout();
           f.disconnect();

       } catch (Exception e) {
           e.printStackTrace();
       }
   }
}

In the end I had to find another solution. After 3 days of searching the internet up and down, I found out that Zebra’s website offered a piece of code in Java to print using ZPL II using socket programming which automatically solve all of my problems, I was like, what the.. Note to myself, always search for the manufacturer site before googling around. However, a few modifications needed for the given code such as the printer port and the language sent. You can get the codes below:

Printing using socket (Got it from Zebra website)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
import java.io.DataOutputStream;
import java.net.Socket;

public class SocketPrint {

   public static void main(String argv[]) throws Exception {

       for (int i = 0; i &lt; 10; i++) {
           Socket clientSocket = new Socket("10.1.127.3", 9100);
           DataOutputStream outToServer = new DataOutputStream(clientSocket.getOutputStream());
           outToServer.writeBytes("^XA^FO100,40^BY3^B3,,30^FD123ABC^XZ");
           clientSocket.close();
       }
   }
}

Download guide to ZPL II

Written by amree

May 11th, 2008 at 9:44 am

Posted in programming

Tagged with ,