技术博客

NFC基础与应用


本文由马星海@Worktile同学为大家分享

NFC 技术在我们生活中比较常见,可 NFC 给人们的印象却是似乎没有什么实用价值。但是NFC 实际上已经在移动支付领域深耕多年,虽然除了日本,在其他国家都没有什么起色,可是随着 Apple Pay 的到来,我们似乎又看到了 NFC 技术在移动支付领域的一丝曙光。

首先笔者先来普及一些关于 NFC 技术的基础:
1、 NFC 技术由 RFID 技术演化而来
NFC 技术使用 RFID 技术的 13.56MHz 频段;
2、 NFC 标签的供能是根据电磁感应耦合原理
读卡器中的线圈通过交变电流的时候,会产生一个交变磁场,当 NFC 标签中的线圈进入交变磁场的时候会产生一定的磁通量,因此在线圈上产生一个电动势,以此来给 NFC 标签中的芯片供电;
3、 NFC 有三种工作方式:卡模拟模式、读卡器模式和点对点模式
点对点模式的使用场景是两台具有 NFC 功能的手机,背靠背就可以传递信息;读卡器模式的使用场景是手机变成一个 NFC 读卡器,可以读取一些比如公交卡、饭卡之类的信息;卡模拟模式就是在移动支付中使用最多的模式,也是笔者今天希望与大家分享的。

根据 NFC 组织的约定,出于对 NFC 通信安全的考虑,如果需要使用 NFC 卡模拟,除了 NFC控制器外,还需要 SE 元件。SE 元件的英文全称是 Secure Element,中文名叫安全元件,它的作用是用来存储用户的敏感信息,例如银行卡卡号、密码之类的,其采用硬件加密的方式,安全性不是软件加密可比的。

SE 元件方案:

SIM 卡方案

SD 卡方案

全终端方案

HCE

以上三种方案在此就不赘述了,它们分别是将 SE 元件放在 SIM 卡、SD 卡、和整机中,如果要访问,需要涉及硬件访问,然而除了运营商或者银联,其他 APP 在不 root 的情况下根本无权访问,出于这样的原因,谷歌推出了 HCE 技术,即 Host—based card emulation,基于主机的卡模拟,在这样的技术下,系统可以将一个 APP 或者一个云端作为一个虚拟SE,当读卡器需要和 NFC 标签进行读写时,由系统根据每个注册 HCE 服务的 APP 的 AID来选择启动相应的 HCE 服务。

下面来看在 Android 4.4 以上的 HCE 的实现:
####Step 1:
写一个类继承于 HostApduService:

   import android.nfc.cardemulation.HostApduService;
   import android.os.Bundle;
   import android.telephony.TelephonyManager;
   import android.util.Log;

   public class MyHostApduService extends HostApduService {
   private int messageCounter = 0;
        private boolean canNext = true;
   @Override
   public byte[] processCommandApdu(byte[] apdu, Bundle extras) {
          if (selectAidApdu(apdu)) {
                  return getWelcomeMessage();
          }
         else {
                 return getNextMessage();
         }
   }
   private byte[] getWelcomeMessage() {
        return "Hello Desktop!".getBytes();
   }
   private byte[] getNextMessage() {
           if (canNext) {
                   TelephonyManager tm =             (TelephonyManager)this.getSystemService(TELEPHONY_SERVICE);
           if (tm.getDeviceId() != null){
                     Log.v("HCEDEMO" , tm.getDeviceId());
                     canNext = true;
                     return tm.getDeviceId().getBytes();
            }
       }
       return null;
   }
   private boolean selectAidApdu(byte[] apdu) {
           return apdu.length >= 2 && apdu[0] == (byte)0 && apdu[1] == (byte)0xa4;
   }

   @Override
   public void onDeactivated(int reason) {
          Log.i("HCEDEMO", "Deactivated: " + reason);
   }}

在此类中,主要需要重写 processCommandApdu 方法,此方法用来和读卡器之间进行通信。在上述例子中,processCommandApdu 方法的功能是一开始接收第一条“选择 APDU指令”来选择相应的 AID,之后再进行真正的数据传递。
####Step2:
在 res 文件下新建一个 xml,名为 apduservice.xml

<host-apdu-service xmlns:android="http://schemas.android.com/apk/res/android"
 android:description="@string/servicedesc"
 android:requireDeviceUnlock="false" >
 <aid-group
 android:category="other"
 android:description="@string/aiddescription" >
 <aid-filter android:name="F0010203040506" />
 <aid-filter android:name="F0394148148100" />
 </aid-group>
</host-apdu-service>

AID 组(AID groups)
在某些情况下, HCE 服务可能需要注册多个设备,以实现一个特定的应用程序,它需要确保它是默认的处理程序,对于所有的这些 AIDs(如在一个组的一些 AIDs 到其他的服务)。一个 AID 组是一个 AIDs 清单,应视为属于在一起的操作系统,对于一个组的所有AID,安卓保证以下几点:
1、该组的所有 AIDs 能被连接到这个 HCE 服务上
2、本组中没有 AIDs 连接到这个 HCE 服务上(例如,因为用户首选了其他的服务,这个
服务请求了在你的组里的一个或多个 AIDS)
换句话说,没有中间状态,中间状态指的是组中的一些 AIDs 被连接到一个 HCE 服务中,一些连接到其他的 HCE 服务中。
AID 组和类别(AID groups and categories)
每个 AID 组能够和一个类别关联起来,这样就允许安卓把 HCE 服务按照类别组织在一起,而这又使用户可以在类级别而不是 AID 级别设置的默认值。一般情况下,避免在你的应用中的任何面向用户部分提及 AIDs:AIDs 对于普通用户而言没有意义的。
安卓 4.4 支持两种类别:CATEGORY-PAYMENT(包括行业标准的支付应用程序)和CATEGORY-OTHER (所有其他 HCE 应用程序) 。
注意:在 CATEGORY-PAYMENT 类别中仅有一个 AID 组可在系统中的任何给定时间被激活。通常情况下,这将是一个应用程序,解析主要的信用卡支付 protcols 并可以在任何商家合作。
对于闭环支付应用程序,只能在一个商家(如储值卡),你应该使用 CATEGORY-OTHER 。在这一类的 AID 组可以始终处于活动状态,并且通过 NFC 读写器中的 AID 选择,如果有必要可以优先考虑。
####Step3:
在 manifest 中的标签中注册 service

<service
 android:name=".MyHostApduService"
 android:exported="true"
 android:permission="android.permission.BIND_NFC_SERVICE" >
<intent-filter>
 <action
android:name="android.nfc.cardemulation.action.HOST_APDU_SERVICE" />
 </intent-filter>
 <meta-data
 android:name="android.nfc.cardemulation.host_apdu_service"
 android:resource="@xml/apduservice" />
 </service>

####Step4:
在读卡器编程中将第一条选择 APDU 指令中的 AID 写为刚刚注册的 F0010203040506 或者 F0394148148100
最后简单说明一下 APDU 指令的指令结构:

71570e81-b5d4-4bd8-903f-a6bd0d8a4622_480x360.png

其中 APDU 的 CLA、INS 指令的具体值可以参考官方文档。

智齿客服