创建 NFC 智能卡应用

重要

本主题仅适用于 Windows 10 移动版。

本主题介绍如何使用主机卡仿真(HCE)直接与近场通信(NFC)卡读取器进行通信,并让客户通过手机(而不是物理卡)访问你的服务(而不是物理卡),而无需移动网络运营商(MNO)。

开发 HCE 应用所需的内容

若要开发基于 HCE 的卡仿真应用,需要安装 Microsoft Visual Studio 2015(请参阅 Visual Studio 下载页)(包括 Windows 开发人员工具)和 Windows 10 移动版模拟器

有关获取设置的详细信息,请参阅使用适用于 Windows 10 移动版的 Microsoft 模拟器 测试。

(可选)如果要使用真正的 Windows 10 移动版设备(而不是包含的 Windows 10 移动版模拟器)进行测试,则还需要以下项。

  • 支持 NFC HCE 的 Windows 10 移动版设备。
  • 支持协议 ISO/IEC 14443-4 和 ISO/IEC 7816-4 的读取器终端

Windows 10 移动版实现提供以下功能的 HCE 服务。

  • 应用可以为要模拟的卡片注册小程序标识符(AID)。
  • 应用协议数据单元(APDU)命令和响应对的冲突解决与路由,依据外部读卡器选择和用户偏好,将这些命令和响应对路由到其中一个已注册的应用程序。
  • 由于用户操作,处理应用的事件和通知。

Windows 10 支持模拟基于 ISO-DEP(ISO-IEC 14443-4)的智能卡,并使用 ISO-IEC 7816-4 规范中定义的 APDU 进行通信。 Windows 10 支持适用于 HCE 应用的 ISO/IEC 14443-4 类型 A 技术。 类型 B、类型 F 和非ISO-DEP(例如 MIFARE)技术默认路由到 SIM 卡。

只有 Windows 10 移动版设备启用了卡片仿真功能。 基于 SIM 卡和基于 HCE 的卡仿真在其他版本的 Windows 10 上不可用。

下图显示了 HCE 和基于 SIM 卡仿真支持的体系结构。

HCE 和 SIM 卡仿真的体系结构

应用选择和 AID 路由

若要开发 HCE 应用,必须了解 Windows 10 移动版设备如何将 AID 路由到特定应用,因为用户可以安装多个不同的 HCE 应用。 每个应用都可以注册多个基于 HCE 和 SIM 卡的卡。

当用户点击其 Windows 10 移动版设备到终端时,数据会自动路由到设备上安装的正确应用。 此路由基于小程序 ID(AID),它是 5-16 字节标识符。 在点击期间,外部终端将传输一个名为 SELECT 的协议数据单元 (APDU) 命令,以指定希望将所有后续 APDU 命令路由的 AID。 后续 SELECT 命令将再次更改路由。 根据应用和用户设置注册的 AID,APDU 流量将路由到特定应用,该应用将发送响应 APDU。 请注意,终端可能需要在同一点击期间与多个不同的应用通信。 因此,当停用应用后台任务以腾出空间以响应 APDU 时,必须确保应用的后台任务尽快退出。 我们将在本主题后面讨论后台任务。

HCE 应用必须将自身注册到它们能够处理的特定 AID,以便接收与该 AID 相关的 APDU。 应用通过使用 AID 组来声明应用标识符 (AID)。 AID 组在概念上等效于单个物理卡。 例如,一张信用卡使用某一 AID 组进行注册,另一家银行的第二张信用卡则使用不同的第二个 AID 组进行注册,尽管它们可能有相同的 AID。

支付援助小组的冲突解决

当应用注册物理卡(AID 组)时,它可以将 AID 组类别声明为“付款”或“其他”。虽然在任何给定时间注册了多个付款 AID 组,但一次只能为 Tap 和 Pay 启用其中一个付款 AID 组,该组由用户选择。 此行为存在,因为用户希望能够控制有意识地选择使用单一的付款方式,如信用卡或借记卡,从而避免在将设备连接到终端时误用不同的卡进行支付。

