NXP

HCE技术实战分析

2019-07-12 11:57发布

       Google开始放出HCE的支持后,掀起了巨大的反响。 摆脱了SWP-SIM对运营商的依赖,摆脱了eSE对手机厂商的依赖,其对于产业生态的影响不言而喻。网上基于HCE的前景和应用讨论的不亦乐乎,但技术层面的资料却乏善可陈。现我们就通过一个具体的实现来揭秘HCE,同时给出一个技术层面的理解。       首先,需要的环境:         一个支持Android 4.4.2 SDK的开发环境:可以是eclipse插件或者ADT工具。可以到google的网站上去下载:http://developer.android.com/sdk/index.html 。 (此处攻略漫天飞,搞不定的同学自己搜)         一个支持HCE的测试手机:目前可以确定使用了NXP PN547作为CLF的NFC手机已经打通了HCE。市面上可见的目前有Sony Xperia Z2 和 Samsung Galaxy S5。本次测试使用Xperia Z2。(CLF芯片不包含在手机发布的资料里,最终确定Z2的CLF芯片还是依靠网上流出的拆机图)         一份参考资料:http://developer.android.com/guide/topics/connectivity/nfc/hce.html 里面写的非常细致,我摘要一下:         1. HCE工作在ISO 7816-4也就是ISO-DEP层面。(想模拟Mifare标签的同学洗洗睡吧)         2.命令的派发是基于系统接管SelectbyName指令,以及HCE服务注册到系统的AID来完成的。HCE的派发高于SE,不支持logic channel,也不支持GP的AID部分匹配。         3.HCE以Android服务的方式启动,通过接口函数响应APDU。可以配置多个AID(AID Group),可以配置类型--支付类或者其它类,该类型用于AID冲突时系统的策略。支付类是通过设置默认应用,而其它类则是UI弹出选择提示。         4.屏幕关闭的状态下HCE不可用,屏幕锁定状态下HCE可以选择支持,也可以选择提示用户解锁再支持。         5.在终端只有HCE没有SE的情况下,ISO 14443-3的非接参数由Android接管,UID使用随机数,请勿使用HCE实现任何基于UID的ID卡。         6.Open Mobile API并不能向访问SE一样访问HCE。     然后,我们开始编码了,首先实现Service: [java] view plaincopyprint?在CODE上查看代码片派生到我的代码片
  1. package com.broadthinking.hcedemo;  
  2.   
  3. import android.nfc.cardemulation.HostApduService;  
  4. import android.os.Bundle;  
  5. import android.util.Log;  
  6.   
  7. public class MyHostApduService extends HostApduService {  
  8.   
  9.     private int messageCounter = 0;  
  10.   
  11.     @Override  
  12.     public byte[] processCommandApdu(byte[] apdu, Bundle extras) {  
  13.         if (selectAidApdu(apdu)) {  
  14.             Log.i("HCEDEMO""Application selected");  
  15.             return getWelcomeMessage();  
  16.         }  
  17.         else {  
  18.             Log.i("HCEDEMO""Received: " + new String(apdu));  
  19.             return getNextMessage();  
  20.         }  
  21.     }  
  22.   
  23.     private byte[] getWelcomeMessage() {  
  24.         return "Hello Desktop!".getBytes();  
  25.     }  
  26.   
  27.     private byte[] getNextMessage() {  
  28.         return ("Message from android: " + messageCounter++).getBytes();  
  29.     }  
  30.   
  31.     private boolean selectAidApdu(byte[] apdu) {  
  32.         return apdu.length >= 2 && apdu[0] == (byte)0 && apdu[1] == (byte)0xa4;  
  33.     }  
  34.   
  35.     @Override  
  36.     public void onDeactivated(int reason) {  
  37.         Log.i("HCEDEMO""Deactivated: " + reason);  
  38.     }  
  39. }  

