当前位置:天才代写 > tutorial > C语言/C++ 教程 > 智能存储:通过托管代码和Windows Vista智能卡API来掩护您的数据

智能存储:通过托管代码和Windows Vista智能卡API来掩护您的数据

2017-11-04 08:00 星期六 所属: C语言/C++ 教程 浏览:447

副标题#e#

本文接头:

Windows 智能卡编程基本

示例智能卡应用措施的实现要领

编写实现智能卡成果的托管打包措施

智能卡事务打点

这篇文章基 于 Windows Vista 的预宣布版而撰写。个中包括的信息大概会有所变换。

本文使 用了以下技能:

Windows Vista, C++, C#

目次

Windows 智能卡编程

智能卡的成长

示例应用措施的实现要领

WinSCard API 打包措施

GetSmartCard 辅佐器例程

卡模块 API 打 包措施

处理惩罚 CardAcquireContext

事务打点

卡模块接口设计根基原 理

利用 CLR 加密

依赖干系和测试

智能卡(简朴地说,就是嵌入了 微型芯片的信用卡)的观念已经提出快要 30 年了。但此刻安详事情的重点是让公司和政 府等机构从头审视一些早已有之的理念。

对付身份验证系统的懦弱毗连(即暗码 )来说,智能卡是一个很吸引人的替代要领。整个行业很是需要可以或许替代暗码的技能。借 助嵌入式的加密处理惩罚器,智能卡提供了很是安详且易于利用的身份验证机制。

然 而,智能卡的陈设也带来了其特有的挑战。整个行业需要更好的产物来陈设和打点巨大的 身份验证技能。比尔·盖茨在 RSA 2006 大会上做的主题讲话中,演示了 Microsoft Certificate Lifecycle Manager,该产物充实操作了我们这篇文章中接头的 API。

整个行业还要着手应对客户隐私掩护的要求。消费者需要一种掩护特权信息 的要领,譬喻,美国的在线银行业很快会要求利用严格的身份验证。在这个进程中,消费 者将利用诸如 Windows® CardSpace 的技能选择在各类在线事务中,披露哪些小我私家书 息。譬喻,在在线银行事务进程中,我大概很机要地证明我的社会保险号 (SSN) 与我的 身份是绑定在一起的,但我不行能与之共享我的信用卡号码。反之,我会授权将我的信用 卡号码(而不是 SSN)透露给电子商务网站。

Microsoft 已经认识到智能卡在其 平台的安详计谋中所饰演的重要脚色。开拓人员需要相识可识别智能卡的应用措施的事情 道理,以及 Windows 操纵系统利用了何种要领,使糊口变得越发轻松。


#p#副标题#e#

Windows 智能卡编程

Windows 已支持小我私家计较机/智能卡 (PC/SC) 软件仓库快要十年了( 具体信息,请拜见 www.pcscworkgroup.com)。PC/SC 成果通过 Windows 智能卡 (WinSCard) 客户端 API 提供应应用措施,该 API 在 winscard.dll 和 scarddlg.dll 中实现(在后者中实现水平较小)。客户端 API 答允主机应用措施通过智能卡资源打点 器处事(也称作 SCardSvr)与智能卡间接举办通信。

由于智能卡是一种共享资源 ,在任何给按时间都大概有多个应用措施试图与该卡举办通信,因此要利用资源打点器模 型。与该卡的通信必需是串行的。因此,资源打点器强制回收简化的、雷同于数据库的锁 定模子。在 PC/SC 中,持有给定智能卡读卡器(也称为接口设备或 IFD)的锁,就便是 把握了事务。然而,本文中“事务”这个术语大概会引起误解。譬喻,没有回 滚或打消提交的工作产生,PC/SC 不能自动取消应用措施在事务中作出的变动。

在 SCardEstablishContext、SCardBeginTransaction 以及其他 WinSCard 函数的平台 SDK 文档中可找到对 PC/SC 的其他说明。主机应用措施利用这些例程成立到资源打点器 的管道,得到智能卡读卡器的独有锁,向卡发送一系列呼吁,然后释放锁。

图 1 中汇总了 PC/SC 组件仓库。请留意,PC/SC 客户端代码是在应用措施历程中加载的。资 源打点器是一种系统处事,是独立于客户端的。读卡器设备驱动措施是独一在内核模式下 运行的 PC/SC 仓库组件。读卡器驱动措施被资源打点器独有加载,以防备应用措施忽略 事务模子。这是 PC/SC 安详模子很重要的一个方面。最后,本文中接头的 API 只供用户 模式挪用措施利用。遗憾的是,没有内核模式的资源打点器。

