Scard API 智能卡操作

一、概述

ICC是Integrated Circuit Card的缩写,意思是集成电路卡,我们通常把它称为智能卡(Smart Card)。智能卡应用广泛,它可以用来保存私人密码、银行账号、个人资料等。那么如何编写应用程序,从智能卡上读出或向其写入信息呢?其实在Windows 98或Windows NT 4.0及以上版本的Windows操作系统中,就已经能够解决该问题了。本文将就此展开讨论。

二、智能卡了系统

智能卡子系统(Smart Card Subsystem)的基本组件包括:

1.资源管理器(Resource Manager),它使用Win32 API。

2.用户界面(User Interface, UI)。

3.一些基本服务提供者(Base Service Provider),它们提供了对特定服务的访问。与资源管理器不同,服务提供者使用COM接口模型而不是Win32 API。

下图演示了这些组件在智能卡系统中的关系。

三、子系统中的部件

1.智能卡读卡机

智能卡读卡机用来从智能卡中读取或写入信息。读卡机能够被划分为逻辑上的“组”,这些组称为读卡机组(Reader Group)。这些组可以由智能卡子系统定义,也可以由管理员和用户定义。这里将介绍一些管理读卡机组的函数。一台读卡机能够属于一个或多个组。智能卡子系统已经定义了下列两个组:

Scard$AllReaders:这个组包括系统中所有的读卡机。当一个新的读卡机被添加到智能卡子系统时,它就会被自动添加到该组中。

Acard$DefaultReaders:这是个默认的组,每个终端对应一个这样的组,它包括所有被分配到终端的没有留作特殊用途的读卡机

2.智能卡接口

一个智能卡接口包括:智能卡中一组预定义的可用服务、用来调用这些服务的协议、以及所有关于这些服务描述表的假定。智能卡接口类似于COM中使用的接口,也是由GUID标识的。

3.将智能卡安装到系统中

智能卡必须安装到系统中,这样智能卡子系统才能找到它。安装过程通常由智能卡厂商提供的安装工具来完成。安装工具必须提供下列有关智能卡的信息:

(1)智能卡的ATR字符串和一个可选的标记,用来标识智能卡。

(2)被智能卡支持的一组智能卡接口的列表。

(3)智能卡的友好名称,用于将智能卡标识给用户。

(4)智能卡附带的主要服务提供者(可选),在通过COM接口访问智能卡时使用。

为了简化安装、并保证智能卡数据库(Smart Card Database)的完整性,智能卡子系统提供了两个辅助函数:ScardIntroduceCardType将一个智能卡引入数据库;ScardForgetCardType将一个智能卡从数据库中删除。

目的<TBODY> 调用函数
智能卡数据库查询函数
用来查询智能卡数据库。
 
获取给定的智能卡的主要服务提供者的标识符(GUID)。 SCardGetProviderId
获取以前被某个用户引入系统的所有智能卡列表。 SCardListCards
获取由一个给定的智能卡提供的接口的标识符(GUID)。 SCardListInterfaces
获取以前被引入系统的读卡机组列表。 SCardListReaderGroups
获取某个读卡机组中的读卡机的列表。 SCardListReaders
智能卡数据库管理函数
用来管理智能卡数据库,并使用特定的资源管理器描述表更新数据库。
 
向一个读卡机组中添加一个读卡机。 SCardAddReaderToGroup
从系统中删除一个智能卡。 SCardForgetCardType
从系统中删除一个读卡机。 SCardForgetReader
从系统中删除一个读卡机组。 SCardForgetReaderGroup
向系统中引入一个新卡。 SCardIntroduceCardType
向系统引入一个新读卡机。 SCardIntroduceReader
向系统引入一个新读卡机组。 SCardIntroduceReaderGroup
从一个读卡机组中删除一个读卡机。 SCardRemoveReaderFromGroup
资源管理器描述表函数
用来建立并释放被数据库查询、管理函数所使用的资源管理器描述表。
 
为访问智能卡数据库建立一个描述表。 SCardEstablishContext
关闭一个已经建立的描述表。 SCardReleaseContext
资源管理器支持函数
用来释放通过使用SCARD-AUTOALLOCATE标识符分配的内存,简化了其他资源管理器函数的使用。
 
释放通过使用SCARD-AUTOALLCATE返回的内存。 SCardFreeMemory
智能卡追踪函数
用来寻找读卡机内的卡。
 
