智能卡

本主题说明 Windows 应用如何使用智能卡将用户连接到安全网络服务,包括如何访问物理智能卡读取器、创建虚拟智能卡、与智能卡通信、对用户进行身份验证、重置用户 PIN 以及删除或断开连接智能卡。

智能卡的 Windows 运行时 (WinRT) API 是 Windows 软件开发工具包 (SDK)一部分。 这些 API 已创建用于 通用 Windows 平台 (UWP) 应用,但它们也可以在 WinUI 应用或打包的桌面应用中使用,包括 WPF 和Windows 窗体。 有关在 Windows 桌面应用中使用 WinRT API 的详细信息,请参阅在桌面应用中调用Windows 运行时 API。

配置应用清单

在应用可以使用智能卡或虚拟智能卡对用户进行身份验证之前,必须在 WinUI 项目或打包项目的项目 Package.appxmanifest 文件中设置 共享用户证书 功能。

访问连接的卡读取器和智能卡

你可以通过向 SmartCardReader.FromIdAsync 方法传递设备 ID(在 DeviceInformation 中指定)来查询读取器和连接的智能卡。 若要访问当前连接到返回的读取器设备的智能卡,请调用 SmartCardReader.FindAllCardsAsync

string selector = SmartCardReader.GetDeviceSelector();
DeviceInformationCollection devices =
    await DeviceInformation.FindAllAsync(selector);

foreach (DeviceInformation device in devices)
{
    SmartCardReader reader =
        await SmartCardReader.FromIdAsync(device.Id);

    // For each reader, we want to find all the cards associated
    // with it. Then we will create a SmartCardListItem for
    // each (reader, card) pair.
    IReadOnlyList<SmartCard> cards =
        await reader.FindAllCardsAsync();
}

你还应当通过实现处理插卡上的应用行为的方法使你的应用能够观察 CardAdded 事件。

private void reader_CardAdded(SmartCardReader sender, CardAddedEventArgs args)
{
  // A card has been inserted into the sender SmartCardReader.
}

然后你可以将每个返回的 SmartCard 对象传递到 SmartCardProvisioning 以访问使你的应用可以访问并自定义其配置的方法。

创建虚拟智能卡

若要使用 SmartCardProvisioning 创建虚拟智能卡,你的应用将首先需要提供一个昵称、一个管理员密钥和一个 SmartCardPinPolicy。 昵称一般向应用提供,但你的应用将仍需要提供一个管理员密钥并生成一个当前 SmartCardPinPolicy 的实例,然后才能将所有三个值传递到 RequestVirtualSmartCardCreationAsync

  1. 新建 SmartCardPinPolicy 的实例
  2. 通过调用由该服务或管理工具提供的管理密钥值上的 CryptographicBuffer.GenerateRandom 来生成管理密钥值。
  3. 将这些值与 FriendlyNameText 字符串一起传递到 RequestVirtualSmartCardCreationAsync
var pinPolicy = new SmartCardPinPolicy
    {
        MinLength = 6
    };

IBuffer adminkey = CryptographicBuffer.GenerateRandom(24);

SmartCardProvisioning provisioning = await
     SmartCardProvisioning.RequestVirtualSmartCardCreationAsync(
          "Card friendly name",
          adminkey,
          pinPolicy);

RequestVirtualSmartCardCreationAsync 返回关联的 SmartCardProvisioning 对象之后,将设置虚拟智能卡并且为使用做好准备。

注意

若要使用打包的 Windows 应用创建虚拟智能卡,运行该应用的用户必须是管理员组的成员。 如果用户不是管理员组的成员,虚拟智能卡创建将失败。

处理身份验证质询

要使用智能卡或虚拟智能卡进行身份验证,你的应用必须提供行为以完成存储在卡上的管理密钥数据和由身份验证服务器或管理工具维护的管理密钥数据之间的质询。

以下代码显示了如何支持服务的智能卡身份验证或者物理或虚拟卡详细信息修改。 如果使用卡上的管理员密钥生成的数据(“challenge”)与由服务器或管理工具提供的管理员密钥数据(“adminkey”)相同,则身份验证成功。

