【问题标题】:Write Data to NFC smartcard in Java在 Java 中将数据写入 NFC 智能卡
【发布时间】:2020-12-15 17:22:24
【问题描述】:

我被要求编写一个简单的程序来使用 NFC 从智能卡 (MIFARE) 读取和写入数据,但我被卡住了。

我必须获得 UID(到目前为止一切顺利),将一些从数据库中获取的数据写入卡的某些块中(没办法)。

我必须使用 Java 和 ACR122 阅读器。我写的代码仍然需要改进和重构,这主要是一个草稿,但我想先以某种方式完成任务,然后修复所有问题。我阅读了我在互联网上可以找到的内容,但我仍然想念一些东西。到目前为止我得到的(主要是把一些代码放在一起)是:

    package testnfc;
    import javax.smartcardio.*;
    import java.util.Arrays;
    import java.util.List;
    import static testnfc.Helpers.*;
    import java.nio.ByteBuffer;

    public class TestNFC {

    `enter code here`public static void main(String[] args) throws CardException {
    // get and print any card readers (terminals)
    TerminalFactory factory = TerminalFactory.getDefault();
    List<CardTerminal> terminals=null;
    try{
        
         terminals = factory.terminals().list();
        System.out.println("Terminals: " + terminals);
        
         // work with the first terminal
        CardTerminal term = terminals.get(0);

        // connect with the card. Throw an exception if a card isn't present 
        // the * means use any available protocol
        try{
            
            Card card = term.connect("*");
            System.out.println("card: " + card);
            //Got the card
            CardChannel channel = card.getBasicChannel();

            byte[] instruction = hexToBytes("FF CA 00 00 00");
            CommandAPDU getUID = new CommandAPDU(instruction);

            ResponseAPDU response = channel.transmit(getUID);
            String uid = bytesToPrettyHex(response.getData());
            String status = bytesToPrettyHex(new byte[] {(byte)response.getSW1(), (byte)response.getSW2()});
            System.out.printf("UID: %s\tResponse: %s\n", uid, status);  //Status = 90 -> Success, 63 -> Fail
            
            //Up to this point it works, I can get the terminal and the UID of the card. Now I am trying to write datas in the card
            byte[] dati;
            dati=new byte[4];
            dati[0]=(byte)12;
            dati[1]=(byte)12;
            dati[2]=(byte)12;
            writeData(card, (byte)1, dati);
        }
        catch(CardNotPresentException exc){
            System.out.println("Card not found!");
        }
    }
    catch(CardException ex){
        System.out.println("Terminal not found!");
    }
    
}

public static void writeData(Card c, byte block, byte[] data)
        throws CardException {
    byte cla = (byte) 0xFF;
    byte ins = (byte) 0xD6;
    byte p1 = (byte) 0x00;
    byte p2 = block;
    byte le = 0x10;
    byte[] params = new byte[21];
    for (int i = 0; i < 21; i++) {
        params[i] = 0x20;
    }
    params[0] = cla;
    params[1] = ins;
    params[2] = p1;
    params[3] = p2;
    params[4] = le;
    for (int i = 0; i < data.length; i++) {
        params[5 + i] = data[i];
    }
    
    System.out.println("step1");

    CardChannel channel = c.getBasicChannel();
    CommandAPDU command = new CommandAPDU(params);
    
    System.out.println("Step2");

    ResponseAPDU response = channel.transmit(command);
    System.out.println("Step3, response ->"+ response);
    validateResponse(response);
    System.out.println("Step4");
}

private static void validateResponse(ResponseAPDU response)
        throws CardException {
        int respSW1=0;
        int respSW2=0;
        respSW1 = response.getSW1();
        respSW2 = response.getSW2();
        System.out.println("SW1 ->"+respSW1 + ", SW2 ->"+ respSW2);
        if (respSW1 != 144) {
            throw new CardException("Autentication Problem?");
        }
    }
}    

我得到的输出是