但是,可以同时启用注册为“其他”的多个 AID 组,而无需用户交互。 此行为存在,因为其他类型的卡片(如会员卡、优惠券或交通卡)在手机触碰时,通常无需任何操作或提示即可正常使用。

注册为“付款”的所有 AID 组都显示在 NFC 设置页的卡列表中,用户可以在其中选择其默认支付卡。 选择默认支付卡后,注册此付款 AID 组的应用将成为默认支付应用。 默认付款应用可以在不与用户交互的情况下启用或禁用其任何 AID 组。 如果用户拒绝默认付款应用提示,则当前的默认支付应用(如果有)将继续保持为默认值。 以下屏幕截图显示了“NFC 设置”页。

NFC 设置页的屏幕截图

使用上面的示例屏幕截图,如果用户将默认支付卡更改为“HCE 应用程序 1”未注册的另一张卡,系统会为用户的同意创建确认提示。 但是,如果用户将默认支付卡更改为“HCE 应用程序 1”注册的另一张卡,则系统不会为用户创建确认提示,因为“HCE Application1”已是默认支付应用。

未付款 AID 组的冲突解决

非付款卡分类为“其他”不会显示在 NFC 设置页中。

你的应用可以创建、注册和启用非付款 AID 组的方式与付款 AID 组相同。 主要区别在于,对于非付款 AID 组,仿真类别设置为“其他”,而不是“付款”。 向系统注册 AID 组后,需要使 AID 组能够接收 NFC 流量。 尝试启用非付款 AID 组以接收流量时,不会提示用户进行确认,除非其他应用与系统中已注册的某个 AID 发生冲突。 如果存在冲突,系统会提示用户输入有关哪个卡的信息,如果用户选择启用新注册的 AID 组,则会禁用其关联的应用。

与基于 SIM 卡的 NFC 应用程序共存

在 Windows 10 移动版中,系统会设置 NFC 控制器路由表,用于在控制器层做出路由决策。 该表包含以下项的路由信息。

  • 单个 AID 路由。
  • 基于协议的路由(ISO-DEP)。
  • 基于技术的路由(NFC-A/B/F)。

当外部读取器发送“SELECT AID”命令时,NFC 控制器首先检查路由表中的 AID 路由是否存在匹配。 如果没有匹配,它将使用基于协议的路由作为 ISO-DEP(14443-4-A)协议流量的默认路由。 对于任何其他非ISO-DEP 流量,它将使用基于技术的路由。

Windows 10 移动版在 NFC 设置页中提供了菜单选项“SIM 卡”,以继续使用基于旧版 Windows Phone 8.1 SIM 的应用,这些应用不会将其 AID 注册到系统。 如果用户选择“SIM 卡”作为默认支付卡,则 ISO-DEP 路由设置为 UICC,对于下拉菜单中所有其他选择,则 ISO-DEP 路由至主机。

对于首次使用 Windows 10 移动操作系统启动的、启用了 SE 的 SIM 卡的设备,ISO-DEP 路由被设置为“SIM 卡”。 当用户安装启用了 HCE 的应用并且该应用启用任何 HCE AID 组注册时,ISO-DEP 路由将指向主机。 基于 SIM 卡的新应用程序需要在 SIM 卡中注册 AID,才能在控制器路由表中填充特定的 AID 路由。

创建基于 HCE 的应用

您的 HCE 应用程序有两个部分。

  • 用于用户交互的主前台应用。
  • 由系统触发的后台任务,用于处理给定 AID 的 APTU。

由于在响应 NFC 点击时加载后台任务的性能要求非常严格,因此我们建议整个后台任务在 C++/CX 本机代码(包括依赖的任何依赖项、引用或库)而不是 C# 或托管代码中实现。 虽然 C# 和托管代码通常表现良好,但是有一些开销(例如加载 .NET CLR),这些可以通过使用 C++/CX 编写代码来避免。

创建和注册您的后台任务