智能存储:通过托管代码和Windows Vista智能卡API来呵护您的数据

图 1PC/SC 组件仓库

文章的剩余部门将重点接头栈顶:主机应用措施和 PC/SC 客户 端 API。接着,我将接头此编程模子的范围性及其成长进程。

智能卡的成长

迄今为止,智能卡在 Windows 中的典范用途凡是与身份验证相关。譬喻,它们可 以取代暗码举办登录。这些智能卡身份验证方案是基于加密技能的。这就导致一直以来, 在 Windows 中添加对新型智能卡的支持始终要求供给商陈设一个名为 CSP(加密处事提 供商)的插件,该插件陈设了名为 Crypto API 1.0 的接口。不幸的是,人人都知道 CSP 的编写事情很是巨大。并且,由于 CSP 接口是专用于初级别加密操纵的,因此不能提供 现代智能卡的各类富厚的新成果。这些成果包罗增加的数据存储容量、高带宽 I/O、卡载 小措施以及新的加密算法。由于缺少可实现这些成果的应用措施级此外编程模子,阻碍了 硬件供给商出产出有别于人的商品,也阻碍着编程人员为各类硬件编写措施。

#p#分页标题#e#

值 得兴奋的是,由于回收了被称作“卡模块 API”的新插件模子,Windows Vista™ 中的环境已大大改进。新 API 最大的利益是答允可识别智能卡的应用措施 不知道卡的详细范例。让我们看一个示例。

我的示例是一个 Windows 数字权限管 理 (DRM) 应用措施,该措施利用智能卡来存储数字媒体许可证。数字许可证包罗数字签 名的、颠末压缩的 XML Blob。通过在许可证生命周期中与智能卡的交互,主机应用措施 在卡上执行一些简朴的呼吁:建设存储许可证的文件、写入文件、读取文件和删除文件。 每个呼吁都对应于一个差异的字节序列。譬喻,假如应用措施确定被智能卡识别为 “02”的文件(许多智能卡的文件系统很是简朴)包括了许可证 Blob,那么 可以对相关的呼吁/字节序罗列办编码:“读取 02 文件的前 128 个字节。” 每个呼吁都是通过 WinSCard 函数 SCardTransmit 发送到卡上的。稍后,我将转回到这 个话题。

要普及可识别智能卡的应用措施,需要留意的一个问题是各个供给商使 用的呼吁编码是差异的。就算已经有了一些尺度化编码(如 ISO 7816),而且针对根基 操纵(如读取文件)的 ISO 编码大概简直合用于多种卡,但凡是开拓人员不能指望这些 编码。因此,必需对本示例中的 DRM 应用措施举办修改,以支持各类新的智能卡范例。 按照我的履历,构建这些智能卡呼吁字节序列的应用措施代码很是混乱,很难维护。

新的卡模块 API 通过提供与常见文件系统雷同的接口,处理惩罚卡不兼容的问题,并 利用其他一些例程满意前面提到过的与加密有关的身份验证要求。譬喻,假如应用措施采 用的是针对各类卡举办“读取 02 文件的前 128 个字节”呼吁编码的旧模式 ,则伪代码大概与图 2 雷同。

Figure2Pseudocode for Per-Card-Type Command Handling

if (cardType == A) {
  // Build read-file transmit buffer for card type A
  ...
  // Send the command to the card
   SCardTransmit(
    cardHandle,     // PC/SC context handle
    pciInputPointerA,  // Protocol Control Information (in)
     transmitBufferA,   // Our read-file command
     transmitBufferLength,// Read-file command length
     pciOutputPointerA,  // Protocol Control Information (out)
     receiveBuffer,    // Data response from card
     receiveBufferLength);// Length of data response
  // Interpret the response from this card type
  ...
}
else if (cardType == B) {
  // Build read-file transmit buffer for card type A
  ...
  // Send the command to the card
  SCardTransmit(
     cardHandle,     // PC/SC context handle
    pciInputPointerB,   // Protocol Control Information (in)
    transmitBufferB,   // Our read-file command
    transmitBufferLength,// Read-file command length
    pciOutputPointerB,  // Protocol Control Information (out)
    receiveBuffer,    // Data response from card
     receiveBufferLength);// Length of data response
  // Interpret the response from this card type
  ...
}

#p#副标题#e#

相反,假如利用新的 API,就可以通过挪用单个函数来取代整个块:

