zoukankan      html  css  js  c++  java
  • 使用Java驱动ACR122U对IC卡进行读写,总结

    1。站在他的肩膀上,快速的看完,动手自己实战了下。对过程写下总结。总历时3.5小时。

    2。手上有一个ACR122U,读卡器。不贵有条件的买一个,毕竟是神器,很好用。

    3。那文中提示的JavaCard文档,和,ACR官方的文档。很重要,是核心内容。

    就像数学中的公式概念,定义。没有它,就没有假设和规范。所以有必要重申下,链接。

    https://docs.oracle.com/javase/7/docs/jre/api/security/smartcardio/spec/javax/smartcardio/package-summary.html

    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();
    	}
    
    }
    
  • 相关阅读:
    12306-单
    12306-票
    12306-车
    12306-人
    12306-目录
    a+=b 是什么意思?
    开启hadoop和Hbase集群的lzo压缩功能(转)
    HBase性能优化方法总结(转)
    Spark向HDFS中存储数据
    Spark从HDFS上读取JSON数据
  • 原文地址:https://www.cnblogs.com/ncepu/p/13694960.html
Copyright © 2011-2022 走看看