寻找一个ATR字符串与提供的智能卡名称相符的卡。 SCardLocateCards
执行块,直到智能卡可用性改变为止。 SCardGetStatusChange
结束未完成的操作。 SCardCancel</TBODY>
智能卡及读卡机访问函数
用以连结到一个特定的智能卡并与之通讯。
 
连接到一张卡。 SCardConnect
重新建立连接。 SCardReconnect
结束一个连接 SCardDisconnect
启动一个事务,阻止其它应用程序访问智能卡。 SCardBeginTransaction
结束一个事务,允许其它引用程序访问智能卡。 SCardEndTransaction
提供某个读卡机目前的状态。 SCardStatus
智能卡及读卡机访问函数
用以连结到一个特定的智能卡并与之通讯。
 
连接到一张卡。 SCardConnect
重新建立连接。 SCardReconnect
结束一个连接。 SCardDisconnect
启动一个事务,阻止其它应用程序访问智能卡。 SCardBeginTransaction
结束一个事务。允许其它引用程序访问智能卡。 SCardEndTransaction
提供某个读卡机目前的状态。 SCardStatus

4.访问智能卡

智能卡子系统为应用程序及服务提供者提供了与智能卡连结的一些方法:

(1)应用程序可以调用ScardConnect函数来连结到一个放置在给定的读卡机里的卡。这是与智能卡建立通讯的最简单的方法。

(2)应用程序可以在一个给定的读卡机组中搜索一个特定的智能卡。应用程序使用智能卡的友好名称标识该卡,并指定一个该智能卡可能放入的读卡机组列表。资源管理器使用ATR字符串,在读卡机列表中搜索所有与给定智能卡相符的卡,并将该卡的壮态信息返回给应用程序。智能卡子系统从不提供CUI,也不在获取ATR字符串之后提供与智能卡的交互。但它们能够为用户定位在他们需要的智能卡或卡的类型上。这导致了将一个申请映射到一个指定的读卡机,并可以进一步映射到I/O的定向。

(3)应用程序能够申请一个支持某些给定智能卡接口的智能卡列表。应用程序可以在前面的情况中使申请服务并从一个使用T=0、T=1以及原始协议的卡上返回数据。

(T=0协议是一个异步的面向字符的半双工传输协议;T=1协议是一个异步的ScardTransmit面向块的半双工传输协议)

用该列表。这使得应用程序能够通过查询智能卡的功能找到相应的智能卡,而不需要知道它们的名称。

当应用程序查找一张卡时,它提供一组可能放有指定智能卡的读卡机名称。对于每一个在该序列中的读卡机,资源管理器提供下列信息:

1.         该读卡机是否能够被这个程序使用。

2.         是否有卡插入该读卡机,如果有的话,它的ATR字符串是什么。

3.         找到的智能卡ATR字符串是否符合所申请的智能卡ATR字符串。

应用程序使用返回的信息来进一步提供智能卡过滤装置,对所有找到的智能卡进行过滤,或者提示用户选择需要的智能卡。

注意:如果一个或多个读卡机列表已经被其他应用程序以独占方式打开,那么访问这些列表上的读卡机将会失败。

四、智能卡资源管理器

智能卡资源管理器(Smart Card Resource Manager)管理对读卡机和智能卡的访问。要管理这些资源,必须执行下列三个操作:

1.         识别并跟踪资源。

2.         在多个应用程序中分配读卡机和资源。

3.         为在一个给定卡上访问可能的服务支持原始事务处理。

注意:因为现有的智能卡是单线程设备,它们经常需要执行多个命令来完成一个单一功能。事务处理程序允许多个命令不中断地执行,并确定媒介状态信息没有被破坏。

资源管理器能够被资源管理器API直接访问,或者被任何智能卡服务提供者间接访问。资源管理器API是一组Win32函数,它们提供了直接对资源管理器服务的访问,但智能卡服务提供者则使用COM接口。资源管理器API中有很多Win32函数等同于智能卡服务提供者COM接口中的属性(Property)和方法(Method)。但是有些功能只有Win32函数提供。比如,当应用程序要操作智能卡数据库中的读卡机列表或读卡机组列表,或需要直接控制一个读卡机时,就必须使用资源管理器API。

资源管理器作为一个可信的服务运行在一个单进程中。所有智能卡访问的申请都被提交到资源管理器,由资源管理器将它们发送到包含目标卡的读卡机。智能卡经常被使用在机密和有关个人隐私的场合。无论什么情况下,当访问一个读卡机或智能卡时,资源管理器都使用现有操作系统底层的安全装置。