CardReadFile(
contextPointer, // PC/SC context handles
NULL,      // Directory in which requested file is located
"license",    // File name to read
&dataOut,    // Contents of the requested file
&dataLengthOut); // Size of the file contents
假如您以为新的 API 比旧要领简朴许多,我完全同意。但别着急,尚有一些事情要做!

到此刻为止,我已 经描写了 PC/SC 软件仓库和卡模块 API 的一些根基环境。我已经做好办理 DRM 应用程 序示例代码的筹备。由于我描写的智能卡相关接口都不向 Microsoft® .NET Framework 代码开放,假如我能将卡模块 API 的成果与 .NET 的利便之处团结起来,是 不是很酷?

我发明可回收最通例的办理方案来提供代码打包措施,该措施可通过 P/Invoke(一种机制,用于挪用来自托管代码的本机 DLL 输出)实现所需的 WinSCard 和卡模块例程。然后,我可以用托管代码演示整个应用措施。我发明假如编写其他打包程 序代码来简化任务,可以使工作简朴一点。以前,假如不熟悉本机 API 的常识,实现托 管接口长短常坚苦的。

首先,我将概述示例应用措施需要做什么。然后具体接头 打包措施和 P/Invoke 接口。

示例应用措施的实现要领

#p#分页标题#e#

首先,应用措施要 找到并绑定到一个插入的智能卡。假如未插入智能卡,措施将提示用户插入一个智能卡。 实现这些操纵需要用到 WinSCard API,因此这一部门的应用措施对 P/Invoke 和本机打 包措施很是依赖。

然后,我将打开卡上的加密密钥对的句柄。如何没有符合的密 钥对,我将新建一个。幸运的是,所需的与加密相关的成果已经通过 CLR 提供了,因此 一切都很简朴。对付应用措施的加密部门,有一个潜在的障碍:必需安装新版本的根基智 能卡加密处事提供措施 (Base Smart Card Crypto Service Provider),这是与基于卡模 块的卡互换数据最靠得住的要领。

我将建设一些代表数字媒体许可证的示例 XML 数 据。然后,我将利用加密密钥对对数据举办加密。同样,必须的 XML 和与加密相关的功 能已通过 CLR 提供。我将通过卡模块生存数据和签名。这需要新的本机打包措施和用于 本机例程的 P/Invoke 接口。下一步,既然该措施是用于测试的,我将反向执行前面的步 骤。这就是说,我将把方才写入卡的文件读取出来。最后,我将通过 CLR 加密例程对数 据举办解密。

我已经提要先容了要在应用措施中看到的行为,此刻让我们将各类 成果要求分类。以下三个构成部门是所有我想要的对象:用于 WinSCard API 的打包措施 、用于卡模块 API 的打包措施以及现有的 CLR 加密例程。一旦实现了这些操纵并领略了 个中的原理,编写应用措施这件事自己是很容易的!让我们仔细相识各个构成部门。我建 议您下载示例 C# 代码并当真进修,这些代码中插手了一些关于智能卡编程基本的注释, 增加了趣味性。

WinSCard API 打包措施

该应用措施做的第一件工作是调 用 .NET 加密 API,找到符合的密钥对。可是,我将在先容完该应用措施举办的底层智能 卡 API 挪用之后再接头这部门问题,以阐发我对整体体系架构的观点。

为了通过 卡模块 API 与智能卡直接举办交互,我要做的第一件事就是为适当的卡获取 PC/SC 句柄 。(请留意,此处引述的所有本机例程在 Platform SDK 中都有记录,因此我不会在此为 用到的各个民众例程提供具体的利用要领。)为了绑定到来自托管应用措施的智能卡,我 需要先容几个本机 API。

任何 Windows 智能卡应用措施的基本都是 SCardEstablishContext。从 winscard.dll 输出的 PC/SC 例程成立了句柄,该句柄答允 应用措施与智能卡资源打点器交互。其成果很是简朴,对付这些简朴的成果,无需实现额 外的本机打包措施。P/Invoke 接口足以:

[DllImport ("winscard.dll")] public static extern Int32 SCardEstablishContext(
UInt32 scope,
[MarshalAs (UnmanagedType.AsAny)] Object reserved1,
[MarshalAs(UnmanagedType.AsAny)] Object reserved2,
ref IntPtr pcscContext);

#p#副标题#e#