Terminals: [PC/SC terminal ACS ACR122 0]
card: PC/SC card in ACS ACR122 0, protocol T=1, state OK
UID: EA:54:42:AA    Response: 90:00
step 1
step2
step3, response ->ResponseAPDU: 2 bytes, SW=6300
SW1 ->99, SW2 ->0
Terminal not found

我可以读取卡的 UID,但我不能写入;卡本身没问题,使用桌面的 NFC 工具我可以访问和写入。我查找了一些文档,但我无法解决问题,如果没有一些示例,我将无法正确学习。我不明白为什么我得到一个 6300 代码:从我发现它的意思是“非易失性内存的状态已更改”,但如果我使用 NFC 工具检查,我找不到任何区别。

我必须更改哪些内容才能从卡中写入和读取?

【问题讨论】:

  • 真的需要知道 NFC 卡的型号 Mifare Classic、Mifare Ultralight、Mifare Desfire 等,因为这些因素会改变要使用的命令。
  • 感谢您的回复@Andrew。我不太清楚,客户给我玩了一些智能卡并告诉我“这些是卡”,没有蚂蚁盒子或蚂蚁其他信息。我想是 Mifare Classic,有什么命令可以找到吗?
  • 来自 nxp play.google.com/store/apps/… 的 Android App TagInfo 或 iOS 版本会告诉您型号(IC 类型)

标签: java nfc smartcardio


【解决方案1】:

根据 NFC 工具,它是 Mifare Classic 1kb

