Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Get UID of Mifare Ultralight with SCL010

I want get the UID of the Mifare Ultralight NFC tag. In Java I have this code:

TerminalFactory factory = TerminalFactory.getDefault();
List<CardTerminal> terminals = factory.terminals().list();
System.out.println("Terminals: " + terminals);

CardTerminal terminal = terminals.get(0);

Card card = terminal.connect("*");
System.out.println("card: " + card);
CardChannel channel = card.getBasicChannel();

ResponseAPDU answer = channel.transmit(new CommandAPDU(0xFF, 0xCA, 0x00, 0x00, 0x00));
byte[] uid = answer.getBytes();

The problem is that I receive two bytes and not the UID. What's the problem? Is the APDU correct?

like image 485
Tom Avatar asked Feb 23 '14 18:02

Tom


2 Answers

The command you are actually using is not what you might have expected.

The correct command APDU to get the UID/serial number/enumeration identifier with this reader is:

+------+------+------+------+------+
| CLA  | INS  |  P1  |  P2  |  Le  |
+------+------+------+------+------+
| 0xFF | 0xCA | 0x00 | 0x00 | 0x00 |
+------+------+------+------+------+

However, the constructor you are using is defined as:

public CommandAPDU(int cla, int ins, int p1, int p2, int ne);

So with

new CommandAPDU(0xFF, 0xCA, 0x00, 0x00, 0x00)

you are creating a C-APDU with the following parameters CLA = 0xFF, INS = 0xCA, P1 = 0x00, P2 = 0x00. So far this is the same as the above APDU. But the last parameter is Ne = 0x00. Ne = 0 means that the number of expected response bytes is zero (whereas Le = 0 would mean that the number of expected response bytes is (up to) 256).

This results in effectively creating the following Case-1 APDU:

+------+------+------+------+
| CLA  | INS  |  P1  |  P2  |
+------+------+------+------+
| 0xFF | 0xCA | 0x00 | 0x00 |
+------+------+------+------+

So at most you will get the 2-byte status word as a response (either indicating success with 0x90 0x00 or indicating an error with a status code like 0x6X 0xXX).

So you can either use a byte array to form your APDU:

new CommandAPDU(new byte[] { (byte)0xFF, (byte)0xCA, (byte)0x00, (byte)0x00, (byte)0x00 } )

Or you can specify a proper value for Ne:

new CommandAPDU(0xFF, 0xCA, 0x00, 0x00, 256)
like image 135
Michael Roland Avatar answered Sep 23 '22 13:09

Michael Roland


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;

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);
            //terminal = terminals.get(i);

            if (readerName.equalsIgnoreCase(" 0")) {
                terminal = terminals.get(i);
            }
        }

        // Establish a connection with the card
        System.out.println("Waiting for a card..");

        if(terminal==null)
            return;
        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


    } 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();
}
}

After read this code For read and write purpose use bellow commands.

And Read From page : 04 to page : 07 Command is:

read_four_to_seven = new byte[]{(byte) 0xFF, (byte) 0x00, (byte) 0x00,
                         (byte) 0x00, (byte) 0x05, (byte) 0x0D4, (byte) 0x40, (byte) 0x01,
                         (byte) 0x30, (byte) 0x04, (byte) 0x07 };
System.out.println("Read : " + send(read_four_to_seven, channel));

Write into Page 04:

Write_Page_Four = new byte[] { (byte) 0xFF, (byte) 0x00, (byte) 0x00,
(byte) 0x00, (byte) 0x15, (byte) 0xD4, (byte) 0x40,
(byte) 0x01, (byte) 0xA0, (byte) 0x04, (byte) 0x4D,
(byte) 0x65, (byte) 0x73, (byte) 0x75, (byte) 0x00,
(byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
(byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
(byte) 0x00, (byte) 0x00, (byte) 0x00 };
System.out.println("Read : " + send(Write_Page_Four, channel));
like image 23
Md. Sajedul Karim Avatar answered Sep 22 '22 13:09

Md. Sajedul Karim