移动支付之智能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)+"\r\n";
				}

				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;
    }
}
时间: 2024-10-29 02:37:05

移动支付之智能IC卡与Android手机进行NFC通信的相关文章

&quot;软掩膜&quot;和“硬掩膜”-智能IC卡

目录 一.“软掩膜”和“硬掩膜”... 2 二.EMV迁移进程... 3 三.PBOC规范和EMV规范对比... 3 四.总结... 5 五.关于SDA和DDA. 6 一.“软掩膜”和“硬掩膜” “软掩膜”和“硬掩膜”的术语常被用在现场试验和智能卡操作系统方面.严格地说,从纯逻辑的观点来 看这两个术语都是没有意义的,因为所谓ROM掩膜就意味着位于ROM里的程序代码总是不变的因而是“硬”的 .然而,在智能卡世界的常用行话里,术语“软掩膜”只表示一些类似于掩膜的东西,当智能卡操作系统的部分或全部程序

智能IC卡中的文件系统

1.文件系统是COS的重要模块之一,它负责组织.管理.维护IC卡内存储的所有数据. 文件系统的设计和实现既是COS系统中最灵活.最有个性的部分,也是对系统整体结构影响最大的模块之一. 2.在IC卡内,数据的管理和维护一般通过文件系统来进行.和PC系统类似,COS的文件系统要实现的主要功能包括: * 空间的管理维护.   * 数据的有效存储.   * 数据的查找和定位.   * 数据的修改和维护. 3. 在COS系统中,文件系统是一个承上启下的模块,对底层来说实现了存储空间的管理维护,对上层完全透

智能IC卡与终端(读卡器)之间的传输协议

1.有两种协议 T=0,异步半双工字符传输协议 T=1,异步半双工块传输协议 终端一般都支持这两种协议,IC卡可以选择支持其中的一种.(因为终端可能需要面对各种类型的卡片,所以必须两种协议都支持,而卡片则不然了) 2.如何判断卡片支持哪种传输协议? 在ATR值得TD1字段中规定了后续传输中所采用的传输协议,T=0或T=1,如果ATR中不存在ATR的话,则假定采用T=0 如果在ATR之后卡片与终端之间没有参数协商的PTS过程的话(详细细节可参考ISO7816相关部分),由IC卡指定的协议将在复位应

WIFI环境下Android手机和电脑通信

前面已经写过一篇java实现最基础的socket网络通信,这篇和之前那篇大同小异,只是将客户端代码移植到手机中,然后获取本机IP的方法略有不同. 先讲一下本篇中用到Android studio的使用吧 使用Android studio开发Android最基本的3个步骤: (1)新建工程 (2)在XML布局文件中定义应用程序的用户界面. 点击画圈中的Design和Text可以切换界面 (3)在java代码中编写业务实现. 这里面用来写java代码 另外这篇文章还需要添加用户权限 好了,可以上代码了

IC卡模块之充电桩发展前景

IC卡模块之充电桩发展前景 自2014年以来,国家相继出台了多种政策措施扶持新能源汽车和充电设施的发展,随着各级政府对充电设施建设投入加大,充电设施市场即将迎来大爆发时期. 按照<电动汽车充电基础设施发展指南>(2015-2020),做好配电网规划与充换电设施规划的衔接,加强充换电设施配套电网建设与改造,保障充换电设施无障碍接入.2020年满足1.2万座充换电站.480万台充电桩接入需求,为500万辆电动汽车提供充换电服务.按照计划,能源局将在五年内做好配电网规划与充换电设施规划的衔接,加强充

教您选购峰华手持IC卡读写机

教您选购峰华手持IC卡读写机:F8手持IC卡读写机采用32位ARM 9 内核微处理器,具备性能高.成本低.扩展性强等特点:使用Wince操作系统,具备多任务功能,提供了先进的维护和管理功能,极大地提高了应用程序的维护和开发效率,确保应用程序的稳定性.支持读写RFID非接触式IC卡.接触式IC卡.PSAM卡等多种交易卡:支持一维条形码扫描.GPRS以及WIFI选配功能. 一查看配置: 1.处理平台 ARM9处理器.主频最高200MHZ 2.存储器 SDRAM:64MB.NandFlash:64MB

HCE对金融、交通、通讯、智能卡行业的巨大影响 电子虚拟智能卡 Android version4.4 NFC手机

HCE模式,可以使得软件模拟智能卡,产生电子虚拟智能卡. HCE(Host-based Card Emulation)是一个模拟智能卡的软件,在Android中以软件形式存在(例如:apk).HCE的特点是模拟智能IC卡(ISO 7816-4),可用于金融.交通.通讯等行业应用,HCE是软件模拟的智能IC卡,所以也会有AID,可以模拟现有实体卡片的功能. 对金融.交通.通讯.智能卡等行业产生巨大影响,就如同电子信息和互联网技术的发展对传统媒体(纸质报纸.实体唱片)等的影响一样. HCE是基于NF

信用卡智能养卡系统app源码开发运营与贴牌oem思路模式

1.我的需求和我想要的模式 我们脑海中的这个系统是长什么样的,一个系统的UI就像他的脸一样,既然我们做平台,那肯定是要有用户来使用我们的产品,那么这个用户体验度就很重要了,而这个UI界面就是用户打开这个app的第一印象,如果UI设计的好,也能提升用户的粘性,让用户愿意使用你的app.2.APP开发团队的选择 很多养卡软件应用软件开发公司内部成员包括设计师.技术人员等,都是一些没有经验或者缺乏经验的,那这样效果自然就不会太理想;另外一些专业的帮你还APP应用软件开发公司,会聘请一些专业性比较强的人

电感耦合非接触IC卡系统的EMI问题

射频识别(RFID)技术近年来发展迅速,并获得了广泛应用.但作为一种无线射频技术,其电磁兼容(EMC)性能也越来越受到人们的关注.RFID涉及的频率范围甚广,包括低于135kHz.13.56MHz.433MHz.860-960MHz.2.45GHz.5.8GHz等多个频段.本文仅就低于135kHz和13.56MHz两个频段的电感耦合非接触RFID卡的电磁干扰(EMI)问题结合相关国际标准进行介绍和剖析.        电子产品的电磁兼容性        电子产品的电磁兼容性EMC包含两个方面:一