【问题标题】:Get UID of Mifare Ultralight with SCL010使用 SCL010 获取 Mifare Ultralight 的 UID
【发布时间】:2014-02-23 18:31:50
【问题描述】:

我想获取 Mifare Ultralight NFC 标签的 UID。 在 Java 中我有这个代码:

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

问题是我收到两个字节而不是 UID。 有什么问题? APDU 是否正确?

【问题讨论】:

  • 我认为 APDU 不正确。你能告诉你得到的接收字节吗?
  • 这可能是对传递 2 个字节的 REQA 命令的响应(ATQA)吗?

标签: java nfc mifare apdu smartcard-reader


【解决方案1】:

您实际使用的命令可能不是您所期望的。

使用此阅读器获取 UID/序列号/枚举标识符的正确命令 APDU 是:

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

但是,您使用的构造函数定义为:

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

所以

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

您正在使用以下参数创建一个 C-APDU CLA = 0xFFINS = 0xCAP1 = 0x00P2 = 0x00。到目前为止,这与上面的 APDU 相同。但最后一个参数是Ne = 0x00Ne = 0 表示预期响应字节数为零(而 Le = 0 表示预期响应字节数(最多)为 256)。

这会有效地创建以下 Case-1 APDU:

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

所以你最多会得到 2 字节的状态字作为响应(用0x90 0x00 表示成功或用0x6X 0xXX 之类的状态码表示错误)。

所以你可以使用字节数组来形成你的 APDU:

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

或者你可以为Ne指定一个合适的值:

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

【讨论】:

    【解决方案2】:
    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();
    }
    }
    

    阅读此代码后,请使用以下命令进行读写。

    从第 04 页读取到第 07 页命令是:

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

    写入第 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));
    

    【讨论】:

    • 愚蠢的问题,但我怎样才能知道我需要发送哪些数据来读/写?我的意思是我有一个 NXP NTAG216,现在我怎么知道我阅读了哪一页?
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多