示例应用措施运用的第 二个 PC/SC 例程 SCardUIDlgSelectCardW 是今朝为止最难在 .NET 代码中利用的例程。 这有两个原因。第一个原因,此函数利用了单个 C 样式布局作为其参数。(请拜见平台 SDK 中 winscard.h 界说的 OPENCARDNAME_EXW。)该布局除了包罗若干个其他字段外, 还包罗两个充当输入和输出的 Unicode 字符数组。要正确地排定这个布局是要费些周折 的。需要说明的是,这个布局包括一个可选的嵌入式布局:OPENCARD_SEARCH_CRITERIAW 。该布局也是在 winscard.h 中界说的。这个嵌套布局不比第一个布局简朴。对付这个示 例而言,我选择不回收依赖于第二个布局的 SCardUIDlgSelectCardW 的高级成果。

回收 SCardUIDlgSelectCardW 所面对的第二个挑战是,必需将 OPENCARDNAME_EXW 第一个数据成员初始化,使之与布局巨细沟通(以字节为单元)。我 实验了三种要领,试图将该字段初始化为正确的值。第一种要领,我试图对其举办硬编码 — 这就是说,确定该布局的巨细(譬喻,通过调试措施或回收本机代码的简朴测试 应用措施),始终在托管代码中将该字段初始化为该值。利用易于移植的代码是很不错的 ,可是,在 64 位的情况下,该布局的巨细会有所差异。

第二种要领,您可以使 用一个或多个 CLR 编排原始字节(如 Marshal.SizeOf)来确定被编排的布局在运行时的 巨细。不幸的是,在我的测试中,该非凡例程获得了令人意外的功效,表白特定命据成员 经编排后的巨细是无法确定的。并且,除非此要领获得的巨细与本机巨细是完全一样的, 不然,这不是一个好主意。

#p#分页标题#e#

第三种要领,我实现了一个可接管部门初始化布局的 本机打包措施,配置 dwStructSize 字段,然后挪用 SCardUIDlgSelectCardW。然后,我 通过 P/Invoke 向托管代码提供了本机打包措施。您大概已经猜出来了,我选择了这种做 法。我知道,出于机动性的思量,将本机打包措施封装为独立的 DLL 以及添加特别 DLL 的打点开销以支持单个 API 的做执法人很难接管,但我已经获知卡模块例程也需要雷同 的办理方案。换言之,我发明提供一个独立的本机 DLL 是不行制止的。我将在下一节介 绍卡模块例程。

在示例代码中,MgSc.dll(托管智能卡的缩写)输出本机打包程 序例程。打包措施例程的代码(包罗 MgScSCardUIDlgSelectCardW)存储在 MgSc.cpp 中 。该 DLL 顶用于所有本机打包措施的 P/Invoke 类也称作 MgSc;它包括与以下内容雷同 的 P/Invoke 占位措施:

[DllImport("mgsc.dll", CharSet=CharSet.Unicode)] public static extern Int32 MgScSCardUIDlgSelectCardW(
  [MarshalAs(UnmanagedType.LPStruct)] [In, Out]   PCSCOpenCardNameWEx ocnwex);

让该 API 正常事情后,其他 事情就显得很是简朴了!

GetSmartCard 辅佐器例程

图 3 显示了摘录的示 例代码中的 GetSmartCard 辅佐器例程。请留意,假如 MgScSCardUIDlgSelectCardW 是 乐成的,则意味着 PCSCOpenCardNameWEx 参数的 cardHandle 成员被初始化并对应于一 个已插入的智能卡。换言之,我们已经可以利用这张卡了。

Figure3GetSmartCard Helper Routine

