• 移动支付之智能IC卡与Android手机进行NFC通信


    本文来自http://blog.csdn.net/hellogv/ 。引用必须注明出处。

           眼下常见的智能IC卡执行着JavaCard虚拟机。智能IC卡上能够执行由精简后的Java语言编写的卡应用(简称Applet)。

    智能IC卡的Applet不能自己启动,必须由外部终端(比如POS机,地铁刷卡终端等)向卡片发送Select命令,由此选中卡片的Applet,Applet才干执行。Appplet側重于数据的处理。没有花销的I/O功能。

    Applet的程序有生命周期和指定入口,当中最基本的几个方法例如以下:

    • public static void install(byte[] bArray, short bOffset, byte bLength)

    构建了Applet子类的实例,JCRE将会最先调用这个;全部的初始化和分配内存的操作应该在这个里面实现;能够获取卡外实体传进来的一些应用初始化參数。

    • public void process(APDU apdu)
    相似于正常java class的main,在安装后,APDU的执行将在这里实现。

    • protected final void register()
    applet用来在JCRE中注冊该applet实例

    • register(byte[] bArray, short bOffset, byte bLength)
    register( )功能一样,添加了能够分配其特定的AID的功能。

    • public boolean select()
            JCRE一旦接收到SELECT[by name]命令时,将寻找命令中指示的AID相应的Applet。使之处于活跃状态,接收并处理接下来的APDU命令。在选择新的Applet前。JCRE先调用当前Applet的 deselect 方法;Applet能够拒绝被选择,此时 select 方法返回false。SELECT[by name]命令本身也将传递给applet处理,此时通过 selectingApplet 用以推断当前状态。

            本文的DEMO执行效果例如以下。包括一个JavaCard的Applet实现和一个Android端的NFC读敲代码,实现智能IC卡与Android手机的简单通信。

    接下来贴段简单的Applet 源代码。下载地址:http://download.csdn.net/detail/hellogv/8090041

    大概的思路是:Applet定义了2个开头标识皆为CMD_CLA的自己定义命令CMD_INS_1和CMD_INS_2,当Android手机通过NFC分别发送CMD_INS_1和CMD_INS_2,Applet分别返回strHello和strWorld。

    核心源代码例如以下:

    public class mytest extends Applet {
    
    	private static final byte[] strHello= { (byte) 'H', (byte) 'e',
    			(byte) 'l', (byte) 'l', (byte) 'o'};
    
    	private static final byte[] strWorld = {(byte) 'W',
    		(byte) 'o', (byte) 'r', (byte) 'l', (byte) 'd', };
    
    	private static final byte CMD_CLA = (byte) 0x80;
    	private static final byte CMD_INS_1 = (byte) 0x10;
    	private static final byte CMD_INS_2 = (byte) 0x20;
    	
    	public static void install(byte[] bArray, short bOffset, byte bLength) {
    		// GP-compliant JavaCard applet registration
    		new mytest().register(bArray, (short) (bOffset + 1), bArray[bOffset]);
    	}
    
    	/*
    	 * 当Java卡Applet被选中时,由JCRE调用。

    Java卡Applet能够定义select()完毕初始化。 * 否则。JCRE调用父类的select()。

    * @see javacard.framework.Applet#select() */ public boolean select() { short debug=100; debug++;//用于断点调试,当被select时触发。 return super.select(); } /* * 当Java卡Applet被放弃时,由JCRE调用。

    Java卡Applet能够定义deselect()完毕清除。 * 否则,JCRE调用父类的deselect()。 * @see javacard.framework.Applet#deselect() */ public void deselect() { short debug=100; debug++;//用于断点调试 super.deselect(); } /* * 每次收到APDU命令。都会执行 * @see javacard.framework.Applet#process(javacard.framework.APDU) */ public void process(APDU apdu) { if (selectingApplet()) { return; } //获取外部终端发过来的数据 byte[] buffer = apdu.getBuffer(); //获取第一位数据 byte CLA = (byte) (buffer[ISO7816.OFFSET_CLA] & 0xFF); //获取第二位数据 byte INS = (byte) (buffer[ISO7816.OFFSET_INS] & 0xFF); if (CLA != CMD_CLA) {//格式不正确 ISOException.throwIt(ISO7816.SW_CLA_NOT_SUPPORTED); } switch (INS) { case CMD_INS_1: sendBytes(apdu,strHello); break; case CMD_INS_2: sendBytes(apdu,strWorld); break; default: ISOException.throwIt(ISO7816.SW_INS_NOT_SUPPORTED); } } private void sendBytes(APDU apdu,byte[] arrays) { byte[] buffer = apdu.getBuffer(); short length = (short) arrays.length; Util.arrayCopyNonAtomic(arrays, (short) 0, buffer, (short) 0, (short) length); apdu.setOutgoingAndSend((short) 0, length); } }


    接下来贴出Android端的核心代码。下载地址:http://download.csdn.net/detail/hellogv/8090053

    大概的思路是:Android端的NFC读敲代码定义1个Applet的ID(AID)。SELECT命令的报文头(SELECT_APDU_HEADER),2个自己定义命令CMD_INS_1和CMD_INS_2。首先使用AID和SELECT_APDU_HEADER生成完整的SELECT命令。transceive(发送)到卡片,用于启动卡片里的AID相应的Applet。启动卡片里的Applet后,NFC读敲代码发送SAMPLE_COMMAND里面的2条自己定义命令,Applet分别返回"Hello""World"。

    核心源代码例如以下:

    public final class CardReader {
        private static final String TAG = "LoyaltyCardReader";
        // AID for our loyalty card service.
        private static final String SAMPLE_CARD_AID = "1122001122";
        // ISO-DEP command HEADER for selecting an AID.
        // Format: [Class | Instruction | Parameter 1 | Parameter 2]
        private static final String SELECT_APDU_HEADER = "00A40400";
        // "OK" status word sent in response to SELECT AID command (0x9000)
        private static final byte[] SELECT_OK_SW = {(byte) 0x90, (byte) 0x00};
        //自己定义的命令
        private static final String[] SAMPLE_COMMAND={"8010000000",//卡片收到后返回"Hello"
        	"8020000000"};//卡片收到后返回"World"
        
    	public static String[][] TECHLISTS;
    	public static IntentFilter[] FILTERS;
    
    	static {
    		try {
    			//the tech lists used to perform matching for dispatching of the ACTION_TECH_DISCOVERED intent
    			TECHLISTS = new String[][] { { IsoDep.class.getName() }};
    
    			FILTERS = new IntentFilter[] { new IntentFilter(
    					NfcAdapter.ACTION_TECH_DISCOVERED, "*/*") };
    		} catch (Exception e) {
    		}
    	}
        
    	static public String tagDiscovered(Tag tag) {
    		Log.e(TAG, "New tag discovered");
    
    		String strResult="";
    		IsoDep isoDep = IsoDep.get(tag);
    		if (isoDep != null) {
    			try {
    				// Connect to the remote NFC device
    				isoDep.connect();
    
    				//发送select 命令,卡片会返回SELECT_OK_SW(90 00)
    				byte[] cmdSelect = BuildSelectApdu(SAMPLE_CARD_AID);
    				Log.e(TAG, "Sending: " + ByteArrayToHexString(cmdSelect));
    				byte[] result = isoDep.transceive(cmdSelect);
    				Log.e(TAG, "Receive: " + ByteArrayToHexString(result));
    				byte[][] response = getResponse(result);
    				byte[] statusWord =response[0];
    
    				if (Arrays.equals(SELECT_OK_SW, statusWord) == false)
    					return "";
    
    				//循环发送自己定义命令
    				for(int i=0;i<SAMPLE_COMMAND.length;i++){
    					String command = SAMPLE_COMMAND[i];
    					result = HexStringToByteArray(command);
    					Log.e(TAG, "Sending: " + command);
    					
    					result = isoDep.transceive(result);
    					Log.e(TAG, "Receive: " + ByteArrayToHexString(result));
    					response = getResponse(result);
    					byte[] body =response[1];
    					
    					strResult=strResult+new String(body)+":"+ByteArrayToHexString(body)+"
    ";
    				}
    				
    
    				return strResult;
    
    			} catch (IOException e) {
    				Log.e(TAG, "Error communicating with card: " + e.toString());
    			}
    		}
    		return null;
    	}
    
    	/***
    	 * 分解卡片返回的数据
    	 * @param b
    	 * @return [0]表示返回的状态值,[1]表示返回的正文
    	 */
    	private static byte[][] getResponse(byte[] b){
    		byte[][] result = new byte[2][];
    		
    		int length = b.length;
    		byte[] status = { b[length - 2],b[length - 1] };
    		
    		byte[] body = Arrays.copyOf(b, length - 2);
    
    		result[0]=status;
    		result[1]=body;
    		return result;
    	}
    	
    	public static String load(Parcelable parcelable) {
    		// 从Parcelable筛选出各类NFC标准数据
    		final Tag tag = (Tag) parcelable;
    		return tagDiscovered(tag);
    	}
    
    
        /**
         * Build APDU for SELECT AID command. This command indicates which service a reader is
         * interested in communicating with. See ISO 7816-4.
         *
         * @param aid Application ID (AID) to select
         * @return APDU for SELECT AID command
         */
        public static byte[] BuildSelectApdu(String aid) {
            // Format: [CLASS | INSTRUCTION | PARAMETER 1 | PARAMETER 2 | LENGTH | DATA]
            return HexStringToByteArray(SELECT_APDU_HEADER + String.format("%02X", aid.length() / 2) + aid);
        }
    
        /**
         * Utility class to convert a byte array to a hexadecimal string.
         *
         * @param bytes Bytes to convert
         * @return String, containing hexadecimal representation.
         */
        public static String ByteArrayToHexString(byte[] bytes) {
            final char[] hexArray = {'0','1','2','3','4','5','6','7','8','9','A','B','C','D','E','F'};
            char[] hexChars = new char[bytes.length * 2];
            int v;
            for ( int j = 0; j < bytes.length; j++ ) {
                v = bytes[j] & 0xFF;
                hexChars[j * 2] = hexArray[v >>> 4];
                hexChars[j * 2 + 1] = hexArray[v & 0x0F];
            }
            return new String(hexChars);
        }
    
        /**
         * Utility class to convert a hexadecimal string to a byte string.
         *
         * <p>Behavior with input strings containing non-hexadecimal characters is undefined.
         *
         * @param s String containing hexadecimal characters to convert
         * @return Byte array generated from input
         */
        public static byte[] HexStringToByteArray(String s) {
            int len = s.length();
            byte[] data = new byte[len / 2];
            for (int i = 0; i < len; i += 2) {
                data[i / 2] = (byte) ((Character.digit(s.charAt(i), 16) << 4)
                        + Character.digit(s.charAt(i+1), 16));
            }
            return data;
        }
    }



  • 相关阅读:
    SpringMVC框架(4)--异常处理
    SpringMVC框架(3)--文件上传与SpringMVC拦截器
    SpringMVC框架(1)--基础入门
    Spring框架(6)--JDBCTemplate的使用和Spring的事务控制
    Spring框架(5)--AOP相关内容
    Spring框架(4)--Spring注解开发
    Spring框架(3)--Spring配置数据源
    Spring框架(2)--依赖注入
    考试倒计时
    递归列表
  • 原文地址:https://www.cnblogs.com/zfyouxi/p/5240927.html
Copyright © 2020-2023  润新知