需要在 HCE 应用中创建后台任务,以便处理和响应系统路由到它的 APTU。 首次启动应用时,前台将注册实现 IBackgroundTaskRegistration 接口的 HCE 后台任务,如以下代码所示。

var taskBuilder = new BackgroundTaskBuilder();
taskBuilder.Name = bgTaskName;
taskBuilder.TaskEntryPoint = taskEntryPoint;
taskBuilder.SetTrigger(new SmartCardTrigger(SmartCardTriggerType.EmulatorHostApplicationActivated));
bgTask = taskBuilder.Register();

请注意,任务触发器设置为 SmartCardTriggerTypeEmulatorHostApplicationActivated。 这意味着,每当操作系统解析到你的应用并收到 SELECT AID 命令 APDU 时,都会启动后台任务。

接收和响应 APDU

当有针对您应用的 APDU 时,系统将启动后台任务。 后台任务接收通过 SmartCardEmulatorApduReceivedEventArgs 对象的 CommandApdu 属性传递的 APDU,并使用同一对象的 TryRespondAsync 方法响应该 APDU。 出于性能原因,请考虑将后台任务用于轻量操作。 例如,立即响应 APDU,并在完成所有处理后退出后台任务。 由于 NFC 交易的性质,用户往往只会将设备靠近读取器一小段时间。 后台任务将继续接收来自读取器的流量,直到连接被停用,在这种情况下,你将收到一个 SmartCardEmulatorConnectionDeactivatedEventArgs 对象。 由于 SmartCardEmulatorConnectionDeactivatedEventArgs.Reason 属性中所示的以下原因,您的连接可能会被断开。

  • 如果连接因 ConnectionLost 值而被停用,则表示用户将其设备从读取器拉开。 如果你的应用需要用户点击到终端的时间更长,建议考虑提示他们提供反馈。 应快速终止后台任务(通过完成延迟),以防用户再次点击时不必等待上一个后台任务退出。
  • 如果通过 ConnectionRedirected停用连接,则表示终端发送了一条定向到不同 AID 的新 SELECT AID 指令 APDU。 在这种情况下,您的应用程序应通过完成延期来立即退出后台任务,以允许另一个后台任务运行。

后台任务还应在 IBackgroundTaskInstance 接口上注册 已取消事件,并同样快速退出后台任务(通过完成延迟),因为当系统完成后台任务时会触发此事件。 下面是演示 HCE 应用后台任务的代码。

void BgTask::Run(
    IBackgroundTaskInstance^ taskInstance)
{
    m_triggerDetails = static_cast<SmartCardTriggerDetails^>(taskInstance->TriggerDetails);
    if (m_triggerDetails == nullptr)
    {
        // May be not a smart card event that triggered us
        return;
    }

    m_emulator = m_triggerDetails->Emulator;
    m_taskInstance = taskInstance;

    switch (m_triggerDetails->TriggerType)
    {
    case SmartCardTriggerType::EmulatorHostApplicationActivated:
        HandleHceActivation();
        break;

    case SmartCardTriggerType::EmulatorAppletIdGroupRegistrationChanged:
        HandleRegistrationChange();
        break;

    default:
        break;
    }
}