static Int32 GetSmartCard(
  [In, Out] ref MgScContext mgscContext,
  [In, Out] ref IntPtr pcscContext,
  [In, Out] ref IntPtr cardHandle)
{
  Int32 result = 0;
  PCSCOpenCardNameWEx ocnwex = new PCSCOpenCardNameWEx ();
  bool cardIsLocked = false;
  string readerName;
   UInt32 readerNameLength = 0, cardState = 0, cardProtocol = 0;
  byte [] atr;
  UInt32 atrLength = 0;
  try
  {
    // Get a context handle from the smart card resource manager
    if (0 != (result = PCSC.SCardEstablishContext(
       PCSCScope.SCARD_SCOPE_USER, null, null, ref pcscContext)))
         throw new Exception("SCardEstablishContext");
    // Get a handle to a card, prompting the user if necessary
    ocnwex.flags = PCSCDialogUI.SC_DLG_MINIMAL_UI;
    ocnwex.pcscContext = pcscContext;
    ocnwex.shareMode = PCSCShareMode.SCARD_SHARE_SHARED;
    ocnwex.preferredProtocols = PCSCProtocol.SCARD_PROTOCOL_Tx;
    ocnwex.reader = new string(
      (char) 0, (int) readerAndCardBufferLength);
     ocnwex.maxReaderLength = readerAndCardBufferLength;
    ocnwex.card = new string(
      (char) 0, (int) readerAndCardBufferLength);
    ocnwex.maxCardLength = readerAndCardBufferLength;
    if (0 != (result = MgSc.MgScSCardUIDlgSelectCardW(ocnwex)))
      throw new Exception("SCardUIDlgSelectCardW");
    // Lock the card
    if (0 != (result = PCSC.SCardBeginTransaction (ocnwex.cardHandle)))
      throw new Exception ("SCardBeginTransaction");
    cardIsLocked = true;
     // Get the ATR for the selected card
    if (0 != (result = PCSC.SCardStatusW(ocnwex.cardHandle, null,
      ref readerNameLength, ref cardState, ref cardProtocol,
      null, ref atrLength)))
        throw new Exception ("SCardStatusW");
    readerName = new string((char) 0, (int) readerNameLength);
    atr = new byte [atrLength];
     if (0 != (result = PCSC.SCardStatusW(ocnwex.cardHandle,
       readerName, ref readerNameLength, ref cardState,
      ref cardProtocol, atr, ref atrLength)))
        throw new Exception ("SCardStatusW");
    // Get a card module handle for this card
    mgscContext = new MgScContext();
    if (0 != (result = (int) MgSc.MgScCardAcquireContext(
      mgscContext, pcscContext, ocnwex.cardHandle,
      ocnwex.card, atr, atrLength, 0)))
        throw new Exception ("MgScCardAcquireContext");
    cardHandle = ocnwex.cardHandle;
  }
  finally
  {
    if (0 != result)
    {
      // Something failed, so we need to cleanup.
      if (cardIsLocked)
         PCSC.SCardEndTransaction(ocnwex.cardHandle,
           PCSCDisposition.SCARD_LEAVE_CARD);
      if ((IntPtr)0 != ocnwex.cardHandle)
        PCSC.SCardDisconnect (ocnwex.cardHandle,
           PCSCDisposition.SCARD_LEAVE_CARD);
      if ((IntPtr)0 != ocnwex.pcscContext)
      {
         PCSC.SCardReleaseContext(ocnwex.pcscContext);
        pcscContext = (IntPtr)0;
      }
    }
  }
  return result;
}

#p#副标题#e#

#p#分页标题#e#

我利用返回的卡句柄在 GetSmartCard 中做的第一件工作是调 用 SCardBeginTransaction。这一操纵授予了对智能卡的独有会见权限,防备其他应用程 序(甚至该历程中的其他卡句柄)与该卡交互。假如 GetSmartCard 例程乐成返回,事务 仍然是保存的,我将使卡保持在锁定状态,直到挪用托管的加密 API 例程为止。在谁人 时间点释放锁的原因很深奥,我将稍后作出表明。

我对卡句柄做的第二件工作是 用句柄查询用于所选智能卡的 ATR(重置应答)字符串的 PC/SC。ATR 的字节构成了一个 因卡范例而异的标识符,该标识符简化了卡与智能卡读卡器设备驱动措施之间的底层协议 协商。也可以利用卡 ATR,通过系统注册表,在卡和对应的卡模块之间实现映射(还可以 在卡与对应的 CSP 之间,尽量这些数据是由加密 API 提取的)。换言之,为了加载正确 的卡模块,我必需首先知道要与之互换信息的卡的 ATR。

既然我已经有了卡句柄 ,就可以通过 P/Invoke 挪用 SCardStatus API 以获取 ATR 了。您可以在 GetSmartCard 中看到我挪用了两次 SCardStatus:一次是查询两个变量长度输出参数的 巨细,第二次是用足够巨细的缓冲区接管输出信息。但假如您看一下针对 SCardStatus 的平台 SDK 文档,会发明 API 还可以以挪用措施的名义分派输出缓冲区(可选)。

以 ATR 输出缓冲区为例。在本机代码中,挪用措施可以将缓冲区长度的输入/输 出参数 pcbAtrLen 配置为指向 SCARD_AUTOALLOCATE 值,该值在 winscard.h 中界说为 –1。然后,pbAtr 参数被作为 LPBYTE * 传送并转换为 LPBYTE。返回的缓冲区通 过 SCcardFreeMemory 释放。许多 PC/SC 函数也利用同样的语义。实际上,每当我编写 与 PC/SC 相关的本机代码时,城市出于利便的目标,频繁利用 SCARD_AUTOALLOCATE。然 而,由于界说如此毫无纪律的行为和正确编排这些行为的巨大性,我没有选择在打包措施 中回收这一成果。

