智能卡体系结构

本主题面向 IT 专业人员介绍在 Windows 操作系统中支持智能卡的系统体系结构,包括凭据提供程序体系结构和智能卡子系统体系结构。

身份验证是用于验证对象或人员身份的过程。 对对象(例如智能卡)进行身份验证时,目标是验证该对象是否为正版。 当你对某人进行身份验证时,目标是验证你是否没有与冒名顶替者打交道。

在网络上下文中,身份验证是向网络应用程序或资源证明标识的行为。 通常,加密操作使用只有用户知道 (密钥(例如公钥加密) 或共享密钥)来证明标识。 身份验证交换的服务器端将签名的数据与已知的加密密钥进行比较,以验证身份验证尝试。 将加密密钥存储在安全的中心位置可缩放且可维护身份验证过程。

对于智能卡,Windows 支持满足安全身份验证要求且可扩展的提供程序体系结构,以便你可以包括自定义凭据提供程序。 本主题包括有关以下内容的信息:

凭据提供程序体系结构

下表列出了交互式登录体系结构中包含的组件:

组件 描述
Winlogon 提供交互式登录基础结构。
登录 UI 提供交互式 UI 呈现。
凭据提供程序 (密码和智能卡) 描述凭据信息和序列化凭据。
本地安全机构 (LSA) 处理登录凭据。
身份验证包 包括 NTLM 和 Kerberos 协议。 与服务器身份验证包通信以对用户进行身份验证。

当用户按 Ctrl+ALT+DEL 时,Windows 中的交互式登录开始。 CTRL+ALT+DEL 组合键称为安全注意序列 (SAS) 。 为了防止其他程序和进程使用它,Winlogon 会在启动过程中注册此序列。

收到 SAS 后,UI 会从从已注册凭据提供程序接收的信息生成登录磁贴。 下图显示了 Windows 操作系统中凭据提供程序的体系结构。

凭据提供程序体系结构。

通常,使用本地帐户或域帐户登录到计算机的用户必须输入用户名和密码。 这些凭据用于验证用户的标识。 对于智能卡登录,用户的凭据包含在智能卡的安全芯片上。 智能卡阅读器允许计算机与智能卡上的安全芯片进行交互。 当用户使用智能卡登录时,他们输入个人标识号 (PIN) ,而不是用户名和密码。

凭据提供程序是进程内 COM 对象,在本地系统上运行并用于收集凭据。 登录 UI 提供交互式 UI 呈现,Winlogon 提供交互式登录基础结构,凭据提供程序使用这两个组件来帮助收集和处理凭据。

Winlogon 指示登录 UI 在收到 SAS 事件后显示凭据提供程序磁贴。 登录 UI 会查询每个凭据提供程序要枚举的凭据数。 凭据提供程序可以选择将其中一个磁贴指定为默认值。 所有提供程序枚举其磁贴后,登录 UI 会向用户显示这些磁贴。 用户与磁贴交互以提供正确的凭据。 登录 UI 提交这些凭据进行身份验证。

凭据提供程序与支持硬件相结合,可以扩展 Windows 操作系统,使用户能够使用生物识别 ((例如指纹、视网膜或语音识别) 、密码、PIN、智能卡证书或任何自定义身份验证包)登录。 企业和 IT 专业人员可以为所有域用户开发和部署自定义身份验证机制,并且可能会显式要求用户使用此自定义登录机制。

注意

凭据提供程序不是强制机制。 它们用于收集和序列化凭据。 LSA 和身份验证包强制实施安全性。

