zoukankan      html  css  js  c++  java
  • 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。

    核心源码如下:

    1. <span style="font-family:Microsoft YaHei;">public class mytest extends Applet {  
    2.   
    3.     private static final byte[] strHello= { (byte) 'H', (byte) 'e',  
    4.             (byte) 'l', (byte) 'l', (byte) 'o'};  
    5.   
    6.     private static final byte[] strWorld = {(byte) 'W',  
    7.         (byte) 'o', (byte) 'r', (byte) 'l', (byte) 'd', };  
    8.   
    9.     private static final byte CMD_CLA = (byte) 0x80;  
    10.     private static final byte CMD_INS_1 = (byte) 0x10;  
    11.     private static final byte CMD_INS_2 = (byte) 0x20;  
    12.       
    13.     public static void install(byte[] bArray, short bOffset, byte bLength) {  
    14.         // GP-compliant JavaCard applet registration  
    15.         new mytest().register(bArray, (short) (bOffset + 1), bArray[bOffset]);  
    16.     }  
    17.   
    18.     /* 
    19.      * 当Java卡Applet被选中时,由JCRE调用。Java卡Applet可以定义select()完成初始化, 
    20.      * 否则,JCRE调用父类的select()。 
    21.      * @see javacard.framework.Applet#select() 
    22.      */  
    23.     public boolean select() {  
    24.         short debug=100;  
    25.         debug++;//用于断点调试,当被select时触发。  
    26.         return super.select();  
    27.     }  
    28.   
    29.     /* 
    30.      * 当Java卡Applet被放弃时,由JCRE调用。Java卡Applet可以定义deselect()完成清除, 
    31.      * 否则,JCRE调用父类的deselect()。 
    32.      * @see javacard.framework.Applet#deselect() 
    33.      */  
    34.     public void deselect() {  
    35.         short debug=100;  
    36.         debug++;//用于断点调试  
    37.         super.deselect();  
    38.     }  
    39.    
    40.     /* 
    41.      * 每次收到APDU命令,都会执行 
    42.      * @see javacard.framework.Applet#process(javacard.framework.APDU) 
    43.      */  
    44.     public void process(APDU apdu) {  
    45.         if (selectingApplet()) {  
    46.             return;  
    47.         }  
    48.   
    49.         //获取外部终端发过来的数据  
    50.         byte[] buffer = apdu.getBuffer();  
    51.         //获取第一位数据  
    52.         byte CLA = (byte) (buffer[ISO7816.OFFSET_CLA] & 0xFF);  
    53.         //获取第二位数据  
    54.         byte INS = (byte) (buffer[ISO7816.OFFSET_INS] & 0xFF);  
    55.   
    56.         if (CLA != CMD_CLA) {//格式不对  
    57.             ISOException.throwIt(ISO7816.SW_CLA_NOT_SUPPORTED);  
    58.         }  
    59.   
    60.         switch (INS) {  
    61.         case CMD_INS_1:  
    62.             sendBytes(apdu,strHello);  
    63.             break;  
    64.         case CMD_INS_2:  
    65.             sendBytes(apdu,strWorld);  
    66.             break;  
    67.         default:  
    68.             ISOException.throwIt(ISO7816.SW_INS_NOT_SUPPORTED);  
    69.         }  
    70.     }  
    71.   
    72.     private void sendBytes(APDU apdu,byte[] arrays) {  
    73.         byte[] buffer = apdu.getBuffer();  
    74.         short length = (short) arrays.length;  
    75.   
    76.         Util.arrayCopyNonAtomic(arrays, (short) 0, buffer, (short) 0,  
    77.                 (short) length);  
    78.   
    79.         apdu.setOutgoingAndSend((short) 0, length);  
    80.     }  
    81. }</span>  


    接下来贴出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"。

    核心源码如下:

    1. <span style="font-family:Microsoft YaHei;">public final class CardReader {  
    2.     private static final String TAG = "LoyaltyCardReader";  
    3.     // AID for our loyalty card service.  
    4.     private static final String SAMPLE_CARD_AID = "1122001122";  
    5.     // ISO-DEP command HEADER for selecting an AID.  
    6.     // Format: [Class | Instruction | Parameter 1 | Parameter 2]  
    7.     private static final String SELECT_APDU_HEADER = "00A40400";  
    8.     // "OK" status word sent in response to SELECT AID command (0x9000)  
    9.     private static final byte[] SELECT_OK_SW = {(byte) 0x90, (byte) 0x00};  
    10.     //自定义的命令  
    11.     private static final String[] SAMPLE_COMMAND={"8010000000",//卡片收到后返回"Hello"  
    12.         "8020000000"};//卡片收到后返回"World"  
    13.       
    14.     public static String[][] TECHLISTS;  
    15.     public static IntentFilter[] FILTERS;  
    16.   
    17.     static {  
    18.         try {  
    19.             //the tech lists used to perform matching for dispatching of the ACTION_TECH_DISCOVERED intent  
    20.             TECHLISTS = new String[][] { { IsoDep.class.getName() }};  
    21.   
    22.             FILTERS = new IntentFilter[] { new IntentFilter(  
    23.                     NfcAdapter.ACTION_TECH_DISCOVERED, "*/*") };  
    24.         } catch (Exception e) {  
    25.         }  
    26.     }  
    27.       
    28.     static public String tagDiscovered(Tag tag) {  
    29.         Log.e(TAG, "New tag discovered");  
    30.   
    31.         String strResult="";  
    32.         IsoDep isoDep = IsoDep.get(tag);  
    33.         if (isoDep != null) {  
    34.             try {  
    35.                 // Connect to the remote NFC device  
    36.                 isoDep.connect();  
    37.   
    38.                 //发送select 命令,卡片会返回SELECT_OK_SW(90 00)  
    39.                 byte[] cmdSelect = BuildSelectApdu(SAMPLE_CARD_AID);  
    40.                 Log.e(TAG, "Sending: " + ByteArrayToHexString(cmdSelect));  
    41.                 byte[] result = isoDep.transceive(cmdSelect);  
    42.                 Log.e(TAG, "Receive: " + ByteArrayToHexString(result));  
    43.                 byte[][] response = getResponse(result);  
    44.                 byte[] statusWord =response[0];  
    45.   
    46.                 if (Arrays.equals(SELECT_OK_SW, statusWord) == false)  
    47.                     return "";  
    48.   
    49.                 //循环发送自定义命令  
    50.                 for(int i=0;i<SAMPLE_COMMAND.length;i++){  
    51.                     String command = SAMPLE_COMMAND[i];  
    52.                     result = HexStringToByteArray(command);  
    53.                     Log.e(TAG, "Sending: " + command);  
    54.                       
    55.                     result = isoDep.transceive(result);  
    56.                     Log.e(TAG, "Receive: " + ByteArrayToHexString(result));  
    57.                     response = getResponse(result);  
    58.                     byte[] body =response[1];  
    59.                       
    60.                     strResult=strResult+new String(body)+":"+ByteArrayToHexString(body)+" ";  
    61.                 }  
    62.                   
    63.   
    64.                 return strResult;  
    65.   
    66.             } catch (IOException e) {  
    67.                 Log.e(TAG, "Error communicating with card: " + e.toString());  
    68.             }  
    69.         }  
    70.         return null;  
    71.     }  
    72.   
    73.     /***  
    74.      * 分解卡片返回的数据  
    75.      * @param b  
    76.      * @return [0]表示返回的状态值,[1]表示返回的正文  
    77.      */  
    78.     private static byte[][] getResponse(byte[] b){  
    79.         byte[][] result = new byte[2][];  
    80.           
    81.         int length = b.length;  
    82.         byte[] status = { b[length - 2],b[length - 1] };  
    83.           
    84.         byte[] body = Arrays.copyOf(b, length - 2);  
    85.   
    86.         result[0]=status;  
    87.         result[1]=body;  
    88.         return result;  
    89.     }  
    90.       
    91.     public static String load(Parcelable parcelable) {  
    92.         // 从Parcelable筛选出各类NFC标准数据  
    93.         final Tag tag = (Tag) parcelable;  
    94.         return tagDiscovered(tag);  
    95.     }  
    96.   
    97.   
    98.     /** 
    99.      * Build APDU for SELECT AID command. This command indicates which service a reader is 
    100.      * interested in communicating with. See ISO 7816-4. 
    101.      * 
    102.      * @param aid Application ID (AID) to select 
    103.      * @return APDU for SELECT AID command 
    104.      */  
    105.     public static byte[] BuildSelectApdu(String aid) {  
    106.         // Format: [CLASS | INSTRUCTION | PARAMETER 1 | PARAMETER 2 | LENGTH | DATA]  
    107.         return HexStringToByteArray(SELECT_APDU_HEADER + String.format("%02X", aid.length() / 2) + aid);  
    108.     }  
    109.   
    110.     /** 
    111.      * Utility class to convert a byte array to a hexadecimal string. 
    112.      * 
    113.      * @param bytes Bytes to convert 
    114.      * @return String, containing hexadecimal representation. 
    115.      */  
    116.     public static String ByteArrayToHexString(byte[] bytes) {  
    117.         final char[] hexArray = {'0','1','2','3','4','5','6','7','8','9','A','B','C','D','E','F'};  
    118.         char[] hexChars = new char[bytes.length * 2];  
    119.         int v;  
    120.         for ( int j = 0; j < bytes.length; j++ ) {  
    121.             v = bytes[j] & 0xFF;  
    122.             hexChars[j * 2] = hexArray[v >>> 4];  
    123.             hexChars[j * 2 + 1] = hexArray[v & 0x0F];  
    124.         }  
    125.         return new String(hexChars);  
    126.     }  
    127.   
    128.     /** 
    129.      * Utility class to convert a hexadecimal string to a byte string. 
    130.      * 
    131.      * <p>Behavior with input strings containing non-hexadecimal characters is undefined. 
    132.      * 
    133.      * @param s String containing hexadecimal characters to convert 
    134.      * @return Byte array generated from input 
    135.      */  
    136.     public static byte[] HexStringToByteArray(String s) {  
    137.         int len = s.length();  
    138.         byte[] data = new byte[len / 2];  
    139.         for (int i = 0; i < len; i += 2) {  
    140.             data[i / 2] = (byte) ((Character.digit(s.charAt(i), 16) << 4)  
    141.                     + Character.digit(s.charAt(i+1), 16));  
    142.         }  
    143.         return data;  
    144.     }  
    145. }</span>  
  • 相关阅读:
    AES密码算法详解(转自https://www.cnblogs.com/luop/p/4334160.html)
    快速排序和插入排序——我的代码
    北京大学1001ACM——高精度类型题总结
    C语言数组不知道输入几个整数以及输入一直到为0
    C语言并查集例子——图问题巧用parent[]数组
    C语言快速判断素数——不超时
    C语言如何才能使用bool类型
    C语言两个特别大的整数类型相加超出范围使用两个技巧
    C语言存30位数字长的十进制方法
    dockerfile相关命令
  • 原文地址:https://www.cnblogs.com/Alanturing/p/5007866.html
Copyright © 2011-2022 走看看