其中:processCommandApdu用以重载传入接收到的CAPDU,函数返回值作为RAPDU。但此接口的调用使用程序主栈,如果处理时间较长(比如基于云的处理),则需要启动处理线程,并返回null,并在处理线程结束后主动调用sendResponseApdu来发送RAPDU。     继续配置Android环境 AndroidManifest.xml: [html] view plaincopyprint?在CODE上查看代码片派生到我的代码片
  1. xml version="1.0" encoding="utf-8"?>  
  2. <manifest xmlns:android="http://schemas.android.com/apk/res/android"  
  3.     package="com.broadthinking.hcedemo"  
  4.     android:versionCode="1"  
  5.     android:versionName="1.0" >  
  6.   
  7.     <uses-sdk  
  8.         android:minSdkVersion="19"  
  9.         android:targetSdkVersion="19" />  
  10.   
  11.     <uses-permission android:name="android.permission.NFC" />  
  12.   
  13.   
  14.     <application  
  15.         android:allowBackup="true"  
  16.         android:icon="@drawable/ic_launcher"  
  17.         android:label="@string/app_name"  
  18.         android:theme="@style/AppTheme" >  
  19.         <activity  
  20.             android:name="com.broadthinking.hcedemo.MainActivity"  
  21.             android:label="@string/app_name" >  
  22.             <intent-filter>  
  23.                 <action android:name="android.intent.action.MAIN" />  
  24.   
  25.                 <category android:name="android.intent.category.LAUNCHER" />  
  26.             intent-filter>  
  27.         activity>  
  28.   
  29.         <service  
  30.             android:name=".MyHostApduService"  
  31.             android:exported="true"  
  32.             android:permission="android.permission.BIND_NFC_SERVICE" >  
  33.             <intent-filter>  
  34.                 <action android:name="android.nfc.cardemulation.action.HOST_APDU_SERVICE" />  
  35.             intent-filter>  
  36.   
  37.             <meta-data  
  38.                 android:name="android.nfc.cardemulation.host_apdu_service"  
  39.                 android:resource="@xml/apduservice" />  
  40.         service>  
  41.     application>  
  42.   
  43. manifest>  
   几个关键点:         用户授权:android.permission.NFC
        服务授权:android.permission.BIND_NFC_SERVICE
        exported:必须为true
        initent-filter:android.nfc.cardemulation.action.HOST_APDU_SERVICE
        meta-data:指定服务的细节,见apduservice.xml [html] view plaincopyprint?在CODE上查看代码片派生到我的代码片
  1. <host-apdu-service xmlns:android="http://schemas.android.com/apk/res/android"  
  2.     android:description="servicedesc"  
  3.     android:requireDeviceUnlock="false" >  
  4.   
  5.     <aid-group  
  6.         android:category="other"  
  7.         android:description="aiddescription" >  
  8.         <aid-filter android:name="F0010203040506" />  
  9.         <aid-filter android:name="F0394148148100" />  
  10.     aid-group>  
  11.   
  12. host-apdu-service>  
  几个关键点:           可以通过指定多个aid-filter,来注册多个AID。
          category可以指定为 other 或者 payment。
          requireDeviceUnlock为false的时候,可以在锁屏状态下完成处理,为true的时候,则会要求用户解锁屏幕。
 开始测试:   把应用下载到手机中,保持屏幕打开状态,将手机放入非接触读卡器,发送APDU:00A4040007F0010203040506 或者 00A4040007F0394148148100, 将会得到反馈:48656C6C6F204465736B746F7021,解码一看,正是“Hello Desktop!”的ASCII值,应用的选择成功。继续发送一条APDU 00010000,将会得到反馈:4D6573736167652066726F6D20616E64726F69643A2030,解码一看,正是“Message from android: 0”的ASCII值,可以看到命令的派发成功!
  喷几句:   结合实践,我们看到HCE技术支持提供了一个软实现SE的通路,Service实现的方式很多,可以使用文件,使用网络,甚至连接真正的SE。具体的实现方案还是要依赖于具体的业务需求,网上热炒的云方案只是其中一种。基于云的方式我个人并不看好,从安全的角度:云的方式固然保证了数据安全,但是如何保证客户端的访问安全?从易用性的角度:而基于网络导致的刷卡时间不稳定也不适合公交地铁等需要快速通过的场合。反而是基于文件的方式比较适用于ID卡,积分卡等低安全等级的一卡通整合业务。