下表列出了智能卡资源管理器API中主要的函数以及使用这些函数的作用和目的。

五、用户界面

智能卡用户界面是一个单一的通用对话框,它让用户指定或者选择一个智能卡并将其打开(也就是连接到该智能卡,并在应用程序中使用它)。下面提供了使用该通用对话框的两种方法。

1.让用户自己选择一个智能卡并将其打开

(1)声明一个OPENCARDNAME类型的变量。

(2)指定lpstrGroupNames、lpstrCardNames、dwShareMode以及dwProtocols OPENCARDNAME的值,缩小智能卡的查询范围。

(3)调用GetOpenCardName函数,向用户显示通用对话框。事下图所示:

(4)用户选择一张卡,单南OK,就可以连接到该智能卡。

2.通过使用回叫函数查找某张特定的卡

(1)声明一个OPENCARDNAME类型的变量。

(2)指定lpstrGroupNames和lpstrCardNames的值,缩小智能卡的查询范围。

(3)寻立Connect、Check以及Disconnect回叫函数并适当设置lpfnConnect、lpfnCheck以及lpfnDisconnect OPENCARDNAME数据成员。

(4)调用GetOpenCardName通用对话框函数。

(5)通用对话框将寻找申请的卡。如果一张匹配的卡的名称或ATR字符串被找到了,那么Connect、check以及Disconnect回叫函数将会被依次调用。如果一张智能卡将例程传递给Check(也就是说,Check回叫函数返加TRUE),那么这张卡将会被高亮显示给用户。

(6)如果没有找到匹配的卡,那么上面的通用对话框将会出现。

scard API 操作错误码:

#region Error Codes 
        public const int SCARD_F_INTERNAL_ERROR = -2146435071; 
        public const int SCARD_E_CANCELLED = -2146435070; 
        public const int SCARD_E_INVALID_HANDLE = -2146435069; 
        public const int SCARD_E_INVALID_PARAMETER = -2146435068; 
        public const int SCARD_E_INVALID_TARGET = -2146435067; 
        public const int SCARD_E_NO_MEMORY = -2146435066; 
        public const int SCARD_F_WAITED_TOO_LONG = -2146435065; 
        public const int SCARD_E_INSUFFICIENT_BUFFER = -2146435064; 
        public const int SCARD_E_UNKNOWN_READER = -2146435063; 
        public const int SCARD_E_NO_READERS_AVAILABLE = -2146435026; 
 
 
        public const int SCARD_E_TIMEOUT = -2146435062; 
        public const int SCARD_E_SHARING_VIOLATION = -2146435061; 
        public const int SCARD_E_NO_SMARTCARD = -2146435060; 
        public const int SCARD_E_UNKNOWN_CARD = -2146435059; 
        public const int SCARD_E_CANT_DISPOSE = -2146435058; 
        public const int SCARD_E_PROTO_MISMATCH = -2146435057; 
 
 
        public const int SCARD_E_NOT_READY = -2146435056; 
        public const int SCARD_E_INVALID_VALUE = -2146435055; 
        public const int SCARD_E_SYSTEM_CANCELLED = -2146435054; 
        public const int SCARD_F_COMM_ERROR = -2146435053; 
        public const int SCARD_F_UNKNOWN_ERROR = -2146435052; 
        public const int SCARD_E_INVALID_ATR = -2146435051; 
        public const int SCARD_E_NOT_TRANSACTED = -2146435050; 
        public const int SCARD_E_READER_UNAVAILABLE = -2146435049; 
        public const int SCARD_P_SHUTDOWN = -2146435048; 
        public const int SCARD_E_PCI_TOO_SMALL = -2146435047; 
 
        public const int SCARD_E_READER_UNSUPPORTED = -2146435046; 
        public const int SCARD_E_DUPLICATE_READER = -2146435045; 
        public const int SCARD_E_CARD_UNSUPPORTED = -2146435044; 
        public const int SCARD_E_NO_SERVICE = -2146435043; 
        public const int SCARD_E_SERVICE_STOPPED = -2146435042; 
 
        public const int SCARD_W_UNSUPPORTED_CARD = -2146435041; 
        public const int SCARD_W_UNRESPONSIVE_CARD = -2146435040; 
        public const int SCARD_W_UNPOWERED_CARD = -2146435039; 
        public const int SCARD_W_RESET_CARD = -2146435038; 
 
        //From SCARD_W_REMOVED_CARD to SCARD_E_DIR_NOT_FOUND 
        public const int SCARD_E_DIR_NOT_FOUND = -2146435037; 
 
        public const int SCARD_W_REMOVED_CARD = -2146434967; 