【讨论】:

    【解决方案2】:

    我解决了这个问题。这是我的代码(概念证明,只是为了展示它是如何工作的)

    package testnfc;
    
    import javax.smartcardio.*;
    import java.util.List;
    import java.io.*;
    import static testnfc.Helpers.*;
    import java.lang.Integer;
    
    import java.nio.charset.StandardCharsets;
    
    public class TestNFC {
        public static void main(String[] args) throws CardException {
            // get and print any card readers (terminals)
            TerminalFactory factory = TerminalFactory.getDefault();
            List<CardTerminal> terminals=null;
            try{
                
                 terminals = factory.terminals().list();
                System.out.println("Terminals: " + terminals);
                
                 // work with the first terminal
                CardTerminal term = terminals.get(0);
    
                // connect with the card. Throw an exception if a card isn't present 
                // the * means use any available protocol
                try{
                    Card card = term.connect("*");
                    System.out.println("card: " + card);
    
                    // Once we have the card, we can open a communication channel for sending commands and getting responses
                    CardChannel channel = card.getBasicChannel();
             
                    byte[] dati;
                    String valori=[data to be written];
                    dati=valori.getBytes();
                    System.out.println("valori ->" + valori + ", dati ->"+dati);
                    
                    //Not used right now
                    //loadAuthentication(card);
                    
                    byte addr = 0x06;
                    
                    authenticate(card, addr);
                    
                    writeData(card, addr, dati);
                    
                    readData(card, addr); 
    
                }
                catch(CardNotPresentException exc){
                    System.out.println("Card not found!");
                    stampTrace(exc);
                }
            }
            catch(CardException ex){
                System.out.println("Terminal not found");
                stampTrace(ex);
            }
            
        }
        
        /*
        * Prints stack trace (for debug)
        */
        public static String stampTrace(Exception ex){
            StringWriter errors = new StringWriter();
            ex.printStackTrace(new PrintWriter (errors));
            
            String ret=errors.toString();
            System.out.println(ret);
            return ret;
        }
        
        /*
        * Funzioni per convertire in byte, per passare i parametri alle funzionin;
        */
        public byte fromIntToByte(int i){
            Integer v=i;
            byte b=v.byteValue();
            return b;
        }
        public byte[] fromStringToByte(String s){
            byte[] byteArray=s.getBytes();
            return byteArray;
        }
    
        public static void authenticate(Card c, byte addr) throws CardException {
            byte cls = (byte) 0xFF;
            byte ins = (byte) 0x86;
            byte p1 = (byte) 0x00;
            byte p2 = (byte) 0x00; 
            byte lc = (byte) 0x05;
    
            byte[] params = new byte[] { cls, ins, p1, p2, lc, 0x01, 0x00, addr, 0x60, 0x00 };// 0x60 -> Key A per auth, 0x61 -> key B
      
            CardChannel channel = c.getBasicChannel();
            CommandAPDU command = new CommandAPDU(params);
            System.out.println("Tryint to authenticate.");
            ResponseAPDU response = channel.transmit(command);
            validateResponse(response);
            System.out.println("Autenticated!");
        }
        
        /*
        * Load new auth key in the card. Not used at the moment
        */
        public static void loadAuthentication(Card c) throws CardException {
            byte cla = (byte) 0xFF;
            byte ins = (byte) 0x82;
            byte p1 = (byte) 0x00; 
            byte p2 = (byte) 0x01;
            byte lc = (byte) 0x06;
            byte key = (byte) 0xFF;
    
            byte[] params = new byte[] { cla, ins, p1, p2, lc, key, key, key,
                    key, key, key };
            CardChannel channel = c.getBasicChannel();
            CommandAPDU command = new CommandAPDU(params);
            ResponseAPDU response = channel.transmit(command);
            validateResponse(response);
            System.out.println("Auth OK!");
        }
        
        /*
        * Reads from card
        * @param c card to be read
        * @param block  block I want to read
        */
        public static byte[] readData(Card c, byte block) throws CardException {
            byte cla = (byte) 0xFF;
            byte ins = (byte) 0xB0;
            byte p1 = (byte) 0x00;
            byte p2 = block;
            byte le = (byte) 0x10;
    
            byte[] params = new byte[] { cla, ins, p1, p2, le };
            CardChannel channel = c.getBasicChannel();
            CommandAPDU command = new CommandAPDU(params);
            System.out.println("Trying to read");
            ResponseAPDU response = channel.transmit(command);
            System.out.println("Read, response ->"+response);
            
            String ret=new String(response.getData(), StandardCharsets.UTF_8);
            System.out.println("getData ->"+response.getData()+", string ->"+ret);
            validateResponse(response);
            return response.getData();
        }   
        
        /*
        * Writes data into card
        * @param c      card to be written
        * @param block  block I want to write in
        * @param data   what I want to write
        */
        public static void writeData(Card c, byte block, byte[] data)
                throws CardException {
            byte cla = (byte) 0xFF; 
            byte ins = (byte) 0xD6; 
            byte p1 = (byte) 0x00;
            byte p2 = (byte) block;
            byte le = 0x10;
            byte[] params = new byte[21];
            for (int i = 0; i < 21; i++) {
                params[i] = 0x20;
            }
            params[0] = cla;
            params[1] = ins;
            params[2] = p1;
            params[3] = p2;
            params[4] = le;
            for (int i = 0; i < data.length; i++) {
                params[5 + i] = data[i];
                System.out.println("i ->"+ String.valueOf(i)+", dato ->"+params[5+i]);
            }
            
            CardChannel channel = c.getBasicChannel();
            CommandAPDU command = new CommandAPDU(params);
    
            ResponseAPDU response = channel.transmit(command);
            System.out.println("step3, response ->"+ response);
            
            validateResponse(response);
            System.out.println("step4");
        }
    
        /*
        * Checks the response 
        */
        private static void validateResponse(ResponseAPDU response)
                throws CardException {
            int respSW1=0;
            int respSW2=0;
            respSW1 = response.getSW1();
            respSW2 = response.getSW2();
            System.out.println("SW1 ->"+respSW1 + ", SW2 ->"+ respSW2);
            //TODO Fix message!!
            if (respSW1 != 144) {
                throw new CardException("Autentication Problem?");
            }
        }
    }
    

    【讨论】:

      【解决方案3】:

      当您知道它是什么卡后,您可以查看数据表https://www.nxp.com/docs/en/data-sheet/MF1S70YYX_V1.pdf,了解它的内存结构以及它支持哪些命令。

      然后如何从阅读器的 PICC 命令中将它们包装到伪 APDU 中(https://www.acs.com.hk/download-manual/419/API-ACR122U-2.04.pdf 的第 5 节)

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2022-11-12
        • 2019-10-19
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多