在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 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的卡模拟(一)的更多相关文章
- 探讨Android中的内置浏览器和Chrome
1.Android默认浏览器和Chrome的区别 Android出厂自带的浏览器:安卓WebKit浏览器,也成内置浏览器或者默认浏览器. 安卓WebKit不是Chrome.Chrome浏览器在它的用户 ...
- 在Eclipse+ADT中开发Android系统的内置应用
转自: http://www.iteye.com/topic/1050439 在Eclipse+ADT中开发Android系统的内置应用 Android系统内置有:Browser(浏览器).Mms( ...
- android webview 添加内置对象
package com.android.EBrowser; import android.app.Activity;import android.graphics.Rect;import androi ...
- C#使用Word中的内置对话框实例
本文实例讲述了C#使用Word中的内置对话框的方法,分享给大家供大家参考.具体实现方法如下: 使用 Microsoft Office Word 时,有时需要显示用户输入对话框.虽然可以创建自己的对话框 ...
- JS中的内置对象简介与简单的属性方法
JS中的数组: 1.数组的概念: 数组是在内存中连续存储的多个有序元素的结构,元素的顺序称为下标,通过下标查找对应元素 2.数组的声明: ①通过字面量声明var arr1 = [,,,,] JS中同一 ...
- 秒懂ASP.NET中的内置对象
上篇博客,小编主要简单的介绍了一下ASP.NET中的控件,这篇博客,小编主要简单总结一下ASP.NET中的内置对象,七个内置对象分别是:Request.Response.Application.Coo ...
- JSP中的内置对象和Struts中的Web资源的详解
JSP中的内置对象有如下几种: request :继承于HttpServletRequest, HttpServletRequest继承ServletRequest, 获得的Request对象的方法: ...
- IT兄弟连 JavaWeb教程 EL表达式中的内置对象
EL语言定义了11个隐含对象,它们都是java.util.Map类型,网页制作者可通过它们来便捷地访问Web应用中的特定数据.表1对这11个隐含对象做了说明. 1 EL表达式中的内置对象 这11个隐 ...
- oop(面向对象)中的内置函数
oop中的内置函数 类中存在一些名字带有双下划线__开头的内置函数, 这些函数会在某些时候被自动调用,例如之前学习的迭代器__init__函数 一.isinstance(obj, cls) 检查o ...
随机推荐
- DJANGO:从当前用户的所属用户组里查找其所拥有的权限矩阵
没办法,随时项目越来越精进,要求也越来越多. 以前的权限精度已满足不了现在的要求, 那就设计一个权限矩阵,用HOOK返回来判断吧... [莫名其妙的ORM,留个念想] 主要是在表之间的跳转,要注意语法 ...
- 使用phpexecel类库导出数据
公司要求做一个功能:将数据库里的数据导出,并生成excel文件. 于是百度了下,集大牛之所长,加上自己之所长,做出了整理,并分享. 目标:使用phpexcel类库生成xml文件,并下载. 步骤一:下载 ...
- 《sql注入攻击与防御 第2版》的总结 之 如何确定有sql注入漏洞
看完<sql注入攻击与防御 第2版>后,发现原来自己也能黑网站了,就一个字:太爽了. 简单总结一下入侵步骤: 1.确定是否有sql注入漏洞 2.确定数据库类型 3.组合sql语句,实施渗透 ...
- springmvc工作流程
Spring MVC工作流程图 图一 图二 Spring工作流程描述 1. 用户向服务器发送请求,请求被Spring 前端控制Servelt DispatcherServle ...
- C# 4.0 新特性之并行运算(Parallel)
介绍C# 4.0 的新特性之并行运算 Parallel.For - for 循环的并行运算 Parallel.ForEach - foreach 循环的并行运算 Parallel.Invoke - 并 ...
- 【POJ】1054 The Troublesome Frog
题目是非常经典的搜索+剪枝.题意简言之就是,青蛙需要沿着直线踩着踏点通过田地,并且踏点需要至少为3.问哪条路径青蛙踩坏的作物最多.很好的一个条件是青蛙每次移动都是等间距的.题目需要注意将其排序. #i ...
- poj1054The Troublesome Frog
链接 想O(n*n)的DP 怎么想都超内存 看讨论有说hash+DP过的 实现比较繁琐 大部分直接暴力过了 直接枚举每个i j 与他们在一条线上的点 是不是给出的点 注意它必须能跳进和跳出 #inc ...
- Guid 的几种形式
Guid.NewGuid().ToString()得几种格式显示 1.Guid.NewGuid().ToString("N") 结果为: 38bddf48f43c485 ...
- LightOJ 1220 Mysterious Bacteria 水题
暴力就行了,找出素因子,正的最多是30,然后负的最多是31(这一点wa了一次) #include <cstdio> #include <iostream> #include & ...
- mysql服务启动 但端口未监听
mysql 启动了,用 localhost 可以连接,但是用 127.0.0.1 不能连接.可能的原因是 1. mysql为了增强安全性而跳过了端口监听,查看方法: 用mysql> SHOW V ...