static class ChallengeResponseAlgorithm
{
    public static IBuffer CalculateResponse(IBuffer challenge, IBuffer adminkey)
    {
        if (challenge == null)
            throw new ArgumentNullException("challenge");
        if (adminkey == null)
            throw new ArgumentNullException("adminkey");

        SymmetricKeyAlgorithmProvider objAlg = SymmetricKeyAlgorithmProvider.OpenAlgorithm(SymmetricAlgorithmNames.TripleDesCbc);
        var symmetricKey = objAlg.CreateSymmetricKey(adminkey);
        var buffEncrypted = CryptographicEngine.Encrypt(symmetricKey, challenge, null);
        return buffEncrypted;
    }
}

你将看到这个在本主题的剩余部分中被多次引用的代码是我们审查如何完成身份验证操作以及如何将更改应用到智能卡和虚拟智能卡信息。

验证智能卡或虚拟智能卡身份验证响应

现在我们已定义了身份验证质询的逻辑,我们可以与读取器进行通信以访问智能卡,或者访问虚拟智能卡进行身份验证。

  1. 要开始质询,请从与智能卡关联的 SmartCardProvisioning 对象调用 GetChallengeContextAsync。 这将生成一个 SmartCardChallengeContext 的实例,此实例包含卡的 Challenge 值。
  2. 接着,将由服务或管理工具提供的卡的质询值和管理员密钥传递到我们在之前示例中定义的 ChallengeResponseAlgorithm 中。
  3. 如果身份验证成功,VerifyResponseAsync 将返回 true
bool verifyResult = false;
SmartCard card = await rootPage.GetSmartCard();
SmartCardProvisioning provisioning =
    await SmartCardProvisioning.FromSmartCardAsync(card);

SmartCardChallengeContext context =
    await provisioning.GetChallengeContextAsync();

IBuffer response = ChallengeResponseAlgorithm.CalculateResponse(
    context.Challenge,
    rootPage.AdminKey);

verifyResult = await context.VerifyResponseAsync(response);

更改或重置用户 PIN

更改与智能卡关联的 PIN 的步骤:

  1. 访问该卡并生成关联的 SmartCardProvisioning 对象。
  2. 调用 RequestPinChangeAsync 来向用户显示 UI 以完成此操作。
  3. 如果 PIN 成功更改,调用将返回 true
SmartCardProvisioning provisioning =
    await SmartCardProvisioning.FromSmartCardAsync(card);

bool result = await provisioning.RequestPinChangeAsync();

请求 PIN 重置的步骤:

  1. 调用 RequestPinResetAsync 以启动操作。 此调用包括一个表示智能卡和 PIN 重置请求的 SmartCardPinResetHandler 方法。
  2. SmartCardPinResetHandler 提供我们的 ChallengeResponseAlgorithm(包装在 SmartCardPinResetDeferral 调用中)用于比较卡的质询值和由服务或管理工具提供的管理员密钥的信息,以便对请求进行身份验证。
  3. 如果质询成功,RequestPinResetAsync 调用将完成;如果成功重置 PIN,将返回 true
SmartCardProvisioning provisioning =
    await SmartCardProvisioning.FromSmartCardAsync(card);

bool result = await provisioning.RequestPinResetAsync(
    (pinResetSender, request) =>
    {
        SmartCardPinResetDeferral deferral =
            request.GetDeferral();

        try
        {
            IBuffer response =
                ChallengeResponseAlgorithm.CalculateResponse(
                    request.Challenge,
                    rootPage.AdminKey);
            request.SetResponse(response);
        }
        finally
        {
            deferral.Complete();
        }
    });
}

删除智能卡或虚拟智能卡

当删除物理智能卡时,CardRemoved 事件将在删除卡时引发。

使用将应用在卡或读取器删除上的行为定义为事件处理程序的方法,将此事件的引发与卡读取器关联。 此行为可以与向用户提供卡已删除的通知同样简单。

reader = card.Reader;
reader.CardRemoved += HandleCardRemoved;

虚拟智能卡的删除以编程方式处理,方法是先检索卡,然后从 SmartCardProvisioning 返回的对象调用 RequestVirtualSmartCardDeletionAsync

bool result = await SmartCardProvisioning
    .RequestVirtualSmartCardDeletionAsync(card);