zoukankan      html  css  js  c++  java
  • 在Android中访问内置SE和基于SE的卡模拟(一)

    2013-10-10 编写

    前言

    在“十问Android NFC手机上的卡模拟”文中仅仅简单的介绍了一下相关的概念,如果需要了解基于SE的卡模拟的更多细节,也就是,究竟在Android的NFC手机上,目前能够做到何种程度的卡模拟,以及如何实现,则需要更深入的讨论。

    我们已经了解,NFC RF模块可以支持卡模拟工作方式,而且可以通过两种方式实现卡模拟,一种是基于硬件的,被称为虚拟卡模式(Virual Card Mode);一种是基于软件的,被称为主机卡模式(Host Card Mode)。无论哪种方式,都是NFC RF模块将外部读写器的指令转发到相关的处理模块,SE或手机上的应用程序,然后将回复信息发回外部读写器。

    本文不讨论基于软件的方式,因为在Android中,必须修改相关固件以支持该功能,也就是必须使用第三方ROM,例如Cyanogenmod。本文的重点是,如果使用硬件SE的方式,我们是否能够做到:

    1,  从手机内部访问SE,建立手机应用程序与SE之间的通讯连接并发送命令。

    2,  将NFC模块和SE置于卡模拟工作方式,使用外部读写器中的命令转向SE。

    3,  在SE中安装自己的应用,实现最终的卡模拟。

    这里先预告一下本文的结论,以免浪费大家的时间,在目前看来,没有SE密钥的用户,只能在特定条件下实现功能1和2,而功能3则是不可能的。而功能1和2的条件对一般用户也是非常苛刻的,包括

    • l  手机支持NFC,并且SE为内置SE或SWP-SIM,
    • l  手机已经ROOT,
    • l  Android版本在Android 4.0.4 (API Level 15)上,而且因为用到了一些未公开类,所以不能保证在今后的版本中还能使用。(经测试这些未公开类在目前最新版本4.3中还可以工作)。

    当然,深入讨论需要更多的专业知识,包括Android编程,智能卡等,虽然不需要全部精通,但至少有所了解。


    SE硬件形态

    SE是一个CPU卡,可以运行智能卡应用程序(称为小应用或卡应用)。一个智能卡从本质上讲就是在单一芯片上的微型计算环境,具有完备的CPU,ROM,EEPROM,RAM和I/O接口。一般智能卡还具有密钥算法协处理器,可以支持常用的加解密算法,例如DES,AES和RSA等。智能卡通过多种技术实现抗攻击特性,很难通过分解或分析芯片提取数据。事实上手机用户对SE都不陌生,因为手机的SIM卡本身就是一个SE(技术上讲只能在GSM中叫做SIM卡,更通用的应该叫做UICC)。

    SE可以有多种集成形态:UICC,内置SE或在SD插槽上的插卡。本文主要讨论内置SE的方式,但首先简要了解一下其它形态的SE。


    • l  UICC形式的SE

    普通的UICC仅仅和手机中的基带处理器相连,但基带处理器与运行Android的应用处理器是分离的,因此不能通过Android应用程序直接访问。所有的通讯需要通过射频界面层Radio Interface Layer (RIL),这是与基带处理器的IPC界面。UICC SE的通讯基于扩展AT命令 (AT+CCHO,AT+CCHC, AT+CGLA等),在目前Android中的telephony manager不支持。目前还有没有能够通过RIL访问UICC SE的标准方式(尽管有些带有定制化固件的商业设备据说支持这种方式),因为这个原因,普通的UICC并不适合NFC应用。还有一种方法是使用Single Wire Protocol (SWP)方式,SWP类型的UICC通过SWP连接到NFC控制器,目前很多移动支付都使用该方式,只要手机支持NFC功能,就可以通过更换UICC实现移动支付应用。


    • l  SD卡形式

    另一种形态是AdvancedSecurity SD card (ASSD),本质上是一个带有嵌入式SE芯片的SD卡。将SD卡插入Android设备SD插槽,并运行一个SEEK补丁过的Android版本,可以通过SmartCard API访问SE。但是并不是所有手机都具有SD插槽,因此ASSD方式不太可能成为主流。


    • l  内置SE模式

    正如其名,内置SE是设备主板的一部分,并作为NFC芯片的专用芯片,或者干脆集成为NFC芯片的一部分,因此内置SE不能从手机上移除。第一个支持内置SE的设备是Nexus S,这款手机也是首款支持NFC的Android手机。我们实验用的设备,Galaxy Nexus,带有内置的NXPPN65N 芯片,该芯片在一个单独的封装中集成了一个NFC射频控制器和一个SE(NXPSmartMX系列的P5CN072)。下图为P5CN081的硬件架构图,由于没有找到P5CN072的图,用P5CN081代替,它们之间的区别仅仅是EEPROM大小不同。


    P5xyzzz SmartMX 型号命名

    x 产品类型:

    C= PKI 控制器 + 3-DES 协处理器 + AES协处理器

    y 接口类型:

    C= 接触界面 - ISO/IEC 7816

    D= 接触和非接触双界面 - ISO/IEC 7816 +ISO/IEC 14443 contactless interface

    N= ISO/IEC 7816 + S2C NFC接口

    zzz非易失存储器大小,单位KB


    SE与手机的连接

    由于SE的硬件形态很多,因此从手机应用程序访问起来也有很多不同的路径。所幸Android下的SEEK(Secure Element Evaluation Kit  https://code.google.com/p/seek-for-android/)项目试图为开发人员屏蔽这些不同的硬件类型,提供一个统一的访问接口。感兴趣的可以去参考一下,本文使用SEEK的架构图说明不同SE的不同访问途径。

             从手机访问SE被称为在WIRED CARD 模式,可以分别通过NFC API, ASSD, RIL访问Ese(包括SWPSIM), µSD和SIM(非SWPSIM)类型的SE,同时通过第三方驱动支持外置模块SE。


    从图中可以看到:

    1, 内置SE(eSE)                        手机APP – NFC LIB – CLF-内置SE

    2, SWP-UICC                           手机APP – NFCLIB – CLF-SWP-UICC

    3, 普通UICC                             手机APP – RIL –基带处理器 –UICC

    4, SD卡(ASSD)                   手机APP – ASSD -µSD

    5, 第三方外置模块(PLUGIN)            手机APP –外置模块驱动 -外置模块SE

    其中CLF为ContactlessFrontend的缩写,一般指NFC RF模块和NFC天线。为了简化测试程序,本文并没有使用SEEK库,而仅仅使用Android中未公开的访问类,因此只能对内置SE和SWP-UICC进行测试。


    在手机中通过连线模式访问SE

    内置式SE或SWP UICC分别通过SignalIn/SignalOut接口(S2C,即NFCWI)和SWP接口与NFC控制器连接,具有三种操作模式:关闭,连线和虚拟。

    • l  关闭模式,NFC CLF模块与SE没有通讯
    • l  连线模式,NFC CLF模块使SE对Andorid操作系统可见,就像与RF读写器连接的(非接触式)智能卡
    • l  虚拟模式,NFC CLF模块使SE对外部读写器可见,这时手机就像一个非接触智能卡

    这三种模式本质上是互斥的,因此我们可以通过外部非接触界面与SE通讯(例如外部读写器),或者通过内部连线接口访问(例如通过Android上的应用程序),但无法同时使用。

    在手机开机后,缺省状态下,SE是处于关闭状态。因此为了实现卡模拟,将SE置于虚拟模式,并在这三种模式之间自由切换,我们必须先实现在手机内部对SE的访问。

    我们在前面提到,实现手机内部应用程序对SE的访问需要几个苛刻的条件,现在就具体解释一下为什么需要这些条件

    1,  为什么需要支持内置或SWP-UICC的SE的NFC手机

    NFC模块就不用说了,没有NFC模块,就不可能实现外部的读写器访问。为什么需要内置或SWP-UICC形式的SE在上面也有说明。


    2,  为什么要求Android版本高于Android4.0.4 (API Level 15)?

    在Android 2.3.4中引入了访问内置SE的内部API,并在这个版本上发布了谷歌钱包。但这些API依然在SDK中隐藏,另外在2.3.4和接下来的Gingerbread发布版中,还需要系统级权限(WRITE_SECURE_SETTINGS或NFCEE_ADMIN)才能使用这些API。早期的Ice Cream Sandwich发布版(4.0, API Level 14)中也是如此。这就意味着只有谷歌(对Nexus手机)和手机制造商(对他们自己品牌的手机)才能发布使用SE的应用程序,因为他们要么能够访问操作系统核心,要么能够拥有硬件平台密钥。但在Android 4.0.4 (API Level 15)中使用签名证书代替了系统级的权限许可(也就是在Android架构术语中的签名),因此,只要在Android系统上进行白名单登记,不需要制造商密钥,就可以访问内置的SE,这就大大简化了发布流程。另外,由于签名在白名单中文件中保存,这就可以通过OTA方式更新该列表,以便添加使用SE的应用程序。


    3,  为什么要求ROOT

    上面提到的白名单文件就是/etc/nfcee_access.xml,该文件是一个XML格式的文件,保存了允许访问SE的包名称和签名证书列表。下面是该文件的示例:

    <?xmlversion="1.0" encoding="utf-8"?>

    <resourcesxmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">

      <signerandroid:signature="30820...90">

        <package android:name="com.example.embeddedseaccess">

      </package></signer>

    </resources>

    例子中表明允许'com.example.embeddedseaccess' 包访问SE。因此允许应用访问SE的第一步就是在nfcee_access.xml中添加签名证书和包名。该文件位于系统分区(/etc 是 /system/etc的符号链接),因此我们需要root权限,以便在读写模式下修改该文件。

     

    开始编程

    满足上述条件后,我们可以开始实际的编程工作,建立访问SE的应用程序

    1,  在AndroidManifest.xml添加必要的权限和库

    <uses-permissionandroid:name="android.permission.NFC" />

    <uses-library

           android:name="com.android.nfc_extras"

               android:required="true" />


    2,  使用未公开的类

    在配置完这些文件后,终于可以使用SEAPI的时候了。目前Android并没有实现标准的智能卡通讯API,例如JSR 177 or the Open Mobile API,而仅仅在NfcExecutionEnvironment (NFC-EE)类中提供了一个非常基础的通讯接口,它只有三个公共方法。

    publicclass NfcExecutionEnvironment {

        public void open() throws IOException {...}

        public void close() throws IOException{...}

        public byte[] transceive(byte[] in) throwsIOException {...}

    }

    通过这个简单的接口就足以与SE通讯了,我们现在需要实例化一个访问接口。通过NfcAdapterExtras类的一个静态方法,可以完成对卡模拟流程(目前仅支持内置SE,因为缺少UICC接口方法)和NFC-EE的管理。向SE发送一条命令的完整代码如下:

    NfcAdapterExtrasadapterExtras = NfcAdapterExtras.get( NfcAdapter.getDefaultAdapter(context) );

    NfcExecutionEnvironmentnfceEe = adapterExtras.getEmbeddedExecutionEnvironment();

    nfcEe.open();

    byte[]response = nfcEe.transceive(command);

    nfcEe.close();

     

    然而,正如我们上面讲到的,com.android.nfc_extras是一个可选库,不是SDK中的一部分。我们不能直接的引入它,因此我们要么在Android源代码中编译(将其放在/packages/apps/目录下),或者使用反射机制。由于SE接口很小,为了便于编译和测试,我们选择反射方式。获取,打开和使用NFC-EE实例的代码就变成了下面的形式:

    ClassnfcExtrasClazz =Class.forName("com.android.nfc_extras.NfcAdapterExtras");

    MethodgetMethod = nfcExtrasClazz .getMethod("get",Class.forName("android.nfc.NfcAdapter"));

    NfcAdapteradapter = NfcAdapter.getDefaultAdapter(context);

    ObjectnfcExtras = getMethod .invoke(nfcExtrasClazz, adapter);

    MethodgetEEMethod = nfcExtras.getClass().getMethod("getEmbeddedExecutionEnvironment",

                        (Class[]) null);

    Object ee= getEEMethod.invoke(nfcExtras , (Object[]) null);

    ClasseeClazz = se.getClass();

    MethodopenMethod = eeClazz.getMethod("open", (Class[]) null);

    MethodtransceiveMethod = ee.getClass().getMethod("transceive",

                        new Class[] { byte[].class});

    MethodcloseMethod = eeClazz.getMethod("close", (Class[]) null);

    openMethod.invoke(se,(Object[]) null);

    Objectresponse = transceiveMethod.invoke(se, command);

    closeMethod.invoke(se,(Object[]) null);

    当然我们可以更优雅的方式将其封装在一个单独的类中,正如在测试程序中使用的方式。现在我们拥有了一个与SE的有效连接,可以发送一些测试数据了。


    3,  打开SE,发送命令,最后关闭SE。

    我们先发送一个空的选择命令

    SEConnectionsec = new SEConnection(self);

    booleanconnStatus = sec.connect();

    byte[]cmd = {00, (byte)0xA4, 04, 00, 00};

    byte[]response = sec.sendApdu(cmd);

    Stringinfo = ByteTool.ByteArrToHexString(response, 0, response.length);

    Log.d(TAG,String.format("Select result: %s", info));

    infoTextView.setText(info);

    sec.disconnect();

    需要注意的是一定要记得在最后调用close(),因为当NFC-EE被打开后,会阻塞非接触通讯界面。


    4,  编译应用程序,提取签名证书并修改手机上的/etc/nfcee_access.xml

    4.1,测试中使用debug方式编译应用程序,因此需要找到debug签名用的keystore,文件的路径可以从Window-->Preferences-->Android-->Build中得到


    4.2,使用JRE下的keytool导出android程序debug版证书到一个文件

    C:Program FilesJavajre6in>keytool -exportcert -v-keystore debug.keystore -alias androiddebugkey -storepass android >>a.bin

    注意我们要的是文本编码的签名证书,而不是二进制的,因此需要将a.bin转换为HEX字符串形式。


    4.3,将HEX字符串拷贝到nfcee_access.xml ,内容如下:

    <signerandroid:signature="308203……20712F" >

               <package android:name="com.example.embeddedseaccess"/>

    </signer>


    4.4,将文件上传到手机上SD卡中,并用root权限的文件管理器覆盖/etc下的文件(原文件先备份,或在原文件上添加,否则原有的使用SE的应用可能会失效)


    4.5,重启手机以使新文件生效


    5,  运行访问SE的测试程序

    由于SE符合GlobalPlatform(GP)标准,因此可以通过一个空选择APDU命令得到卡和主安全域Issuer Security Domain (ISD)的相关信息。该APDU指令数据为00 A4 0400 00,下面为发送的例子。

    APP发送:

    00A4040000

    SE返回:

    6F658408....9000


    这是一个TLV格式的HEX数据,结尾处的9000表明指令成功。具体内容可以根据格式自行解析。如果你的手机上装有SE上的应用,例如谷歌钱包,或其它移动支付应用,并知道其AID,就可以通过APDU选择访问。


    既然我们可以访问SE了,那么能否实现

    1,  控制其工作模式,使SE工作在卡模拟模式

    2,  在SE中添加应用以实现自己的卡模拟


    本文的第二部分将讨论这些内容。

  • 相关阅读:
    如何在Elasticsearch中解析未分配的分片(unassigned shards)
    spark-streaming获取kafka数据的两种方式
    hbase读写优化
    手动修复 under-replicated blocks in HDFS
    kubectl常用命令(个人记录)
    mysql远程访问被拒绝问题
    海盗分金问题
    《转》基于OpenCV的傅里叶变换及逆变换
    char*,string和CString之间的转换
    commons-fileupload 核心API 分析
  • 原文地址:https://www.cnblogs.com/pangblog/p/3365981.html
Copyright © 2011-2022 走看看