65.9K
CodeProject 正在变化。 阅读更多。
Home

使用 Gemalto .NET 智能卡 V2 构建您自己的安全应用程序

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.86/5 (15投票s)

2011 年 10 月 2 日

CPOL

7分钟阅读

viewsIcon

60010

downloadIcon

2404

如何在 .NET 中使用 Gemalto .NET V2 智能卡创建安全应用程序

引言

在本文中,您将学习如何使用运行 .NET CLR 的智能卡,并将其用于安全存储操作和加密操作。在上一篇文章中,我描述了如何将智能卡证书存储与 .NET Framework 安全 API 配合使用。那么,使用 CSP API 和使用 .NET 卡实现相同操作有什么区别呢?答案是极大的灵活性和增强的安全性。

背景

.NET 智能卡 V2 相当于 Microsoft 生态系统中的 Java 卡。我以前使用过 Java 卡,而这张 .NET 卡为 APDU 协议的限制带来了一些创新解决方案。您写入卡中的应用程序通过 .NET 远程处理技术的实现暴露给客户端应用程序。通过此协议,卡成为一个运行 .NET 应用程序的微型服务器。当然,存在一些限制,我将尝试给出其中一些。

Gemalto .NET V2

智能卡和 USB 读卡器。

重要

为了使用本文中的代码,您需要拥有一张 Gemalto .NET card v2.2 和 .NET Smartcard SDK V2.2,后者集成了 Visual Studio 2008 的开发。您可以从 Gemalto 网上商店 获取一些智能卡和读卡器,但 .NET card SDK V2.2 现在是免费的。

.NET Remoting 与卡上应用程序通信

.NET 卡运行一个占用空间小的 .NET CLR 和 .NET Framework,这意味着您可以开发将在卡内运行并利用卡上 API 的 .NET 程序集。

这张 .NET 智能卡最有趣的特性之一是您不再需要设计和使用 APDU 命令,框架提供了一个 .NET 远程处理管道,允许开发人员将其卡服务公开为简单的远程处理服务。

在卡中,您声明您的服务,然后使用生成的存根与这些服务进行通信。

/// <summary>
/// The On card code project server
/// </summary>
public class CodeProjectServer
{
    /// <summary>
    /// specify the exposed remote object URI.
    /// </summary>
    private const string REMOTE_LOGIN_URI = "LoginStoreService.uri";
    private const string REMOTE_AUTH_URI = "AuthenticationService.uri";

    /// <summary>
    /// Register the server onto the card.
    /// </summary>
    /// <returns></returns>
    public static int Main()
    {
        // Register the channel the server will be listening to.
        ChannelServices.RegisterChannel(new APDUServerChannel());

        // Register the servers           
        RemotingConfiguration.RegisterWellKnownServiceType(typeof(LoginStoreService), 
		REMOTE_LOGIN_URI, WellKnownObjectMode.Singleton);
        RemotingConfiguration.RegisterWellKnownServiceType(typeof(AuthenticationService), 
		REMOTE_AUTH_URI, WellKnownObjectMode.Singleton);

        return 0;
    }
}

在此示例中,当您在卡中执行此应用程序时,将声明并启动两个服务。在 PC 端,您只需编写很少的代码。

private const string AUTHENTICATION_URL = 
		"apdu://selfdiscover/AuthenticationService.uri";

private APDUClientChannel channel = null;
private AuthenticationService authService = null;


public AuthenticationDemo()
{
    channel = new APDUClientChannel();
    ChannelServices.RegisterChannel(channel, false);

    authService = (AuthenticationService)Activator.GetObject
		(typeof(AuthenticationService), AUTHENTICATION_URL);

    aesAlgo = Rijndael.Create();
    aesAlgo.Key = AESKey;
    aesAlgo.IV = AESIv;
}

您现在可以使用 authService 引用来调用 AuthenticationService 的方法。

// Call of the GetAuthenticationSignature method
byte[] encrSignature = authService.GetAuthenticationSignature(encrChallenge);

.NET 智能卡中的密码管理器服务器

