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

适用于 .NET Compact Framework 的智能卡框架

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.38/5 (3投票s)

2007年1月30日

CPOL

5分钟阅读

viewsIcon

72401

downloadIcon

1226

本文介绍了一个框架,用于在 PocketPC 上的 .NET 中使用 PCSC 智能卡 API。

引言

在我之前几篇文章中,我介绍了一个 .NET API 和一个简单的 XML 框架,用于为 .NET 环境编写智能卡应用程序。当我最初开发这个框架时,我也制作了一个 Compact Framework 版本,特别是针对 PocketPC 的。由于收到读者关于 CE 版本的请求,我决定写这篇短文介绍它。我建议您先阅读我的另外两篇文章,A .NET API for PC/SCAn XML framework for Smart card application,因为我只描述了 CF 版本与完整 .NET 2.0 版本相比的主要区别。

一个简单的智能卡框架

智能卡类的 CF 版本与我之前介绍的版本非常相似。您可以在下面找到用于管理卡通信和命令格式的接口和基类。

public interface ICard
{
    string[] ListReaders();
    void Connect(string Reader, SHARE ShareMode, 
                 PROTOCOL PreferredProtocols);
    void Disconnect(DISCONNECT Disposition);
    APDUResponse Transmit(APDUCommand ApduCmd);
} 

APDUCommandAPDUResponse 类用于发送命令并从卡获取响应。SHARE, <code>PROTOCOLDISCONNECT 是 PC/SC 使用的常量枚举。

public class APDUCommand
{
    public APDUCommand(byte bCla, byte bIns, byte bP1, byte bP2, 
                       byte[] baData, byte bLe);
    public void Update(APDUParam apduParam);
    public override string ToString();
    public byte    Class;
    public byte    Ins;
    public byte    P1;
    public byte    P2;
    public byte[]  Data;
    public byte    Le;
}
    
public class APDUResponse
{
    public APDUResponse(byte[] baData);
    public byte[]    Data;
    public byte    SW1;
    public byte    SW2;
    public ushort    Status;
    public override string ToString();
}

PocketPC 版本不提供任何事件管理,并且与 PC 版本不同的是,没有使用 PC/SC 的 COM 版本实现,因为 CE 上不存在 COM 版本,而且 Compact Framework 也不支持 COM 互操作。

因此,ICard 接口的唯一实现是 NativeCard 类,该类使用 .NET 的本地互操作功能。

CardNative:一个使用 P/Invoke 的本地互操作实现类

平台调用机制 (P/Invoke) 是一个非常强大的机制,它提供了对 Win32 平台 API 的完全访问。 .NET 框架提供了一整套类,您可以使用它们来实现调用 .NET 中的 Win32 函数所需的任何封送操作。这些类定义在 System.Runtime.InteropServices 程序集中,您只需要将其导入您的程序中。P/Invoke 机制即使有点复杂,也比 Java 的 JNI 机制方便得多。所有原子类型,如 int、byte、long 等,都会被编译器自动封送。byte[] 也会被自动封送为输入或输出参数。当您需要处理更复杂的参数时,如字符串、结构体或指向结构体的指针,.NET 提供了一系列封送类,在封送参数时可以在您的程序中用作属性或对象。为了开发 CardNative 类,我不需要开发任何额外的代码来使用 PC/SC API,所有代码都在源文件 CardNative.cs 中。当您想使用 Win32 API C 函数时,您需要在类中使用 System.Runtime.InteropServices 程序集提供的互操作属性来声明该函数。一旦您声明了函数,就可以像使用任何 C# 方法一样在您的类中使用它。以下代码示例说明了这种机制。

PC 版本和 PocketPC 版本之间的主要区别在于 PC/SC 函数位于不同的 DLL 中,并且除了字节数组之外,通过指针传递的参数不会自动封送。Compact Framework 也不提供一些内存分配函数,您需要使用 C 的原生函数。

您需要导入的内存函数。

[DllImport("coredll.dll", SetLastError=true)]
internal static extern IntPtr    LocalAlloc(UInt32 uFlags, UInt32 uBytes);

[DllImport("coredll.dll", SetLastError=true)]
internal static extern IntPtr LocalFree(IntPtr hMem);

PC/SC 原生函数的声明

[DllImport("scardce.dll", SetLastError=true, CharSet=CharSet.Auto )]
internal static extern int SGCardListReaders(UInt32 hContext, 
                          IntPtr mszReaders, out UInt32 pcchReaders);

