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 Files\Java\jre6\bin>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中添加应用以实现自己的卡模拟

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

在Android中访问内置SE和基于SE的卡模拟(一)的更多相关文章

  1. 探讨Android中的内置浏览器和Chrome

    1.Android默认浏览器和Chrome的区别 Android出厂自带的浏览器:安卓WebKit浏览器,也成内置浏览器或者默认浏览器. 安卓WebKit不是Chrome.Chrome浏览器在它的用户 ...

  2. 在Eclipse+ADT中开发Android系统的内置应用

    转自:  http://www.iteye.com/topic/1050439 在Eclipse+ADT中开发Android系统的内置应用 Android系统内置有:Browser(浏览器).Mms( ...

  3. android webview 添加内置对象

    package com.android.EBrowser; import android.app.Activity;import android.graphics.Rect;import androi ...

  4. C#使用Word中的内置对话框实例

    本文实例讲述了C#使用Word中的内置对话框的方法,分享给大家供大家参考.具体实现方法如下: 使用 Microsoft Office Word 时,有时需要显示用户输入对话框.虽然可以创建自己的对话框 ...

  5. JS中的内置对象简介与简单的属性方法

    JS中的数组: 1.数组的概念: 数组是在内存中连续存储的多个有序元素的结构,元素的顺序称为下标,通过下标查找对应元素 2.数组的声明: ①通过字面量声明var arr1 = [,,,,] JS中同一 ...

  6. 秒懂ASP.NET中的内置对象

    上篇博客,小编主要简单的介绍了一下ASP.NET中的控件,这篇博客,小编主要简单总结一下ASP.NET中的内置对象,七个内置对象分别是:Request.Response.Application.Coo ...

  7. JSP中的内置对象和Struts中的Web资源的详解

    JSP中的内置对象有如下几种: request :继承于HttpServletRequest, HttpServletRequest继承ServletRequest, 获得的Request对象的方法: ...

  8. IT兄弟连 JavaWeb教程 EL表达式中的内置对象

    EL语言定义了11个隐含对象,它们都是java.util.Map类型,网页制作者可通过它们来便捷地访问Web应用中的特定数据.表1对这11个隐含对象做了说明. 1  EL表达式中的内置对象 这11个隐 ...

  9. oop(面向对象)中的内置函数

    oop中的内置函数 ​ 类中存在一些名字带有双下划线__开头的内置函数, 这些函数会在某些时候被自动调用,例如之前学习的迭代器__init__函数 一.isinstance(obj, cls) 检查o ...

随机推荐

  1. Druid数据库连接池两种简单使用方式

    阿里巴巴推出的国产数据库连接池,据网上测试对比,比目前的DBCP或C3P0数据库连接池性能更好 简单使用介绍 Druid与其他数据库连接池使用方法基本一样(与DBCP非常相似),将数据库的连接信息全部 ...

  2. 转:Web service是什么?

    作者: 阮一峰 我认为,下一代互联网软件将建立在Web service(也就是"云")的基础上. 我把学习笔记和学习心得,放到网志上,欢迎指正. 今天先写一个最基本的问题,Web ...

  3. python还不能作为主要编程语言的原因:

    1.不太熟悉,容易犯新手错误,2.调试方法不同3.写了一个函数,是否语法正确,不能知道,只有具体调用它的时候才知道4.编辑器太业余,没有输入联想功能5.要查找一个函数或变量在哪里定义的,只能通过搜索的 ...

  4. matlab numpy equivalents

    THIS IS AN EVOLVING WIKI DOCUMENT. If you find an error, or can fill in an empty box, please fix it! ...

  5. 【HDOJ】2430 Beans

    这题目用线段树超时了,其实也差不多应该超时.10^6大数据量.看了一下网上的解法是单调队列.大概了解了一下,是个挺有意思的数据结构.首先,需要求满足0<=(S[r]-S[l])%p<=k时 ...

  6. Android开发之ListView-BaseAdapter的使用

    ListView优化原则: UI优化: listview条目与条目之间的间隙的分割内容 : android:divider="@android :color/transparent" ...

  7. Hadoop中两表JOIN的处理方法

    Dong的这篇博客我觉得把原理写的很详细,同时介绍了一些优化办法,利用二次排序或者布隆过滤器,但在之前实践中我并没有在join中用二者来优化,因为我不是作join优化的,而是做单纯的倾斜处理,做joi ...

  8. c程序设计语言_习题1-11_学习单元测试,自己生成测试输入文件

    How would you test the word count program? What kinds of input are most likely to uncover bugs if th ...

  9. 【转】Android学习系列(1)--为App签名(为apk签名)

    原文网址:http://www.cnblogs.com/qianxudetianxia/archive/2011/04/09/2010468.html 写博客是一种快乐,前提是你有所写,与人分享,是另 ...

  10. MVC系统过滤器、自定义过滤器

    一.系统过滤器使用说明 1.OutputCache过滤器 OutputCache过滤器用于缓存你查询结果,这样可以提高用户体验,也可以减少查询次数.它有以下属性: Duration:缓存的时间,以秒为 ...