void BgTask::HandleHceActivation()
{
 try
 {
        auto lock = m_srwLock.LockShared();
        // Take a deferral to keep this background task alive even after this "Run" method returns
        // You must complete this deferral immediately after you have done processing the current transaction
        m_deferral = m_taskInstance->GetDeferral();

        DebugLog(L"*** HCE Activation Background Task Started ***");

        // Set up a handler for if the background task is cancelled, we must immediately complete our deferral
        m_taskInstance->Canceled += ref new Windows::ApplicationModel::Background::BackgroundTaskCanceledEventHandler(
            [this](
            IBackgroundTaskInstance^ sender,
            BackgroundTaskCancellationReason reason)
        {
            DebugLog(L"Cancelled");
            DebugLog(reason.ToString()->Data());
            EndTask();
        });

        if (Windows::Phone::System::SystemProtection::ScreenLocked)
        {
            auto denyIfLocked = Windows::Storage::ApplicationData::Current->RoamingSettings->Values->Lookup("DenyIfPhoneLocked");
            if (denyIfLocked != nullptr && (bool)denyIfLocked == true)
            {
                // The phone is locked, and our current user setting is to deny transactions while locked so let the user know
                // Denied
                DoLaunch(Denied, L"Phone was locked at the time of tap");

                // We still need to respond to APDUs in a timely manner, even though we will just return failure
                m_fDenyTransactions = true;
            }
        }
        else
        {
            m_fDenyTransactions = false;
        }

        m_emulator->ApduReceived += ref new TypedEventHandler<SmartCardEmulator^, SmartCardEmulatorApduReceivedEventArgs^>(
            this, &BgTask::ApduReceived);

        m_emulator->ConnectionDeactivated += ref new TypedEventHandler<SmartCardEmulator^, SmartCardEmulatorConnectionDeactivatedEventArgs^>(
                [this](
                SmartCardEmulator^ emulator,
                SmartCardEmulatorConnectionDeactivatedEventArgs^ eventArgs)
            {
                DebugLog(L"Connection deactivated");
                EndTask();
            });

  m_emulator->Start();
        DebugLog(L"Emulator started");
 }
 catch (Exception^ e)
 {
        DebugLog(("Exception in Run: " + e->ToString())->Data());
        EndTask();
 }
}

创建和注册 AID 组

在预配卡时首次启动应用程序期间,你将在系统中创建和注册 AID 组。 系统根据已注册的 AID 和用户设置,确定外部读取器希望与哪个应用程序通信,并相应地路由 APDU。

大多数支付卡注册相同的 AID,邻近支付系统环境 (PPSE),以及额外的支付网卡特定的 AID。 每个 AID 组表示一张卡片,当用户启用卡片时,将启用组中的所有 AID。 同样,当用户停用卡片时,组中的所有 AID 都会被禁用。

若要注册 AID 组,需要创建 SmartCardAppletIdGroup 对象并设置其属性以反映这是基于 HCE 的支付卡。 你的显示名称应该对用户具有描述性,因为它将显示在 NFC 设置菜单中以及用户提示中。 对于 HCE 支付卡,SmartCardEmulationCategory 属性应设置为 付款SmartCardEmulationType 属性应设置为 主机

public static byte[] AID_PPSE =
        {
            // File name "2PAY.SYS.DDF01" (14 bytes)
            (byte)'2', (byte)'P', (byte)'A', (byte)'Y',
            (byte)'.', (byte)'S', (byte)'Y', (byte)'S',
            (byte)'.', (byte)'D', (byte)'D', (byte)'F', (byte)'0', (byte)'1'
        };

var appletIdGroup = new SmartCardAppletIdGroup(
                        "Example DisplayName",
                                new List<IBuffer> {AID_PPSE.AsBuffer()},
                                SmartCardEmulationCategory.Payment,
                                SmartCardEmulationType.Host);

对于非付款 HCE 卡,应将 SmartCardEmulationCategory 属性设置为 其他SmartCardEmulationType 属性应设置为 主机

public static byte[] AID_OTHER =
        {
            (byte)'1', (byte)'2', (byte)'3', (byte)'4',
            (byte)'5', (byte)'6', (byte)'7', (byte)'8',
            (byte)'O', (byte)'T', (byte)'H', (byte)'E', (byte)'R'
        };

var appletIdGroup = new SmartCardAppletIdGroup(
                        "Example DisplayName",
                                new List<IBuffer> {AID_OTHER.AsBuffer()},
                                SmartCardEmulationCategory.Other,
                                SmartCardEmulationType.Host);

每个 AID 组最多可以包含 9 个 AID(长度为 5-16 字节)。