[DllImport("scardce.dll", SetLastError=true)]
 internal static extern int SGCardEstablishContext(UInt32 dwScope, 
                                                   IntPtr phContext);

[DllImport("scardce.dll", SetLastError=true)]
internal static extern int SGCardReleaseContext(UInt32 hContext);

[DllImport("scardce.dll", SetLastError=true, CharSet=CharSet.Auto)]
internal static extern int SGCardConnect(UInt32 hContext, 
    byte[] szReader, 
    UInt32 dwShareMode, 
    UInt32 dwPreferredProtocols,
    IntPtr phCard, 
    IntPtr pdwActiveProtocol);

[DllImport("scardce.dll", SetLastError=true)]
internal static extern int SGCardDisconnect(UInt32 hCard, 
                                            UInt32 dwDisposition);

[DllImport("scardce.dll", SetLastError=true)]
internal static extern int SGCardTransmit(UInt32 hCard, 
    [In] ref SCard_IO_Request pioSendPci, 
    byte[] pbSendBuffer,
    UInt32 cbSendLength,
    IntPtr pioRecvPci,
    [Out] byte[] pbRecvBuffer,
    out UInt32 pcbRecvLength);

如果您查看 PC/SC 函数的声明,您会注意到与 PC 声明存在一些差异。例如,字符串没有自动封送,您需要将其作为字节数组处理。其余的声明是相同的。

以下代码展示了如何将字符串传递给 PC/SC 函数 SCardConnect

public void Connect(string Reader, SHARE ShareMode, 
                    PROTOCOL PreferredProtocols)
{
    IntPtr    hCard = IntPtr.Zero;
    IntPtr    pProtocol = IntPtr.Zero;

    try
    {
        if (m_hContext == 0)
            EstablishContext(SCOPE.User);

        // Allocate the memory to receive the pointer's data
        hCard = LocalAlloc(0, (UInt32) Marshal.SizeOf(m_hCard));
        pProtocol = LocalAlloc(0, (UInt32) Marshal.SizeOf(m_nProtocol));

        // Get a byte array version of the string 
        byte[] baReader = Encoding.Unicode.GetBytes(Reader);
        m_nLastError = SGCardConnect(m_hContext, 
            baReader, 
            (uint) ShareMode, 
            (uint) PreferredProtocols, 
            hCard,
            pProtocol);

        if (m_nLastError != 0)
        {
            string msg = "SCardConnect error: " + m_nLastError;

            throw new Exception(msg);
        }

        m_hCard = (uint) Marshal.ReadInt32(hCard);
        m_nProtocol = (uint) Marshal.ReadInt32(pProtocol);
    }
    catch
    {
        throw;
    }
    finally
    {
        LocalFree(hCard);
        LocalFree(pProtocol);
    }
}

这段代码非常有趣,因为它展示了 PC 版本和 PocketPC 版本之间存在的所有差异。除了字符串管理之外,您可以看到,要获取卡句柄 (m_hCard) 和协议 (pProtocol),您首先需要使用原生分配函数分配内存来获取它们。在函数退出时,如果成功,您然后需要像 PC 版本一样,从内存中获取数据。

演示应用程序:PocketPC 的 Exchange APDU 应用程序

当使用应用程序中的 ICard 接口时,与 PC 版本几乎没有区别,除了 .NET Compact Framework 存在的一些限制。

我设计了一个简单的 Exchange APDU 应用程序,它使用了我旧版本的 APDUList.xml(APDU 声明有一些小的差异)。您可能遇到的唯一问题是市场上可用的智能卡读卡器不多。我开发这个应用程序使用的是嵌入 SD 卡的智能卡以及插入 SD 卡槽的小型智能卡读卡器。我知道 iPaq 也有一个扩展,可以插入 PCI 智能卡读卡器。

这是使用 Visual Studio 模拟器截取的屏幕截图,不幸的是,该模拟器无法连接 PC 的智能卡读卡器。

关注点

这篇短文展示了在 PC 或 PocketPC 上开发相同类型的应用程序时可能存在的差异。即使差异比使用 C++ 等语言时要小,但由于 CF 是 .NET 框架的子集,并且互操作支持并未完全实现,因此要达到相同的功能级别可能会相当困难。

© . All rights reserved.