在我之前的一份工作中,我曾在一个应用程序上工作,该应用程序使用 PKCS 智能卡安全地存储登录凭据。然后,这些凭据被一个应用程序使用,该应用程序在网络浏览器中自动填充登录信息,并确保银行站点的安全登录,以避免网络钓鱼和欺骗。

将这些数据存储在 PKCS 对象中需要使用 PKCS#11 API,这是一个 C API,并且使用一些不甚灵活的机制。让我们看看如何使用 .NET 智能卡轻松完成此操作。

在智能卡中编程应用程序,即使运行 .NET CLR,如果要获得响应迅速的应用程序,也必须遵守一些规则。首先,您必须考虑卡执行的字节码不是由 JIT 预编译,而是由将其转换为机器码的 VM 执行的。另一个考虑是智能卡芯片的内存容量。这种 .NET 智能卡使用一个具有 16K RAM 的芯片,您有大约 70K 的闪存(非易失性存储器)用于您的程序集、存储和堆的等效物。闪存当然比 RAM 慢得多,这意味着您的应用程序 90% 的时间将运行在硬盘上!(如果您与 PC 进行比较)

根据经验,我发现一些框架实现相当慢,例如,如果需要,可以使用文件系统,但使用 FileStream 读写文件非常慢。由于 static 类成员像文件存储一样存储在闪存中,因此使用 static 类以良好的性能存储数据是一种更好的方法。

设计登录凭据存储类

因为闪存相对较慢,我设计了使用固定大小的 LoginItem 对象数组进行存储。我使用了第二个固定大小的 string 数组,其中包含登录别名列表。因此,当它搜索具有给定名称的 LoginItem 时,它会搜索登录别名并获取索引,然后使用此索引获取 LoginItem。结果是对任何 LoginItem 的快速访问。由于内存有限,使用固定大小的数组不是问题。

以下摘录显示了我用于搜索索引列表的简单算法。每次迭代同时读取数组的两个元素,一个从开头开始,一个从结尾开始。考虑到数组中的数据量,这应该确保在有限的迭代次数内找到一个元素。

public static int FindLogin(string name)
{
    int index = -1;
    for (int n = 0; n < MAX_LOGINS; n++)
    {
        if (loginAlias[n] != null && loginAlias[n] == name)
        {
            index = n;
            break;
        }

        if (loginAlias[MAX_LOGINS - n - 1] != null && 
			loginAlias[MAX_LOGINS - n - 1] == name)
        {
            index = MAX_LOGINS - n - 1;
            break;
        }

        if (n == (MAX_LOGINS - n - 1))
        {
            break;
        }
    }

    return index;
}

添加 PIN 以访问 LoginStoreService

通常,智能卡应用程序受到 CHV 代码(持卡人验证)的保护。验证此代码后,用户可以调用受给定代码保护的方法。智能卡 .NET 框架提供了一个实现 PIN 码行为的 PIN 类。PIN 码经过验证并保持验证状态,直到卡会话关闭或通过调用 Invalidate 方法使 PIN 失效。

以下代码显示了 LoginStoreService 实现类中 PIN 码的声明。

private static PIN pinCode = new PIN(DEFAULT_PIN, 3);

LoginStorageService 类实现了服务本身。该服务具有以下方法:

/// <summary>
/// Get the list of login alias managed by the token
/// </summary>
/// <returns>List of login alias</returns>
public string[] GetLoginAliasList();

/// <summary>
/// Add a login entry
/// </summary>
/// <param name="alias">Login alias name</param>
/// <param name="loginPageUrl">Login page URL</param>
/// <param name="user">User name</param>
/// <param name="password">Password</param>
/// <returns>true if added, false otherwise</returns>
public bool AddLogin(string alias, string loginPageUrl, string user, string password);

/// <summary>
/// Get the login data for a given login alias
/// </summary>
/// <param name="alias">Login alias name</param>
/// <param name="loginPageUrl">[out] Login page URL</param>
/// <param name="user">[out] User name</param>
/// <param name="password">[out] Password</param>
/// <returns>true if found, false otherwise</returns>
public bool GetLogin(string alias, out string loginPageUrl, 
			out string user, out string password);