一旦获取了 ATR,我就会将选定的卡映射到卡模块。我抉择将 操纵提取到打包措施函数(我所实现的将卡模块提供应托管代码的那些函数)之一中。这 是下一节的主题。

卡模块 API 打包措施

本机卡模块接口是在 cardmod.h 中界说的,Windows Vista 平台 SDK 的最新版本中提供了这一文件。假如您看一下在头 文件尾部界说的 CARD_DATA 布局,就会留意到,在 .NET 代码中回收卡模块例程会有一 些巨大化。与某些 PC/SC 例程中由 SCARD_AUTOALLOCATE 带来的有关内存打点的挑战相 似,一些卡模块例程以挪用措施的名义分派动态巨细的输出缓冲区。可是,与 SCARD_AUTOALLOCATE 差异的是,卡模块内存分派行为不是可选的。以 CardReadFile 为 例(当我从智能卡读取数字许可证文件时,需要在示例应用措施中利用该文件)。我将通 过函数指针终止挪用 CardReadFile,指针范例按如下方法界说。

typedef DWORD (WINAPI *PFN_CARD_READ_FILE)(
__in            PCARD_DATA  pCardData,
__in            LPSTR pszDirectoryName,
__in             LPSTR pszFileName,
__in            DWORD dwFlags,
__out_bcount(*pcbData)   PBYTE *ppbData,
__out            PDWORD pcbData);

此刻看一下 CARD_DATA 布局的两个成 员。第一个成员是建设输出缓冲区时,卡模块利用的堆分派器回调。第二个成员是在 CardAcquireContext 执行期间由卡模块初始化的 PFN_CARD_READ_FILE 函数指针,供后 面的挪用应用措施利用。

// These members must be initialized by the CSP/KSP before
// calling CardAcquireContext.
...
PFN_CSP_ALLOC       pfnCspAlloc;
PFN_CSP_FREE      pfnCspFree;
...
// These members are initialized by the card module
...
PFN_CARD_READ_FILE   pfnCardReadFile;

#p#分页标题#e#

总之,在 CardReadFile 挪用 进程中,卡模块或多或少会执行以下一些操纵:通过 pfnCspAlloc 分派足够大的缓冲区 、通过 SCardTransmit 从卡中读取请求的文件、配置 pcbData 并返回。一旦挪用措施结 束并返回数据缓冲区,则挪用 pfnCspFree。

我知道,与手工编写一组 P/Invokes 对比,为每个卡模块例程利用一个本机打包措施会缩短时间。譬喻,在挪用 CardReadFile 时,.NET 应用措施挪用一次当地打包措施,确定输出缓冲区的巨细,然后 用足够巨细的缓冲区再次挪用。这很明明是一本机能上的折衷,因为应用措施从智能卡上 读取两次文件。这种环境很有大概通过为堆回调实现委托得以改进。

#p#副标题#e#

处理惩罚 CardAcquireContext

让我们回到示例数字许可证应用措施的思路中。追念一下, 我通过 PC/SC 例程,获取了卡以及该卡的 ATR。基于该 ATR,我在称作 Calais 的数据 库中查找对应卡模块 DLL 的名称,该名称就是存储在系统注册表HKLM\Software\Microsoft\Cryptography\Calais中的数据。假如 PC/SC 仓库最初是在 Microsoft 实现的,则将“Calais”选为项目代码名称。按照某些传播的说法 ,我想智能卡是由法国人发现并不完全是巧合。

Calais 数据库查找是通过 SCardGetCardTypeProviderName 传送 cardmod.h 中界说的 SCARD_PROVIDER_CARD_MODULE 值完成的。我抉择将其滚入到 MgScCardAcquireContext, 即 CardAcquireContext 的本机代码打包措施,而不是通过 P/Invoke 接口,将特定的 PC/SC 例程提供应托管情况。为什么呢?让我们向下看几步(本机代码)操纵。下一步是 得到由 Calais 数据库返回的 DLL 字符串名称并将其通报给 LoadLibrary。必需在整个 上下文的生命周期中,用卡模块维护获得的 HMODULE(我虽然不但愿卸载该卡模块)。然 后挪用 GetProcAddress 找到 CardAcquireContext 输出。最后,我用卡模块等候的本机 回调(包罗堆分派例程)初始化 CARD_DATA 布局。

总之,假如 .NET 代码仅在准 备挪用 CardAcquireContext 时执行,则通过 .NET 代码让所有与本机代码相关的质料运 行起来,不会有什么好的功效。将其转动到该例程的本机打包措施会好一些。