#endregion 

一个使用例子:

SCARDCONTEXT m_hContext = 0;                //读卡器是否存在句柄
SCARDHANDLE m_hCard = 0;                    //具体的读卡器通讯句柄
char mszReaders[ 100 ] = "";

void CDebugpcsccardDlg::OnButton1()
{
    //相当于你的connect

//以下是所有的代码

//DisConnectCard();                        //调试注释
    //char s2[10];
    //char s1[10];
    //m_isCardConnected = true;                //调试注释
    LONG res;

//建立设备上下文 
    res = ::SCardEstablishContext(SCARD_SCOPE_USER,NULL,NULL,&m_hContext);

if(res != SCARD_S_SUCCESS)
    {
        //m_isCardConnected = false;
        //errorMessage-> setErrorMessage("ConnectCard","建立设备上下文失败!","Error"); 
        m_hContext = 0;
        return;
        //return false; 
    }

LPTSTR pmszReaders = NULL;
    LPTSTR  pReader = NULL;
    DWORD cch = SCARD_AUTOALLOCATE;
    DWORD dwAP;
    bool bConnected = false;

//为了简化过程直接使用第一个读卡器

/\\\\\\\\\\\\\\\\\\\\\\\\\*

//如果传入的参数sReader为空,则默认选择一个读卡器
    if(NULL == m_pReader)
    {

res = ::SCardListReaders(m_hContext,NULL,(LPTSTR)&pmszReaders,&cch);

if(res == SCARD_S_SUCCESS)
            res =::SCardConnect(m_hContext,pmszReaders,SCARD_SHARE_EXCLUSIVE,SCARD_PROTOCOL_T0 |SCARD_PROTOCOL_T1,&m_hCard,&dwAP);
    }
    else
    {

res = ::SCardConnect(m_hContext,m_pReader,SCARD_SHARE_EXCLUSIVE,SCARD_PROTOCOL_T0 |SCARD_PROTOCOL_T1,&m_hCard,&dwAP);
    }
*\\\\\\\\\\\\\\\\\\\\\\\\\\\\\/

res = ::SCardListReaders(m_hContext,NULL,(LPTSTR)&pmszReaders,&cch);
   
    if(res == SCARD_S_SUCCESS)
    {
        sprintf( mszReaders, "%s", pmszReaders );
        SCardFreeMemory( m_hContext, pmszReaders );
        OnButton2();
        res = ::SCardConnect(m_hContext,mszReaders,SCARD_SHARE_EXCLUSIVE,SCARD_PROTOCOL_T0|SCARD_PROTOCOL_T1,&m_hCard,&dwAP);
        if(res != SCARD_S_SUCCESS)
        {

//m_isCardConnected = false;
            //连接读卡器失败,释放设备上下文 
            ::SCardReleaseContext(m_hContext);
            m_hContext = 0;
            m_hCard = 0;
            //errorMessage-> setErrorMessage("ConnectCard","连接读卡器失败","Error"); 
        }
        //sprintf(s1,"%d",res); 
        //MessageBox(NULL,s1,s1,MB_OK); 
    }
    return;
}

void CDebugpcsccardDlg::OnButton2()
{
    //相当于你的disconnect

::SCardDisconnect(m_hCard,SCARD_EJECT_CARD);
}

void CDebugpcsccardDlg::OnButton3()
{
    //相当于你的status

unsigned char pbAtr[ 100 ] = "";
    DWORD dwState, dwProtocol;
    DWORD dd = sizeof( mszReaders );
    DWORD dwAtrLen = sizeof( pbAtr );

LONG result = ::SCardStatus( m_hCard, mszReaders, &dd, &dwState, &dwProtocol, pbAtr,&dwAtrLen);

if(result != SCARD_S_SUCCESS){
    //errorMessage-> setErrorMessage("GetCardStatus","获取读卡器状态失败","Error");
        //return false; 
        return;
    }

char string[ 100 ] = "";
    char tmp[ 10 ] = "";
    for( int i = 0; i < ( int )dwAtrLen; i++ )
    {
        sprintf( tmp, "%.2X ", pbAtr[ i ] );
        strcat( string, tmp );
    }
    AfxMessageBox( string );

//return true; 
    return;
   
}