/// <summary>
/// Remove a login entry
/// </summary>
/// <param name="alias">Login alias name</param>
/// <returns>true if removed, false otherwise</returns>
public bool RemoveLogin(string alias);

这些方法可以通过客户端应用程序中的 .NET 远程处理进行访问。但是,这种 .NET 远程处理实现存在一些限制。只有在智能卡 .NET 框架中定义的类才能用作远程方法签名中的参数,并且只有在此框架中定义的异常才能由服务方法抛出并在客户端应用程序中捕获。

使用 LoginStorageService 的客户端应用程序

现在我们已经在卡中编写了这项服务,是时候使用它了!LoginStoreManager 应用程序是一个简单的 Winforms 应用程序,它使用卡上的 LoginStorageService 来管理卡上的登录信息。这个应用程序不是一个密码管理器,它不会自动注册您的登录/密码并将它们输入到网页中,它只是管理卡中登录/密码的部分。我想您可以很容易地找到一个开源应用程序来完成这部分并将此代码集成到该应用程序中。

Add Login Form

Login Store Manager

.NET 智能卡中的身份验证服务

身份验证是一个常见的安全主题,其中一方必须向另一方证明他就是他所声称的那个人。在此示例中,卡将证明它就是它所声称的那样。
通常,当您想使用证书时,您会输入 PIN,但在某些情况下,您可能不想输入 PIN,例如将此 PIN 用于其他目的,例如卡中的证书管理。

身份验证服务使用 RSA 签名和 AES 加密。挑战必须使用令牌中硬编码的秘密密钥进行加密。签名使用每个令牌唯一的 RSA 密钥完成,然后使用 AES 密钥进行加密。
加密不是必需的(但可能有用),用于演示 .NET 卡与将卡用作 Microsoft CSP 的证书存储库相比的能力。

卡提供了一个简单的身份验证接口

/// <summary>
/// Get the Public of the certificate
/// </summary>
/// <returns>RSAParameters with the public key</returns>
public RSAParameters GetPublicKey();

/// <summary>
/// Get the authentication signature. 
/// 
/// Challenge must be encrypted with the AES key. Signature is encrypted with the 
/// same AES key
/// </summary>
/// <param name="encrChallenge">Encrypted challenge</param>
/// <returns>Encrypted signature of the challenge</returns>
public byte[] GetAuthenticationSignature(byte[] encrChallenge);

以及一些用于非常简单的 PIN 管理的方法

/// <summary>
/// Verify the PIN
/// </summary>
/// <param name="pinValue">PIN value</param>
public void VerifyPIN(string pinValue);

/// <summary>
/// Get/set PIN enable status
/// 
/// When PIN is not enabled, there is no need to Verify it before 
/// calling a method that needs the 
/// PIN to be verified
/// </summary>
public bool PINEnabled;

AuthenticationServiceDemo 控制台应用程序演示了 .NET 卡和应用程序之间的身份验证过程。

Authentication demo

解密/签名/加密的整个过程由智能卡在大约 1/2 秒内完成,这对于这种设备来说是非常正确的性能。这款智能卡采用加密芯片组构建,这解释了其性能,但是您在设计卡应用程序时必须非常小心,并以与 PC 应用程序不同的方式使用 .NET 框架。

关注点

由于此类智能卡的价格在过去几年中大幅下降,如果您组织中有对加密机制和智能卡知识有很好了解的工程师,现在设计使用加密智能卡的安全解决方案具有成本效益。
例如,智能卡技术被最有效的软件许可系统使用加密狗。

我希望您喜欢这篇文章,并且它拓宽了您对这种小型设备所能做的事情的视野!

历史

  • 2011年9月30日:初始版本
  • 2011年10月9日:小更新,包含一个 Gemalto .NET SDK 的链接,该 SDK 现已可在 Gemalto 网站免费获取
© . All rights reserved.