请 思量卡模块实际执行了什么操纵。给定的卡模块处理惩罚 CardAcquireContext 时的步调取决 于卡的范例。假如该卡基于 ISO 呼吁集,则此时卡模块无需向卡发送任何呼吁。相反, 卡模块将确认是否简直支持指定的 ATR(通过 CARD_DATA 由挪用措施输入)。然后大概 配置一些内部上下文状态并将其附加到挪用措施的 CARD_DATA 的 pvVendorSpecific 成 员。另一方面,较新的卡大概是基于署理或虚拟机的。Sun 的 Java 卡是一个很明明的示 例。在这种环境下,卡模块将很大概通过 SCardTransmit 向卡发送呼吁,将卡端卡模块 API 处理惩罚措施 (card-side card module API handler) 小措施实例化。

事务打点

此刻,让我们返来接头示例代码。假如 GetSmartCard 辅佐器例程乐成返回,则 卡上的事务仍处于被节制的状态。换言之,GetSmartCard 输出的卡句柄仍然对卡拥有独 占的会见权限。这么做不只仅是为了将一切出此刻您眼前。请留意,示例应用措施的总体 流首先要通过托管的加密 API 对数据加密,然后执行所有须要的卡模块事情,包罗写回 和读取文件,最后通过加密 API 将数据解密。

操纵的顺序突出了我称之为事务管 理的几个需要留意的重要问题。第一个需要留意的问题是,在挪用 PC/SC 和卡模块例程 时,我但愿在将独立事务的数量降到最低的同时按照需要执行尽大概多的操纵。换言之, 我要尽大概淘汰释放和从头获取与智能卡之间的独有通信链接的次数。通过在单个事务完 成所有挪用,消除了别的一个应用措施参与并使卡处于未知状态的大概性。

然而 ,这个计策也只能有这么大浸染;第二个要留意的问题是加密 API 本身举办事务打点。 譬喻,我从卡读出加密文件之后,在挪用托管的加密 API 对文件举办解密之前,必需调 用 SCardEndTransaction。不然,对 RSACryptoServiceProvider.Decrypt 的挪用将永远 遏制在深处 CSP 内部的 SCardBeginTransaction 上。

第三个需要留意的事务管 理问题(也是一个重要的安详问题)是,卡在举办身份验证的进程中,必需保持锁定状态 。操作卡模块接口,当正确的 PIN 通过 CardAuthenticatePin 传送后,卡便通过了身份 验证。我已经通过 MgScCardAuthenticatePin 的 P/Invoke 接话柄现这一操纵。PC/SC 安详模子是利用以下顺序对卡执行权限操纵的应用措施。

通过 SCardBeginTransaction 锁定卡。

对卡举办身份验证。我利用 CardAuthenticatePin 完成此操纵。

执行权限操纵。在此示例中,我首先实验从 卡中删除一个文件,然后实验写入一个文件。两个操纵都要求举办身份验证。

取 消对卡的身份验证。

通过 SCardBeginTransaction 解锁卡。

这样,未经 授权的应用措施不能向由已授权应用措施验证过的卡发送权限呼吁。

#p#分页标题#e#

某些卡不支 持直接打消身份验证。应用措施还必需通过 PC/SC 重置卡。重置使卡回到最初的、未经 身份验证的状态。固然重置有效地“从头启动”了智能卡,可是这会对下一个 实验与卡举办通信的主机应用措施的机能带来负面影响。因此,卡模块接口在设计时回收 了可选的例程,制止了这一问题以及由此带来的机能上的缺陷。

在本机打包措施 函数 MgScCardDeauthenticate 中,假如方针卡模块没有实现 CardDeauthenticate 函数 ,打包措施将返回 ERROR_CALL_NOT_IMPLEMENTED。假如打消身份验证的挪用由于某种原 因失败了,托管应用措施将通知本身在释放当前事务前,必需重置该卡。

if (0 != (result = (Int32) MgSc.MgScCardDeauthenticate(
   mgscContext, MgScUserId.User, 0)))
    cardDispositionOnRelease = PCSCDisposition.SCARD_RESET_CARD;
这与我之前描写的事务打点安详模子是一致 的。

#p#副标题#e#

卡模块接口设计根基道理

应用措施与智能卡之间交互的最终细节是最 简朴的,上文已提要先容。那就是,要实现将数字许可证文件安排在卡上的与存储相关的 逻辑,首先通过 P/Invoke 挪用本机 MgScCardDeleteFile。出于轻便的思量,我没有检 查返回的值,这是因为,假如这是第一次对文件不存在的卡执行该应用措施,API 将返回 一个错误。

