使用Java驅動ACR122U對IC卡進行讀寫,總結
阿新 • • 發佈:2018-11-02
1。站在他的肩膀上,快速的看完,動手自己實戰了下。對過程寫下總結。總歷時3.5小時。
2。手上有一個ACR122U,讀卡器。不貴有條件的買一個,畢竟是神器,很好用。
3。那文中提示的JavaCard文件,和,ACR官方的文件。很重要,是核心內容。
就像數學中的公式概念,定義。沒有它,就沒有假設和規範。所以有必要重申下,連結。
https://www.acs.com.hk/download-manual/933/API-ACR122U-CN-2.04.pdf
4。首先按rtz的原文,動手。我附上原始碼,且做了比較的註解。
原始碼中,的思路要說下的。即大綱,首先是TerminalFactory找裝置讀卡器,放到一個List裡,一般接一個。
CardTerminal在讀卡器列表中找第一個。並將讀卡器,處於工作連線狀態,即等你放卡狀態。一放卡,就讀卡號,打印出來。
接著做,載入認證金鑰,即將金鑰臨時存放在讀卡器上,至於為什麼,你想讀一個扇區,它有總共4塊,每一塊都要認證,不可能每次都載入金鑰,存放在讀卡器上,就方便多了,甚至別的扇區和塊,如果密碼一樣,也可以直接用。接下來,認證,就是從將拿存放在讀卡器上的金鑰對某個塊進行認證,通過就,可以讀寫那個塊的資料了。
5。有機會,可以用java的圖形開發,實現。
package com.ddzh.acr; import java.util.List; import javax.smartcardio.Card; import javax.smartcardio.CardChannel; import javax.smartcardio.CardTerminal; import javax.smartcardio.CommandAPDU; import javax.smartcardio.ResponseAPDU; import javax.smartcardio.TerminalFactory; /** * 描 述: <描述>.acr.java * * @author guolp * @since 1.0, 2018-10-24 09:20:16 */ public class acr { /** * <一句話描述該方法的功能> * * @param args * @since 1.0, 2018-10-24 09:20:19 */ public static void main(String[] args) { // TODO Auto-generated method stub TerminalFactory factory = TerminalFactory.getDefault();// 得到一個預設的讀卡器工廠(迷。。) List<CardTerminal> terminals;// 建立一個List用來放讀卡器(誰沒事會在電腦上插三四個讀卡器。。) try { terminals = factory.terminals().list();// 從工廠獲得插在電腦上的讀卡器列表,get讀卡器列表 terminals.stream().forEach(s -> System.out.println(s));// 列印獲取到的讀卡器名稱 CardTerminal a = terminals.get(0);// 使用第0個讀卡器[暫且不考慮同時插N個讀卡器的情況了] a.waitForCardPresent(0L);// 等待放置卡片 Card card = a.connect("T=1");// 連線卡片,協議T=1 塊讀寫(T=0貌似不支援,一用就報錯) CardChannel channel = card.getBasicChannel();// 開啟通道 CommandAPDU getUID = new CommandAPDU(0xFF, 0xCA, 0x00, 0x00, 0x04);// 中文API第12頁 ResponseAPDU r = channel.transmit(getUID);// 傳送getUID指令 System.out.println("UID: " + r.toString());// 返回:UID: ResponseAPDU: 6 bytes, SW=9000 System.out.println("Data:" + bytesToHexString(r.getData())); // 即返回卡號,操作成功 // 載入認證金鑰,放在讀卡器的EEPROM byte[] pwd = { (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff };// 先用一個數組把金鑰存起來 CommandAPDU loadPWD = new CommandAPDU(0xFF, 0x82, 0x00, 0x00, pwd, 0, 6);// 構造載入認證金鑰APDU指令 ,中文API第13頁 ResponseAPDU r1 = channel.transmit(loadPWD);// 傳送loadPWD指令 System.out.println("載入認證金鑰: " + r1.toString()); // 認證金鑰,驗證通過後,讀取同一扇區的其他塊不需要再次驗證,中文API原文 byte[] check = { (byte) 0x01, (byte) 0x00, (byte) 0x0B, (byte) 0x60, (byte) 0x00 }; // 0x01認證版本,0x00空閒,0x08認證區塊號,0x60金鑰型別A/0x61金鑰型別B,0x00金鑰儲存的地址(金鑰號) CommandAPDU authPWD = new CommandAPDU(0xFF, 0x86, 0x00, 0x00, check, 0, 5);// 加上指令頭部,構造出完整的認證APDU指令,中文API第14頁 ResponseAPDU r2 = channel.transmit(authPWD);// 傳送 認證指令 System.out.println("認證第8區塊:" + r2.toString()); // 讀區塊2 CommandAPDU getData8 = new CommandAPDU(0xFF, 0xB0, 0x00, 0x08, 0x10);// 構造 讀區塊APDU指令,中文API第17頁 ResponseAPDU r8 = channel.transmit(getData8);// 傳送 讀區塊指令 System.out.println("第8個區塊,Data:" + bytesToHexString(r8.getData())); CommandAPDU getData9 = new CommandAPDU(0xFF, 0xB0, 0x00, 0x09, 0x10);// 構造 讀區塊APDU指令 ResponseAPDU r9 = channel.transmit(getData9);// 傳送 讀區塊指令 System.out.println("第9個區塊,Data:" + bytesToHexString(r9.getData())); CommandAPDU getData10 = new CommandAPDU(0xFF, 0xB0, 0x00, 0x0A, 0x10);// 構造 讀區塊APDU指令 ResponseAPDU r10 = channel.transmit(getData10);// 傳送 讀區塊指令 System.out.println("第10個區塊,Data:" + bytesToHexString(r10.getData())); CommandAPDU getData11 = new CommandAPDU(0xFF, 0xB0, 0x00, 0x0B, 0x10);// 構造 讀區塊APDU指令 ResponseAPDU r11 = channel.transmit(getData11);// 傳送 讀區塊指令 System.out.println("第11個區塊,Data:" + bytesToHexString(r11.getData())); // 寫區塊 byte[] up = { (byte) 0x00, (byte) 0x01, (byte) 0x02, (byte) 0x03, (byte) 0x04, (byte) 0x05, (byte) 0x06, (byte) 0x07, (byte) 0x08, (byte) 0x09, (byte) 0x0A, (byte) 0x0B, (byte) 0x0C, (byte) 0x0D, (byte) 0x0E, (byte) 0x0F };// 0x 構造要寫入的資料,有16位 CommandAPDU upData = new CommandAPDU(0xFF, 0xD6, 0x00, 0x08, up, 0, 16);//構造 寫區塊APDU指令,中文API第18頁 ResponseAPDU r4 = channel.transmit(upData);// 傳送寫塊指令 System.out.println("寫區塊: " + r4.toString()); System.out.println("寫區塊: " + bytesToHexString(r4.getData()) + ":#:" + bytesToHexString(r4.getBytes()));// 列印返回值 } catch (Exception e) { e.printStackTrace(); } } /** * <一句話描述該方法的功能> * * @param data * @return * @author guolp * @since 1.0, 2018-10-24 09:28:48 */ private static final char[] HEX_CHAR = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f' }; private static String bytesToHexString(byte[] bytes) { // TODO Auto-generated method stub StringBuilder sb = new StringBuilder(); int a = 0; for (byte b : bytes) { // 使用除與取餘進行轉換 if (b < 0) { a = 256 + b; } else { a = b; } // sb.append("0x"); sb.append(HEX_CHAR[a / 16]); sb.append(HEX_CHAR[a % 16]); // sb.append(" "); } return sb.toString().toUpperCase(); } }