PC/SC规范是一个基于WINDOWS平台的一个标准用户接口(API),提供了一个从个人电脑(Personal Computer)到智能卡(SmartCard)的整合环境,PC/SC规范建立在工业标准-ISO7816和EMV标准的基础上,但它对底层的设备接口和独立于设备的应用API接口(例如用来允许多个应用共享使用系统同一张智能卡的资源管理器)做了更详尽的补充。PC/SC体系由三个主要部件组成,分别规定的操作系统厂商、读写器(IFD)厂商、智能卡(ICC)厂商的职责。PC/SC的API函数由操作系统提供,在微软公司提供的MSDN有相关帮助(路径\\MSDN\Platform SDK\Security\Smart Card),函数声明在Winscard.h中。
由于经常使用,所以我将PC/SC几个常用函数进行封装,方便调用。
1.载入头文件
- #include <WinSCard.h>
- #pragma comment(lib,"WinSCard.lib")
- #define NUMBER_OF_READERS 4
- #define INDEX_LENGTH 2
- #define KEY_LENGTH 32
- #define NAME_LENGTH 100
- #define MAX_INPUT 1024
- #define MAX_OUTPUT 4000
- #define MAX_RESPONSE 2000
2 建立资源管理器的上下文,并获得读卡器列表
- /************************************************************************/
- /*
- 与读卡器建立连接
- 返回:连接失败0,否则返回读卡器列表数目
- */
- /************************************************************************/
- extern "C" __declspec(dllexport) int CardReaderInit(
- char* messageBuffer, //_out_ 返回错误信息
- char(*ReaderName)[NAME_LENGTH], //_out_ 返回读卡器列表信息
- SCARDCONTEXT* ContextHandle //_out_ 返回读卡器句柄
- )
- {
- // extra initialization
- //char ReaderName[NUMBER_OF_READERS][NAME_LENGTH];
- memset(messageBuffer, 0, NAME_LENGTH);
- memset(ReaderName[0], 0, NAME_LENGTH);
- memset(ReaderName[1], 0, NAME_LENGTH);
- memset(ReaderName[2], 0, NAME_LENGTH);
- memset(ReaderName[3], 0, NAME_LENGTH);
- PBYTE pOutBuffer = (PBYTE)malloc(MAX_OUTPUT);
- int OutBufferLine = 0;
- PBYTE pResponseBuffer = (PBYTE)malloc(MAX_RESPONSE);
- memset(pResponseBuffer, 0x00, MAX_RESPONSE);
- //
- // Open a context which communication to the Resource Manager
- //
- long ret = SCardEstablishContext(SCARD_SCOPE_USER, NULL, NULL, ContextHandle);
- if (ret != SCARD_S_SUCCESS) {
- sprintf_s(messageBuffer, NAME_LENGTH, "Function SCardEstablishContext returned 0x%X error code.", ret);
- return false;
- }
- int ReaderCount = 0;
- unsigned long ResponseLength = MAX_RESPONSE;
- ret = SCardListReaders(*ContextHandle, 0, (char *)pResponseBuffer, &ResponseLength);
- if (ret != SCARD_S_SUCCESS) {
- sprintf_s(messageBuffer, NAME_LENGTH, "Function SCardListReaders returned 0x%X error code.", ret);
- return false;
- }
- else {
- unsigned int StringLen = 0;
- SCARDHANDLE CardHandle = NULL;
- while (ResponseLength > StringLen + 1) {
- strcpy(ReaderName[ReaderCount], (LPCTSTR)pResponseBuffer + StringLen);
- DWORD ActiveProtocol = 0;
- ret = SCardConnect(*ContextHandle, ReaderName[ReaderCount], SCARD_SHARE_SHARED, SCARD_PROTOCOL_T0 | SCARD_PROTOCOL_T1, &CardHandle, &ActiveProtocol);
- if (ret != SCARD_E_UNKNOWN_READER)
- ReaderCount++;
- if (ret == SCARD_S_SUCCESS)
- SCardDisconnect(CardHandle, SCARD_EJECT_CARD);
- StringLen += strlen((LPCTSTR)pResponseBuffer + StringLen + 1);
- StringLen += 2;
- }
- }
- if (ReaderCount == 0) {
- sprintf_s(messageBuffer, NAME_LENGTH, "No driver is available for use with the resource manager!");
- return false;
- }
- return ReaderCount;
- }
3 读卡器与智能卡连接
- /************************************************************************/
- /*
- 与卡片建立连接
- 返回:连接失败0,否则返回1
- */
- /************************************************************************/
- extern "C" __declspec(dllexport) bool CardConnect(
- SCARDCONTEXT ContextHandle, //_in_ 传入读卡器句柄
- char(*ReaderName)[NAME_LENGTH], //_in_ 传入读卡器列表信息(二维数组)
- int actName, //_in_ 传入选择的列表序号
- SCARDHANDLE *CardHandle, //_out_ 返回卡片句柄
- long* ProtocolType, //_out_ 返回卡片协议
- char* messageBuffer //_out_ 返回错误信息
- )
- { // <span style="font-family: 'Courier New';font-size:14px;">ScardTransmit </span><a target=_blank target="_blank" href="'Courier New';font-size:14px;"> style="margin:0px;padding:0px;border:none;background-color:inherit;">
- DWORD ActiveProtocol = 0;
- *ProtocolType = SCARD_PROTOCOL_T0 | SCARD_PROTOCOL_T1;
- long ret = SCardConnect(ContextHandle, ReaderName[actName], SCARD_SHARE_EXCLUSIVE, *ProtocolType, CardHandle, &ActiveProtocol);
- memset(messageBuffer, 0x00, sizeof(messageBuffer) / sizeof(char));
- if (ret != SCARD_S_SUCCESS){
- GetErrorCode(ret, messageBuffer);
- return false;
- }
- *ProtocolType = ActiveProtocol;
- switch (*ProtocolType) {
- case SCARD_PROTOCOL_T0:
- sprintf_s(messageBuffer, NAME_LENGTH, "Function SCardConnect ok\nProtocoltype = T0");
- break;
- case SCARD_PROTOCOL_T1:
- sprintf_s(messageBuffer, NAME_LENGTH, "Function SCardConnect ok\nProtocoltype = T1");
- break;
- default:
- sprintf_s(messageBuffer, NAME_LENGTH, "Function SCardConnect ok\n%.8x", ActiveProtocol);
- break;
- }
- return true;
- }
4 断开与读卡器的连接
- /************************************************************************/
- /*
- 与卡片断开连接
- 返回:连接成功0.否则错误(v=vs.85).aspx
- */
- /************************************************************************/
- extern "C" __declspec(dllexport) long CardDisconnect(
- SCARDHANDLE CardHandle //_in_ 传入卡片句柄
- )
- {
- return SCardDisconnect(CardHandle, SCARD_EJECT_CARD);
- }
5 释放资源管理上下文
- /************************************************************************/
- /*
- 与读卡器断开连接
- 返回:连接成功0.否则错误(v=vs.85).aspx
- */
- /************************************************************************/
- extern "C" __declspec(dllexport) long CardReaderDisconnect(
- SCARDCONTEXT ContextHandle //_in_ 传入读卡器句柄
- )
- {
- return SCardReleaseContext(ContextHandle);
- }
6 向智能卡发送指令
- int Transmit(byte* cmd,
- long ProtocolType,
- SCARDHANDLE CardHandle
- )
- int Transmit(byte* cmd, long ProtocolType, SCARDHANDLE CardHandle)
- {
- char mhstr[MAX_INPUT];
- char buf[MAX_INPUT / 2];
- PBYTE pInBuffer;
- PBYTE pResponseBuffer;
- memset(mhstr, 0, MAX_INPUT);
- sprintf_s(mhstr, MAX_INPUT, "%s", cmd);
- int bufferLen = AToHex((char *)&mhstr, (BYTE *)&buf);
- if (!bufferLen) {
- return false;
- }
- SCARD_IO_REQUEST IO_Request;
- IO_Request.dwProtocol = ProtocolType;
- IO_Request.cbPciLength = (DWORD) sizeof(SCARD_IO_REQUEST);
- pInBuffer = (PBYTE)malloc(MAX_INPUT);
- memcpy(pInBuffer, buf, bufferLen);
- pResponseBuffer = (PBYTE)malloc(MAX_INPUT);
- memset(pResponseBuffer, 0x00, bufferLen);
- unsigned long ResponseLength = MAX_RESPONSE;
- long ret = SCardTransmit(CardHandle, &IO_Request, pInBuffer, bufferLen, 0, pResponseBuffer, &ResponseLength);
- if (ret != SCARD_S_SUCCESS){
- GetErrorCode(ret, (char*)cmd);
- return false;
- }
- DataX::AscToHex(pResponseBuffer, ResponseLength, cmd);
- cmd[ResponseLength * 2] = 0x00;
- return ResponseLength * 2;
- }
7 获取错误信息
- void GetErrorCode(long ret, char* messageBuffer)
- {
- switch (ret) {
- case SCARD_E_CANCELLED:
- sprintf_s(messageBuffer, NAME_LENGTH, "The action was cancelled by an SCardCancel request.");
- break;
- case SCARD_E_CANT_DISPOSE:
- sprintf_s(messageBuffer, NAME_LENGTH, "The system could not dispose of the media in the requested manner.");
- break;
- case SCARD_E_CARD_UNSUPPORTED:
- sprintf_s(messageBuffer, NAME_LENGTH, "The smart card does not meet minimal requirements for support.");
- break;
- case SCARD_E_DUPLICATE_READER:
- sprintf_s(messageBuffer, NAME_LENGTH, "The reader driver didn't produce a unique reader name.");
- break;
- case SCARD_E_INSUFFICIENT_BUFFER:
- sprintf_s(messageBuffer, NAME_LENGTH, "The data buffer to receive returned data is too small for the returned data.");
- break;
- case SCARD_E_INVALID_ATR:
- sprintf_s(messageBuffer, NAME_LENGTH, "An ATR obtained from the registry is not a valid ATR string.");
- break;
- case SCARD_E_INVALID_HANDLE:
- sprintf_s(messageBuffer, NAME_LENGTH, "The supplied handle was invalid.");
- break;
- case SCARD_E_INVALID_PARAMETER:
- sprintf_s(messageBuffer, NAME_LENGTH, "One or more of the supplied parameters could not be properly interpreted.");
- break;
- case SCARD_E_INVALID_TARGET:
- sprintf_s(messageBuffer, NAME_LENGTH, "Registry startup information is missing or invalid.");
- break;
- case SCARD_E_INVALID_VALUE:
- sprintf_s(messageBuffer, NAME_LENGTH, "One or more of the supplied parameters?values could not be properly interpreted.");
- break;
- case SCARD_E_NOT_READY:
- sprintf_s(messageBuffer, NAME_LENGTH, "The reader or card is not ready to accept commands.");
- break;
- case SCARD_E_NOT_TRANSACTED:
- sprintf_s(messageBuffer, NAME_LENGTH, "An attempt was made to end a non-existent transaction.");
- break;
- case SCARD_E_NO_MEMORY:
- sprintf_s(messageBuffer, NAME_LENGTH, "Not enough memory available to complete this command.");
- break;
- case SCARD_E_NO_SERVICE:
- sprintf_s(messageBuffer, NAME_LENGTH, "The Smart card resource manager is not running.");
- break;
- case SCARD_E_NO_SMARTCARD:
- sprintf_s(messageBuffer, NAME_LENGTH, "The operation requires a smart card but no smart card is currently in the device.");
- break;
- case SCARD_E_PCI_TOO_SMALL:
- sprintf_s(messageBuffer, NAME_LENGTH, "The PCI Receive buffer was too small.");
- break;
- case SCARD_E_PROTO_MISMATCH:
- sprintf_s(messageBuffer, NAME_LENGTH, "The requested protocols are incompatible with the protocol currently in use with the card.");
- break;
- case SCARD_E_READER_UNAVAILABLE:
- sprintf_s(messageBuffer, NAME_LENGTH, "The specified reader is not currently available for use.");
- break;
- case SCARD_E_READER_UNSUPPORTED:
- sprintf_s(messageBuffer, NAME_LENGTH, "The reader driver does not meet minimal requirements for support.");
- break;
- case SCARD_E_SERVICE_STOPPED:
- sprintf_s(messageBuffer, NAME_LENGTH, "The Smart card resource manager has shut down.");
- break;
- case SCARD_E_SHARING_VIOLATION:
- sprintf_s(messageBuffer, NAME_LENGTH, "The card cannot be accessed because of other connections outstanding.");
- break;
- case SCARD_E_SYSTEM_CANCELLED:
- sprintf_s(messageBuffer, NAME_LENGTH, "The action was cancelled by the system presumably to log off or shut down.");
- break;
- case SCARD_E_TIMEOUT:
- sprintf_s(messageBuffer, NAME_LENGTH, "The user-specified timeout value has expired.");
- break;
- case SCARD_E_UNKNOWN_CARD:
- sprintf_s(messageBuffer, NAME_LENGTH, "The specified card name is not recognized.");
- break;
- case SCARD_E_UNKNOWN_READER:
- sprintf_s(messageBuffer, NAME_LENGTH, "The specified reader name is not recognized.");
- break;
- case SCARD_F_COMM_ERROR:
- sprintf_s(messageBuffer, NAME_LENGTH, "An internal communications error has been detected.");
- break;
- case SCARD_F_INTERNAL_ERROR:
- sprintf_s(messageBuffer, NAME_LENGTH, "An internal consistency check failed.");
- break;
- case SCARD_F_UNKNOWN_ERROR:
- sprintf_s(messageBuffer, NAME_LENGTH, "An internal error has been detected but the source is unknown.");
- break;
- case SCARD_F_WAITED_TOO_LONG:
- sprintf_s(messageBuffer, NAME_LENGTH, "An internal consistency timer has expired.");
- break;
- case SCARD_S_SUCCESS:
- sprintf_s(messageBuffer, NAME_LENGTH, "OK");
- break;
- case SCARD_W_REMOVED_CARD:
- sprintf_s(messageBuffer, NAME_LENGTH, "The card has been removed so that further communication is not possible.");
- break;
- case SCARD_W_RESET_CARD:
- sprintf_s(messageBuffer, NAME_LENGTH, "The card has been reset so any shared state information is invalid.");
- break;
- case SCARD_W_UNPOWERED_CARD:
- sprintf_s(messageBuffer, NAME_LENGTH, "Power has been removed from the card so that further communication is not possible.");
- break;
- case SCARD_W_UNRESPONSIVE_CARD:
- sprintf_s(messageBuffer, NAME_LENGTH, "The card is not responding to a reset.");
- break;
- case SCARD_W_UNSUPPORTED_CARD:
- sprintf_s(messageBuffer, NAME_LENGTH, "The reader cannot communicate with the card due to ATR configuration conflicts.");
- break;
- default:
- sprintf_s(messageBuffer, NAME_LENGTH, "Function returned unknown error code: #%ld", ret);
- break;
- }
- }
8 复位卡片
- bool CpuReset(SCARDHANDLE CardHandle, byte* atr)
- {
- CHAR szReader[200];
- DWORD cch = 200;
- BYTE bAttr[32];
- DWORD cByte = 32;
- DWORD dwState, dwProtocol;
- LONG lReturn;
- string AtrValue;
- memset(bAttr, 0, 32);
- memset(szReader, 0, 200);
- // Determine the status.
- // hCardHandle was set by an earlier call to SCardConnect.
- lReturn = SCardStatus(CardHandle,
- szReader,
- &cch,
- &dwState,
- &dwProtocol,
- (LPBYTE)&bAttr,
- &cByte);
- if (SCARD_S_SUCCESS != lReturn)
- return FALSE;
- DataX::AscToHex(bAttr, cByte, atr);
- return TRUE;
- }