下一步,挪用 CreateFile 和 WriteFile 的打包措施。我们一起来学 习卡模块 API 的设计根基道理,这很是有趣。独立的 CardCreateFile 和 CardWriteFile 例程提供了复制 CREATE_NEW 和 OPEN_EXISTING 语义的简朴要领,语义 包罗相应的错误条件,由人们熟悉的 Windows CreateFile API 提供。这一点很是重要, 由于编程人员可以利用在文件 I/O 中概略把握的模式与卡举办交互,因此这样做低落了 编程人员事情的难度。

但与 Windows CreateFile 函数差异,CardCreateFile 将 字节计数参数作为输入。有些卡模块会忽略该参数,但应用措施传入文件的巨细最好不超 过答允建设的文件的最大巨细的底限。这么做的原因是因为有些卡必需在建设时预先分派 整个文件。另一方面,您也不想挥霍空间。这一设计中一个大概令人狐疑的问题是, CardReadFile 大概返回文件的整体分派空间的巨细(通报给 CardCreateFile 的大概较 大的值),而不只仅是通过 CardWriteFile 写入的大概较小的值。

将加密的许可 证写入卡之后,我通过 MgScCardReadFile 读取它。正式的应用措施不执行这一步调,我 这么做是出于说明问题和测试接口的目标。接下来的步调是解密文件,将其与原始值较量 ,举办完整性查抄。

利用 CLR 加密

请留意,我在示例代码中做的第一件 事是通过 .NET 加密 API 绑定到基于智能卡的 RSA 密钥对。(拜见 System.Security.Cryptography 定名空间中 RSACryptoServiceProvider 类的文档。)

尽量 .NET 提供的抽象技能和强大的成果很不错,但我老是想要看看详细产生了 什么。由于 .NET 加密 API 是基于本机加密 API 的,我可以对此代码做一些假定(代码 是从 GetKey 辅佐器例程改写而来的):

CspParameters cspParameters = new CspParameters(
  1, "Microsoft Base Smart Card Crypto Provider");
cspParameters.Flags = CspProviderFlags.UseExistingKey;
cspParameters.KeyContainerName = "";
rsa = new RSACryptoServiceProvider (cspParameters);

我可以假定此代码至少应挪用 CryptAcquireContext 和 CryptGetUserKey。假如这样不可,则将 cspParameters.Flags 配置为零并再次实验。这 将导致在挪用 CryptAcquireContext 后,挪用 CryptGenKey。

依赖干系和测试

我可以进一步做出 CSP 实际在执行什么操纵的断言。这将易于建设示例应用措施 的组件依赖干系图。我挪用的 CSP 实际是在卡模块接口上构建的;这是首选的写入卡的 主要根基道理。而它是智能卡 CSP 这一事实体现,它大概在直接挪用卡模块例程之前, 利用与示例代码雷同的方法,成立到智能卡的毗连。

换言之,在对上文接头过的 SCardUIDlgSelectCardW、SCardBeginTransaction、SCardStatus 和 CardAcquireContext 举办显式挪用前,CSP 或多或少也在内部举办同样的挪用。出格是 思量到仅为挪用 CardAcquireContext 而需要举办配置的事情量,这种反复事情显然不受 接待。假如对卡模块举办直接挪用的上下文信息可以从加密 API 检索或至少通过某些共 享代码发布这些信息,那就更好了。

#p#分页标题#e#

图 4 描写了这些依赖干系。图的底层代表来 自 winscard.dll 客户端 API 智能卡仓库中的所有内容,上至资源打点器处事,下至驱 动措施,以及智能卡。

智能存储:通过托管代码和Windows Vista智能卡API来呵护您的数据

图 4 措施样品的依赖干系

我已经具体接头了一些智能卡的基本常识和测试应用措施, 我但愿您下载示例代码并体验一下。要构建和运行示例代码需要几项特定的内容:

最新版本的 Windows Vista 平台 SDK 中的 cardmod.h 头文件。

基于卡 模块的智能卡。即已由供给商提供了卡模块的智能卡。

智能卡读卡器。在某些情 况下,诸如特定的 USB 密钥、智能卡和读卡器等是一体的。

根基智能卡加密提供 措施 (Base Smart Card Crypto Provider)。可通过 Windows Update 下载。单击 “自界说”按钮,并选择左侧的“软件”、“可选”。

Code download available at: SmartCards2006_11.exe (2079 KB)

http://download.microsoft.com/download/f/2/7/f279e71e-efb0-4155-873d- 5554a0608523/SmartCards2006_11.exe

 

    关键字:

天才代写-代写联系方式