关于HCE 您所在的位置:网站首页 苹果10NFC怎么操作 关于HCE

关于HCE

2024-03-21 11:23| 来源: 网络整理| 查看: 265

关于HCE——Android手机NFC模拟刷卡成果和心得 一、前言

在最近,开始研究了手机模拟NFC刷卡的一些内容,想是自己实现一次手机模拟刷卡。

NFC大家应该都了解,这两年的安卓手机基本都是支持了NFC功能,手机厂商也给出了各自的“钱包”应用,实现卡管理。

门禁卡,公交卡,银行卡等众多功能都可以用一台手机的NFC实现,确实引起了好奇心,所以就开始下面这些研究了。

二、开始摸索

首先呢,还是先去看了看google的官方文档,看看android系统上的nfc到底在技术层面上能干啥:

支持 NFC 的 Android 设备同时支持以下三种主要操作模式: 读取器/写入器模式:支持 NFC 设备读取和/或写入被动 NFC 标签和贴纸。 点对点模式:支持 NFC 设备与其他 NFC 对等设备交换数据;Android Beam 使用的就是此操作模式。 卡模拟模式:支持 NFC 设备本身充当 NFC 卡。然后,可以通过外部 NFC 读取器(例如 NFC 销售终端)访问模拟 NFC 卡。 */

这个就是说明:android的nfc的api总共分为三大功能:

1、nfc功能一:支持读取某个nfc设备或者标签中的内容,和给某个nfc设备或者标签写入内容

这个可以联想到淘宝上卖的那种nfc标签的效果。买来的nfc标签可以用手机写入固定的一些内容,然后需要时,手机碰一碰nfc标签,手机跳转固定网址,碰一碰,手机打开支付宝。功能还是很花哨的。

我们要模拟nfc卡片的话,肯定是要先读取被模拟nfc卡片信息的,所以这个api后面肯定要用到。

2、nfc功能二:支持和其他nfc设备交换数据。

官方文档举的Android Beam这个例子,我都有点生疏了。这个功能我还是在android4.0的时代用过,当时是在相册选中一张照片,选择分享,然后选择Android Beam,然后和另一台支持nfc的安卓手机背部挨在一起,图片就传输过去了。这个说实话不是很好用。。。现在好像都没厂商推广这个功能。。。。

我们应该本次不需要用到这个。。

3、nfc功能三:支持 NFC 设备本身充当 NFC 卡

重点来了,就是要这个。

官方文档里介绍,NFC模拟这个,有两种实现原理。

第一种是使用“安全元件”(一种安全芯片),所有与读卡设备的数据交流,由安全芯片管理控制,然后再传给安卓NFC控制器处理。这个是指所有的交互逻辑都在安全芯片里面,是安全芯片在控制NFC进行模拟。

第二种是android4.4系统引入的基于主机模拟——HCE(Host-based Card Emulation),读卡设备的数据,直接传到android系统的应用上交流处理,然后由应用控制NFC进行模拟。

很明显,我们只能使用第二种——HCE。因为我们要模拟复制nfc卡片的代码,android studio写的代码肯定都是应用层的,应用来控制安卓的nfc。要是由安全芯片来控制,我就没法玩了,还能重新设计个芯片不成。。

4、nfc卡片的分类

阻挡我的第一个坎出现了,没错,nfc卡片是有区别的,不是天下大一统的。

因为nfc本质还是一个电磁感应线圈,数据的传输还是要基于某个协议进行的,根据支持的协议不同,卡就有不同的类别。

android系统的api将nfc分为以下这些类别:MifareClassic、IsoDep、MifareUltralight、ndef、NdefFormatable、NfcA、NfcB、NfcF、NfcV、NfcBarcode

而实际上,我们日常使用上好像也只听说过什么加密卡,非加密卡什么的。这个只是用户体验上的说法,不能作为开发依据。

5、开始干活

既然有分类,我们干脆直接就识别一下,我们目前手上的卡是什么就好了,上代码