使用 RegisterAppletIdGroupAsync 方法向系统注册 AID 组,这将返回 SmartCardAppletIdGroupRegistration 对象。 默认情况下,注册对象的 ActivationPolicy 属性设置为 “已禁用”。 这意味着,即使你的 AID 已注册到系统,但它们尚未启用,并且不会接收流量。

reg = await SmartCardEmulator.RegisterAppletIdGroupAsync(appletIdGroup);

可以通过使用SmartCardAppletIdGroupRegistration 类的 RequestActivationPolicyChangeAsync 方法来启用已注册的卡片(AID 组)。 由于系统上一次只能启用单个支付卡,因此将付款 AID 组的 ActivationPolicy 设置为“启用” 与设置默认支付卡相同。 系统将提示用户允许此卡作为默认支付卡,而不考虑是否已选择默认支付卡。 如果你的应用已经是默认支付应用程序,并且只是更改其自身的 AID 组,那么此声明不成立。 每个应用最多可以注册 10 个 AID 组。

reg.RequestActivationPolicyChangeAsync(AppletIdGroupActivationPolicy.Enabled);

可以使用 OS 查询应用的已注册 AID 组,并使用 GetAppletIdGroupRegistrationsAsync 方法检查其激活策略。

仅当你的应用尚未成为默认支付应用时,才会提示用户将支付卡的激活策略从 “已禁用” 更改为 “已启用”。 只有在发生 AID 冲突时,才会提示用户将非付款卡的激活策略从 “已禁用” 更改为 “已启用 ”。

var registrations = await SmartCardEmulator.GetAppletIdGroupRegistrationsAsync();
    foreach (var registration in registrations)
    {
registration.RequestActivationPolicyChangeAsync (AppletIdGroupActivationPolicy.Enabled);
    }

激活策略更改时的事件通知

在您的后台任务中,您可以注册以便在应用程序外部更改某个 AID 组注册的激活策略时接收事件。 例如,用户可以通过 NFC 设置菜单将默认付款应用从其中一张卡更改为另一个应用托管的另一张卡。 如果你的应用需要知道此更改以供内部设置(例如更新动态磁贴),则可以接收此更改的事件通知,并相应地在应用中采取措施。

var taskBuilder = new BackgroundTaskBuilder();
taskBuilder.Name = bgTaskName;
taskBuilder.TaskEntryPoint = taskEntryPoint;
taskBuilder.SetTrigger(new SmartCardTrigger(SmartCardTriggerType.EmulatorAppletIdGroupRegistrationChanged));
bgTask = taskBuilder.Register();

前台覆盖行为

在您的应用处于前台时,可以将任何 AID 组注册的 ActivationPolicy 更改为 ForegroundOverride,而无需提示用户。 当用户在应用处于前台时将设备靠近终端,即使用户未选择任何支付卡作为其默认支付卡,数据流量也会被路由到你的应用。 将卡片的激活策略更改为 ForegroundOverride 时,此更改只是暂时的,直到应用离开前台,并且不会更改用户设置的当前默认支付卡。 可以从前台应用更改付款或非支付卡的 ActivationPolicy,如下所示。 请注意, RequestActivationPolicyChangeAsync 方法只能从前台应用调用,不能从后台任务调用。

reg.RequestActivationPolicyChangeAsync(AppletIdGroupActivationPolicy.ForegroundOverride);

此外,您还可以注册一个包含单个长度为 0 的 AID 的 AID 组,这将导致系统路由所有 APDU,无论 AID 如何,并包括在收到 SELECT AID 命令之前发送的任何命令 APDU。 但是,此类 AID 组仅在应用处于前台时有效,因为它只能设置为 ForegroundOverride 且无法永久启用。 此外,此机制适用于 主机UICCSmartCardEmulationType 枚举的值,以将所有流量路由到 HCE 后台任务或 SIM 卡。

public static byte[] AID_Foreground =
        {};

var appletIdGroup = new SmartCardAppletIdGroup(
                        "Example DisplayName",
                                new List<IBuffer> {AID_Foreground.AsBuffer()},
                                SmartCardEmulationCategory.Other,
                                SmartCardEmulationType.Host);