凭据提供程序可以设计为支持单一登录 (SSO) 。 在此过程中,他们通过使用 RADIUS 和其他技术) 登录到计算机, (安全网络接入点对用户进行身份验证。 凭据提供程序还设计为支持特定于应用程序的凭据收集,它们可用于对网络资源进行身份验证、将计算机加入域,或提供管理员同意用户帐户控制 (UAC) 。

一台计算机上可以共存多个凭据提供程序。

凭据提供程序必须在运行 Windows 的计算机上注册,并且它们负责:

  • 描述身份验证所需的凭据信息
  • 处理与外部身份验证机构的通信和逻辑
  • 打包用于交互式登录和网络登录的凭据

注意

凭据提供程序 API 不呈现 UI。 它描述了需要呈现的内容。
只有密码凭据提供程序在安全模式下可用。
在网络期间,智能卡凭据提供程序在安全模式下可用。

智能卡子系统体系结构

供应商提供智能卡和智能卡阅读器,在许多情况下,供应商对于智能卡和智能卡阅读器是不同的。 智能卡读取器的驱动程序将写入个人计算机/智能卡 (PC/SC) 标准。 每个智能卡都必须具有加密服务提供商 (CSP) ,该提供程序使用 CryptoAPI 接口启用加密操作,以及 WinSCard API 来启用与智能卡硬件的通信。

基本 CSP 和智能卡微型驱动程序体系结构

下图显示了 CryptoAPI、CSP、智能卡基础加密服务提供程序 (基本 CSP) 与智能卡微型驱动程序之间的关系。

基础 CSP 和智能卡微型驱动程序体系结构。

使用基本 CSP 和智能卡 KSP 进行缓存

智能卡体系结构使用缓存机制来帮助简化操作并改进用户对 PIN 的访问。

  • 数据缓存:数据缓存提供单个进程,以最大程度地减少智能卡 I/O 操作
  • PIN 缓存:PIN 缓存可帮助用户在每次智能卡未经身份验证时重新输入 PIN

数据缓存

每个 CSP 分别实现当前的智能卡数据缓存。 基本 CSP 实现可靠的缓存机制,使单个进程能够最大程度地减少智能卡 I/O 操作。

现有全局缓存的工作方式如下:

  1. 应用程序请求加密操作。 例如,将从智能卡
  2. CSP 检查其缓存中的项
  3. 如果在缓存中找不到该项,或者该项已缓存但不是最新的,则会从智能卡
  4. 从智能卡读取任何项后,会将其添加到缓存中。 将替换该项目的任何现有过期副本

CSP 缓存了三种类型的对象或数据:引脚 (有关详细信息,请参阅 PIN 缓存) 、证书和文件。 如果任何缓存数据发生更改,则会在连续操作中从智能卡读取相应的对象。 例如,如果文件写入智能卡,则 CSP 缓存将过期文件,其他进程至少读取智能卡一次以刷新其 CSP 缓存。

全局数据缓存托管在适用于 Windows 的智能卡服务中。 Windows 包括两个公共智能卡 API 调用:SCardWriteCache 和 SCardReadCache。 这些 API 调用使全局数据缓存功能可供应用程序使用。 每个符合智能卡微型驱动程序规范的智能卡都有一个 16 字节卡标识符。 此值用于唯一标识与给定智能卡相关的缓存数据。 使用标准 Windows GUID 类型。 这些 API 允许应用程序将数据添加到全局缓存以及从全局缓存读取数据。

PIN 缓存

每次智能卡未经身份验证时,PIN 缓存都会保护用户输入 PIN。 智能卡经过身份验证后,不会区分主机端应用程序,任何应用程序都可以访问智能卡上的专用数据。

为了缓解此问题,当应用程序向智能卡进行身份验证时,智能卡将进入独占状态。 但是,这意味着其他应用程序无法与智能卡通信,并且会被阻止。 因此,这种独占连接是最小化的。 问题是,协议 ((如 Kerberos 协议) )需要执行多个签名操作。 因此,该协议需要长时间以独占方式访问智能卡,或者需要执行多个身份验证操作。 在这里,PIN 缓存用于最大程度地减少智能卡的独占使用,而不会强制用户多次输入 PIN。

以下示例演示了其工作原理。 在此方案中,有两个应用程序:Outlook 和 Internet Explorer。 应用程序将智能卡用于不同的目的。

  1. 用户启动 Outlook 并尝试发送已签名的电子邮件。 私钥位于智能卡
  2. Outlook 提示用户输入智能卡 PIN。 用户输入正确的 PIN
  3. 电子邮件数据将发送到智能卡进行签名操作。 Outlook 客户端格式化响应并发送电子邮件
  4. 用户打开 Internet Explorer 并尝试访问需要客户端的传输层安全性 (TLS) 身份验证的受保护站点
  5. Internet Explorer 提示用户输入智能卡 PIN。 用户输入正确的 PIN
  6. 与 TLS 相关的私钥操作发生在智能卡上,并且用户已经过身份验证并登录
  7. 用户返回到 Outlook 以发送另一个已签名的电子邮件。 这一次,不会提示用户输入 PIN,因为该 PIN 是从上一个操作缓存的。 同样,如果用户再次使用 Internet Explorer 执行另一个操作,Internet Explorer 不会提示用户输入 PIN

基本 CSP 在内部维护 PIN 的每个进程缓存。 PIN 已加密并存储在内存中。 用于保护 PIN 的函数是 RtlEncryptMemory、RtlDecryptMemory 和 RtlSecureZeroMemory,这将清空包含 PIN 的缓冲区。

智能卡选择

本文的以下部分介绍了 Windows 如何使用智能卡体系结构来选择正确的智能卡阅读器软件、提供程序和凭据,以便成功登录智能卡:

容器规范级别

为了响应 CryptoAPI 中的 CryptAcquireContext 调用,基本 CSP 尝试将调用方指定的容器与特定的智能卡和读取器匹配。 调用方可以提供具有不同特定级别的容器名称,如下表所示,并按最特定的请求到最不具体的请求进行排序。

同样,为了响应 CNG 中的 NCryptOpenKey 调用,智能卡 KSP 尝试以相同的方式匹配容器,并且采用相同的容器格式,如下表所示。

注意

在使用智能卡 KSP 打开密钥之前,必须调用 NCryptOpenStorageProvider (MS_SMART_CARD_KEY_STORAGE_PROVIDER) 。

Type 名称 Format
I 读取器名称和容器名称 \.<Reader Name><Container Name>
第二 读取器名称和容器名称 (NULL) \.<Reader Name>
第三 仅容器名称 <Container Name>
IV 默认容器 (仅限 NULL)

基本 CSP 和智能卡 KSP 缓存智能卡处理有关调用进程以及进程已访问的智能卡的信息。 搜索智能卡容器时,基本 CSP 或智能卡 KSP 首先检查其缓存中是否有进程。 如果缓存的句柄无效或找不到匹配项,则调用 SCardUIDlg API 以获取卡句柄。

容器操作

可以使用 CryptAcquireContext 请求以下三个容器操作:

  1. 创建新容器。 (将 dwFlags 设置为 CRYPT_NEWKEYSET 的 CryptAcquireContext 的 CNG 等效项是 NCryptCreatePersistedKey.)
  2. 打开现有容器。 (用于打开容器的 CryptAcquireContext 的 CNG 等效项是 NCryptOpenKey.)
  3. 删除容器。 (将 dwFlags 设置为 CRYPT_DELETEKEYSET 的 CryptAcquireContext 的 CNG 等效项是 NCryptDeleteKey.)

用于将加密句柄与特定智能卡和读取器关联的启发式方法基于所请求的容器操作和使用容器规范的级别。

下表显示了容器创建操作的限制。

规范 限制
无静上下文 密钥容器创建必须始终能够显示 UI,例如 PIN 提示。
不覆盖现有容器 如果所选智能卡上已存在指定的容器,请选择另一个智能卡或取消操作。

上下文标志

下表显示了用作容器创建操作限制的上下文标志。

旗帜 描述
CRYPT_SILENT 此操作期间无法显示任何 UI。
CRYPT_MACHINE_KEYSET 此操作期间不应使用缓存数据。
CRYPT_VERIFYCONTEXT 在智能卡上只能访问公共数据。

除了容器操作和容器规范外,还必须在智能卡选择过程中考虑其他用户选项,例如 CryptAcquireContext 标志。

重要提示

CRYPT_SILENT 标志不能用于创建新容器。

在无提示上下文中创建新容器

应用程序可以使用 调用基本 CSP CRYPT_DEFAULT_CONTAINER_OPTIONAL,在无提示上下文中设置 PIN,然后在无提示上下文中创建新的容器。 此操作如下所示:

  1. 通过将 中的智能卡读取器名称作为 II 类型容器规范级别传递并指定CRYPT_DEFAULT_CONTAINER_OPTIONAL标志来调用 CryptAcquireContext
  2. 通过指定 PP_KEYEXCHANGE_PINPP_SIGNATURE_PIN 和以 null 结尾的 ASCII PIN 来调用 CryptSetProvParam。
  3. 释放步骤 1 中获取的上下文
  4. 使用 CRYPT_NEWKEYSET调用 CryptAcquireContext,并指定类型 I 容器规范级别
  5. 调用 CryptGenKey 以创建密钥

智能卡选择行为

在以下某些情况下,系统可能会提示用户插入智能卡。 如果用户上下文为无提示,则此操作将失败,并且不会显示任何 UI。 否则,在响应 UI 时,用户可以插入智能卡或选择“取消”。 如果用户取消操作,操作将失败。 流程图显示 Windows 操作系统执行的选择步骤。

智能卡选择过程。

通常,智能卡选择行为由 SCardUIDlgSelectCard API 处理。 基本 CSP 通过直接调用此 API 来与它交互。 基本 CSP 还会发送用于筛选和匹配候选智能卡的回调函数。 CryptAcquireContext 的调用方提供智能卡匹配信息。 在内部,基本 CSP 使用智能卡序列号、读取器名称和容器名称的组合来查找特定的智能卡。

每次调用 都SCardUI *可能导致从候选智能卡读取其他信息。 基本 CSP 智能卡选择回调缓存此信息。

使智能卡阅读器匹配

对于类型 I 和类型 II 容器规范级别,智能卡选择过程不太复杂,因为只有命名读取器中的智能卡才能被视为匹配。 将智能卡与智能卡阅读器进行匹配的过程是:

  1. 查找请求的智能卡读取器。 如果找不到,则该过程将失败, (这需要按读取器名称)
  2. 如果读取器中没有智能卡,系统会提示用户插入智能卡。 (仅处于非静态模式;如果在无提示模式下进行调用,则调用失败)
  3. 仅对于容器规范级别 II,将确定所选智能卡上默认容器的名称
  4. 若要打开现有容器或删除现有容器,请找到指定的容器。 如果在此智能卡上找不到指定的容器,系统会提示用户插入智能卡
  5. 如果系统尝试创建新容器,如果指定的容器已存在于此智能卡上,则进程将失败

进行智能卡匹配

对于容器规范级别 III 和 IV,使用更广泛的方法将适当的智能卡与用户上下文匹配,因为多个缓存智能卡可能满足提供的条件。

(未指定读取器) 打开现有默认容器

注意

此操作要求将智能卡与基本 CSP 配合使用。

  1. 对于基本 CSP 访问的每个智能卡以及缓存句柄和容器信息,基础 CSP 会查找有效的默认容器。 尝试对缓存的 SCARDHANDLE 执行操作,以验证其有效性。 如果智能卡句柄无效,则基本 CSP 将继续搜索新的智能卡
  2. 如果在基本 CSP 缓存中找不到匹配的智能卡,则基本 CSP 会调用智能卡子系统。 SCardUIDlgSelectCard () 与适当的回调筛选器一起使用,以查找具有有效默认容器的匹配智能卡

(未指定读取器) 打开以 GUID 命名的现有容器

注意

此操作要求将智能卡与基本 CSP 配合使用。

  1. 对于已注册到基本 CSP 的每个智能卡,搜索请求的容器。 尝试对缓存的 SCARDHANDLE 执行操作,以验证其有效性。 如果智能卡句柄无效,智能卡的序列号将SCardUI *传递给 API,以继续搜索此特定智能卡 (而不是仅搜索容器名称的常规匹配)
  2. 如果在基本 CSP 缓存中找不到匹配的智能卡,则会调用智能卡子系统。 SCardUIDlgSelectCard()与相应的回调筛选器一起使用,以查找与所请求容器匹配的智能卡。 或者,如果智能卡序列号是在步骤 1 中搜索生成的,则回调筛选器会尝试匹配序列号,而不是容器名称

(未指定读取器) 创建新的容器

注意

此操作要求将智能卡与基本 CSP 配合使用。

如果未缓存 PIN,则不允许为容器创建CRYPT_SILENT,因为必须至少提示用户输入 PIN。

对于其他操作,调用方可能能够获取针对默认容器CRYPT_DEFAULT_CONTAINER_OPTIONAL验证上下文,然后使用 CryptSetProvParam 进行调用,以缓存用户 PIN 以执行后续操作。

  1. 对于 CSP 已知的每个智能卡,请刷新存储的 SCARDHANDLE 并进行以下检查:
    1. 如果智能卡已删除,请继续搜索
    2. 如果智能卡存在,但它已具有命名容器,请继续搜索
    3. 如果智能卡可用,但对 CardQueryFreeSpace 的调用指示智能卡存储不足,无法再使用密钥容器,请继续搜索
    4. 否则,请使用满足上述容器创建条件的第一个可用的智能卡
  2. 如果在 CSP 缓存中找不到匹配的智能卡,请调用智能卡子系统。 用于筛选枚举智能卡的回调将验证候选智能卡是否还没有命名容器,并且 CardQueryFreeSpace 指示智能卡有足够的空间用于其他容器。 如果未找到合适的智能卡,系统会提示用户插入智能卡

删除容器

  1. 如果指定的容器名称为 NULL,则删除默认容器。 删除默认容器会导致任意选择新的默认容器。 因此,不建议执行此操作
  2. 对于 CSP 已知的每个智能卡,请刷新存储的 SCARDHANDLE 并进行以下检查:
    1. 如果智能卡没有命名容器,请继续搜索
    2. 如果智能卡具有命名容器,但智能卡句柄不再有效,请存储匹配智能卡的序列号并将其传递给 SCardUI
  3. 如果在 CSP 缓存中找不到匹配的智能卡,请调用智能卡子系统。 用于筛选枚举智能卡的回调应验证候选智能卡是否具有命名容器。 如果由于以前的缓存搜索而提供了序列号,则回调应根据序列号而不是容器匹配项筛选枚举智能卡。 如果上下文非无提示且找不到合适的智能卡,则显示提示用户插入智能卡

Windows 中基于基础 CSP 和 KSP 的体系结构

下图显示了 Windows 操作系统使用的加密体系结构。

加密体系结构。

Windows 中的基本 CSP 和智能卡 KSP 属性

注意

API 定义位于 WinCrypt.h 和 WinSCard.h 中。

属性 描述
PP_USER_CERTSTORE - 用于返回HCERTSTORE包含智能卡上所有用户证书的
- 只读 (仅由 CryptGetProvParam) 使用
- 负责关闭证书存储的调用方
- 使用 PKCS_7_ASN_ENCODING 或 编码的证书 X509_ASN_ENCODING
- CSP 应在证书上设置KEY_PROV_INFO
- 应假定证书存储是内存中存储
- 证书应具有有效的 CRYPT_KEY_PROV_INFO 属性
PP_ROOT_CERTSTORE - 读取和写入) 使用的 CryptGetProvParam (CryptSetProvParam
- 用于将根证书的集合写入智能卡或返回 HCERTSTORE,其中包含来自智能卡
- 主要用于使用智能卡加入域
- 负责关闭证书存储的调用方
PP_SMARTCARD_READER - 只读 (仅由 CryptGetProvParam) 使用
- 返回智能卡读取器名称作为 ANSI 字符串,该字符串用于构造完全限定的容器名称 (即智能卡读取器加上容器)
PP_SMARTCARD_GUID - 返回智能卡 GUID (也称为序列号) ,对于每个智能卡
- 证书传播服务用于跟踪根证书的源
PP_UI_PROMPT - 用于设置卡插入对话框的SCardUIDlgSelectCard搜索字符串
- 设置后,整个过程持久化
- 仅供) 使用的 CryptSetProvParam 只写 (

对 Windows 中的 CSP 的影响

加密服务提供商 (CSP) (包括自定义智能卡 CSP)继续受支持,但不建议使用此方法。 将现有基本 CSP 和智能卡 KSP 与智能卡的智能卡微型驱动程序模型配合使用,在性能、PIN 和数据缓存方面具有显著优势。 可以将一个微型驱动程序配置为在 CryptoAPI 和 CNG 层下工作。 这可以从增强的加密支持中受益,包括椭圆曲线加密和 AES。

如果云解决方案提供商和智能卡微型驱动程序注册了智能卡,则最近安装的智能卡将用于与智能卡通信。

编写智能卡微型驱动程序、CSP 或 KSP

CSP 和 KSP 仅在当前智能卡微型驱动程序体系结构中不提供特定功能时编写。 例如,智能卡微型驱动程序体系结构支持硬件安全模块,因此可以为硬件安全模块编写微型驱动程序,并且可能不需要 CSP 或 KSP,除非需要它来支持基础 CSP 或智能卡 KSP 中未实现的算法。

有关如何编写智能卡微型驱动程序、CSP 或 KSP 的详细信息,请参阅智能卡微型驱动程序