(1)在res/xml下面新建nfc_tech_filter.xml文件 android.nfc.tech.IsoDep android.nfc.tech.NfcA android.nfc.tech.NfcB android.nfc.tech.NfcF android.nfc.tech.NfcV android.nfc.tech.Ndef android.nfc.tech.NdefFormatable android.nfc.tech.MifareUltralight android.nfc.tech.MifareClassic (2)修改AndroidManifest.xml,新增nfc权限,声明minSdkVersion=“10”,新增NfcActivity,在其中增加三个nfc标签的过滤器,用上刚刚的nfc_tech_filter.xml --> --> --> (3)NfcActivity,处理nfc读取逻辑,功能实现主要是引入了NfcAdapter。界面布局只有一个textView import androidx.appcompat.app.AppCompatActivity; import android.app.PendingIntent; import android.content.Intent; import android.content.IntentFilter; import android.nfc.NfcAdapter; import android.nfc.Tag; import android.nfc.tech.IsoDep; import android.nfc.tech.MifareClassic; import android.nfc.tech.NfcA; import android.nfc.tech.NfcF; import android.os.Bundle; import android.view.View; import android.widget.TextView; import android.widget.Toast; import com.socks.library.KLog; import java.io.IOException; public class NfcActivity extends AppCompatActivity { NfcAdapter nfcAdapter; TextView text; private PendingIntent pi; private IntentFilter[] mFilters; private String[][] mTechLists; private String metaInfo; private Boolean auth=false; @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_nfc); text = (TextView) findViewById(R.id.text); // 获取默认的NFC控制器 nfcAdapter = NfcAdapter.getDefaultAdapter(this); pi = PendingIntent.getActivity(this, 0, new Intent(this, getClass()).addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP), 0); IntentFilter ndef = new IntentFilter(NfcAdapter.ACTION_TECH_DISCOVERED); try { ndef.addDataType("*/*"); } catch (IntentFilter.MalformedMimeTypeException e) { throw new RuntimeException("fail", e); } mFilters = new IntentFilter[]{ndef,}; mTechLists = new String[][]{{IsoDep.class.getName()}, {NfcA.class.getName()},}; KLog.d(" mTechLists", NfcF.class.getName() + mTechLists.length); if (nfcAdapter == null) { Toast.makeText(this, "手机不支持nfc", Toast.LENGTH_SHORT).show(); finish(); return; } if (!nfcAdapter.isEnabled()) { Toast.makeText(this, "设置没开NFC", Toast.LENGTH_SHORT).show(); finish(); return; } } @Override protected void onNewIntent(Intent intent) { /* isoDep CPU卡(ISO 14443-4) NfcA或NfcB m1卡 NfcA */ super.onNewIntent(intent); Toast.makeText(this, intent, Toast.LENGTH_SHORT).show(); Tag tagFromIntent = intent.getParcelableExtra(NfcAdapter.EXTRA_TAG); KLog.e("tagFromIntent", "tagFromIntent" + tagFromIntent); if (NfcAdapter.ACTION_TAG_DISCOVERED.equals(intent.getAction())) { processIntent(intent);//处理响应 } } //页面获取焦点 @Override protected void onResume() { super.onResume(); nfcAdapter.enableForegroundDispatch(this, pi, null, null); } //页面失去焦点 @Override protected void onPause() { super.onPause(); if (nfcAdapter != null) { nfcAdapter.disableForegroundDispatch(this); } //字符序列转换为16进制字符串 private String bytesToHexString(byte[] src) { StringBuilder stringBuilder = new StringBuilder("0x"); if (src == null || src.length buffer[0] = Character.forDigit((src[i] >>> 4) & 0x0F, 16); buffer[1] = Character.forDigit(src[i] & 0x0F, 16); System.out.println(buffer); stringBuilder.append(buffer); } return stringBuilder.toString(); } private String ByteArrayToHexString(byte[] inarray) { int i, j, in; String[] hex = {"0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "A", "B", "C", "D", "E", "F"}; String out = ""; for (j = 0; j byte[] arrB = hexString.getBytes(); int iLen = arrB.length; byte[] arrOut = new byte[iLen / 2]; String strTmp = null; for (int i = 0; i //取出封装在intent中的TAG Tag tagFromIntent = intent.getParcelableExtra(NfcAdapter.EXTRA_TAG); String CardId = ByteArrayToHexString(tagFromIntent.getId()); metaInfo = "卡片ID:" + CardId+"\n"; KLog.e(metaInfo); boolean auth = false; String tagString=tagFromIntent.toString(); //读取TAG if (tagString.contains("MifareClassic")){ metaInfo+="MifareClassic\n"; readMiCard(tagFromIntent); }else{ readIsoDepTag(tagFromIntent); } } private void readIsoDepTag(Tag tagFromIntent) { IsoDep isoDep = IsoDep.get(tagFromIntent); try { if (!isoDep.isConnected()) { isoDep.connect(); } byte[] SELECT = { //APDU查询语句 (byte) 0x00, // CLA = 00 (first interindustry command set) (byte) 0xA4, // INS = A4 (SELECT) (byte) 0x00, // P1 = 00 (select file by DF name) (byte) 0x0C, // P2 = 0C (first or only file; no FCI) (byte) 0x06, // Lc = 6 (data/AID has 6 bytes) (byte) 0x31, (byte) 0x35, (byte) 0x38, (byte) 0x34, (byte) 0x35, (byte) 0x46 // AID 应用表示,用于系统区分nfc卡片和启动对应服务 }; byte[] result = isoDep.transceive(SELECT); //尝试请求一次 KLog.d(result[0]+" "+result[1]); //基本都是错误返回,因为没有nfc的厂家协议说明,啥都做不了 isoDep.close(); } catch (Exception e) { e.printStackTrace(); } } private void readMiCard(Tag tagFromIntent) { MifareClassic mfc = MifareClassic.get(tagFromIntent); try { mfc.connect(); int type = mfc.getType();//获取TAG的类型 int sectorCount = mfc.getSectorCount();//获取TAG中包含的扇区数 String typeS = ""; switch (type) { case MifareClassic.TYPE_CLASSIC: typeS = "TYPE_CLASSIC"; break; case MifareClassic.TYPE_PLUS: typeS = "TYPE_PLUS"; break; case MifareClassic.TYPE_PRO: typeS = "TYPE_PRO"; break; case MifareClassic.TYPE_UNKNOWN: typeS = "TYPE_UNKNOWN"; break; } metaInfo += "\n卡片类型:" + typeS + "\n共" + sectorCount + "个扇区\n共" + mfc.getBlockCount() + "个块\n存储空间: " + mfc.getSize() + "B\n"; for (int j = 0; j if(mfc.authenticateSectorWithKeyA(j, MifareClassic.KEY_MIFARE_APPLICATION_DIRECTORY)){ auth= mfc.authenticateSectorWithKeyA(j, MifareClassic.KEY_MIFARE_APPLICATION_DIRECTORY); } if(mfc.authenticateSectorWithKeyA(j, MifareClassic.KEY_NFC_FORUM)){ auth= mfc.authenticateSectorWithKeyA(j, MifareClassic.KEY_NFC_FORUM); } } int bCount; int bIndex; if (auth) { metaInfo += "Sector " + j + ":验证成功\n"; // 读取扇区中的块 bCount = mfc.getBlockCountInSector(j); bIndex = mfc.sectorToBlock(j); for (int i = 0; i metaInfo += "Sector " + j + ":验证失败\n"; } } text.setText(metaInfo); //Toast.makeText(this, metaInfo, Toast.LENGTH_SHORT).show(); } catch (Exception e){ e.printStackTrace(); } } }

这个是做了一个nfc读取页面,声明过滤器后,如果手机识别到nfc卡片响应,会弹出应用选择器,这个时候,我们开发的应用就在列表中。 选中后跳转到这个activity中,读取nfc卡片后,界面会显示这个nfc的卡片类型。后续逻辑,如果是芯片卡(IsoDep),还会尝试交互一波读取一下数据。如果是MifareClassic,就把每个64个扇区块数据读取出来,打印到页面上。

发现内容有点多,一篇还没讲完。 后续还有关于手上几个nfc卡的试验结果,分析,结论,留给下篇再继续介绍



【本文地址】

公司简介

联系我们

今日新闻

    推荐新闻

    专题文章
      CopyRight 2018-2019 实验室设备网 版权所有