reg = await SmartCardEmulator.RegisterAppletIdGroupAsync(appletIdGroup);
reg.RequestActivationPolicyChangeAsync(AppletIdGroupActivationPolicy.ForegroundOverride);

检查 NFC 和 HCE 支持

你的应用应检查设备是否具有 NFC 硬件、支持卡仿真功能,并支持主机卡仿真,然后再向用户提供此类功能。

NFC 智能卡仿真功能仅在 Windows 10 移动版上启用,因此尝试在任何其他版本的 Windows 10 中使用智能卡仿真器 API 将导致错误。 可以在以下代码片段中检查智能卡 API 支持。

Windows.Foundation.Metadata.ApiInformation.IsTypePresent("Windows.Devices.SmartCards.SmartCardEmulator");

还可以通过检查 SmartCardEmulator.GetDefaultAsync 方法是否返回 null 来检查设备是否具有支持某种形式的卡仿真的 NFC 硬件。 如果这样做,则设备上不支持 NFC 卡仿真。

var smartcardemulator = await SmartCardEmulator.GetDefaultAsync();<

仅在最近启动的设备上(如 Lumia 730、830、640 和 640 XL)才支持基于 HCE 和 AID 的 UICC 路由。 任何运行 Windows 10 移动版及以上版本的新 NFC 设备都应支持 HCE。 您的应用程序可以按以下步骤进行检查是否支持 HCE。

Smartcardemulator.IsHostCardEmulationSupported();

锁定屏幕和屏幕关闭行为

Windows 10 移动版具有设备级卡仿真设置,可由移动运营商或设备制造商设置。 默认情况下,“点击支付”开关处于禁用状态,“设备级启用策略”设置为“始终”,除非 MO 或 OEM 覆盖这些值。

应用程序可以在设备级别查询 EnablementPolicy 的值,并根据每种情况中应用程序的所需行为相应采取措施。

SmartCardEmulator emulator = await SmartCardEmulator.GetDefaultAsync();

switch (emulator.EnablementPolicy)
{
case Never:
// you can take the user to the NFC settings to turn "tap and pay" on
await Windows.System.Launcher.LaunchUriAsync(new Uri("ms-settings-nfctransactions:"));
break;

 case Always:
return "Card emulation always on";

 case ScreenOn:
 return "Card emulation on only when screen is on";

 case ScreenUnlocked:
 return "Card emulation on only when screen unlocked";
}

除非外部阅读器选择解析为您的应用的 AID,否则在手机锁定和/或屏幕关闭时,您的应用后台任务将不会启动。 可以在后台任务中响应来自读取器的命令,但如果需要用户的任何输入,或者想要向用户显示消息,则可以使用一些参数启动前台应用。 你的后台任务可以通过以下方式启动前台应用。

  • 在设备锁屏界面下(用户仅在解锁设备后才会看到你的前台应用)
  • 在设备锁屏界面上方(用户关闭应用后,设备仍处于锁定状态)
        if (Windows::Phone::System::SystemProtection::ScreenLocked)
        {
            // Launch above the lock with some arguments
            var result = await eventDetails.TryLaunchSelfAsync("app-specific arguments", SmartCardLaunchBehavior.AboveLock);
        }

基于 SIM 卡的应用的 AID 注册和其他更新

使用 SIM 卡模拟应用作为安全元素可以注册到 Windows 服务,以声明 SIM 上支持的 AID。 此注册与基于 HCE 的应用注册非常相似。 唯一的区别是 SmartCardEmulationType,在基于 SIM 的应用中应设置为 Uicc。 由于支付卡注册,卡的显示名称也将在 NFC 设置菜单中填充。

var appletIdGroup = new SmartCardAppletIdGroup(
                        "Example DisplayName",
                                new List<IBuffer> {AID_PPSE.AsBuffer()},
                                SmartCardEmulationCategory.Payment,
                                SmartCardEmulationType.Uicc);