Scard API 智能卡操作相关推荐

  1. Scard API 智能卡操作

    一.概述 ICC是Integrated Circuit Card的缩写,意思是集成电路卡,我们通常把它称为智能卡(Smart Card).智能卡应用广泛,它可以用来保存私人密码.银行账号.个人资料等. ...

  2. Java8新特性总结 -5.Stream API函数式操作流元素集合

    所有示例代码打包下载 : 点击打开链接 Java8新特性 : 接口新增默认方法和静态方法 Optional类 Lambda表达式 方法引用 Stream API - 函数式操作流元素集合 Date/T ...

  3. Hadoop生态圈-Hbase的API常见操作

    Hadoop生态圈-Hbase的API常见操作 作者:尹正杰 版权声明:原创作品,谢绝转载!否则将追究法律责任.

  4. VB如何使用API直接操作打印机

    有时出于某种原因,我们需要使用API直接操作打印机,而不是使用Printer对象.在网上这类代码较少,而且功能也不齐全,所以写了这段代码.由于写得匆忙,且没有安装打印,因此差错在所难免,希望朋友们指正 ...

  5. 微信入口绑定,微信事件处理,微信API全部操作

    微信入口绑定,微信事件处理,微信API全部操作包含在这些文件中. 微信支付.微信红包.微信卡券.微信小店. 1. index.php <?php include_once 'lib.inc.ph ...

  6. java规约运算的signature_Java8笔记第七篇(Stream API 的操作-规约,收集 )

    深入 java8 第 07 篇 ( Stream API 的操作->规约,收集 ) 一.Stream API 的操作步骤: 创建 Stream 中间操作 终止操作(终端操作 -> 规约,收 ...

  7. Kudu入门和API基础操作

    Kudu入门和API基础操作 文章目录 Kudu入门和API基础操作 为什么使用Kudu作为存储介质 1. Kudu介绍 1.1 背景介绍 1.2 新的硬件设备 1.3 Kudu是什么 1.4 Kud ...

  8. 通达信二次开发接口api如何操作?

    通达信二次开发接口api如何操作,步骤如下: 1.调用LoadLibrary加载MetaTrade.dll实例: 2.调用GetProcAddress获取API函数地址: 3.调用Init接口进行AP ...

  9. win32 api 文件操作!

    CreateFile 打开文件 要对文件进行读写等操作,首先必须获得文件句柄,通过该函数可以获得文件句柄,该函数是通向文件世界的大门. ReadFile 从文件中读取字节信息. 在打开文件获得了文件句 ...

最新文章

  1. 更换YUM及升级包方法
  2. 第二十七期:Deepfake视频正在快速传播,也许区块链能够阻止这波“瘟疫”
  3. jekins创建ssh_linux – Jenkins SSH slave无法创建/ home // jenkins
  4. 从gb2py.idx中获取一个汉字的拼音首字母
  5. iOS开源项目周报1222
  6. 猫眼top前100电影爬取demo(正则初试)
  7. 使用 lanmps 环境套件安装设置新站点 案例
  8. block才会执行 mono_Monogb基本概念及基本操作
  9. table td 纵向求和
  10. 碧桂园建筑机器人造楼,梦照进现实还是“海市蜃楼”?
  11. ubuntu 安装openproj-1.4-2.noarch.rpm
  12. 电脑主板上的一些电子元件
  13. 实现用户分层的手段——RFM模型
  14. [35期] 没有硝烟的战争
  15. html easyui怎么实现折叠面板,Easyui 创建折叠面板_EasyUI 教程
  16. 用一个简单的例子来阐述强化学习的相关概念(二)
  17. GAN中的Spectral Normalization
  18. 星巴克中国门店全职员工涨至14薪;迪桑特在北京三里屯开设全球最大零售门店|美通社头条...
  19. 【spark】spark介绍
  20. vue router html模板,vue.router

热门文章

  1. “洋记者”眼中的中国航天员:永不放弃的梦想
  2. 机器学习项目开发实战,应用
  3. linux每个文件标识,Linux文件系统中每个文件用____来标识。
  4. 爱因斯坦个人不是神仙
  5. 谈谈实习第一天的感受
  6. 马云卸任CEO演讲:明天起生活将是我的工作
  7. 算法训练营 重编码_您可能不需要$ 15K的编码训练营
  8. 连锁企业如何迈出网络运维数字化升级第一步?
  9. java super父类方法_java super关键字,super调用父类构造方法详解
  10. Xilinx FPGA资源解析与使用系列——Transceiver(九)TX buffer使用和旁路