首页 > 代码库 > 常用的PC/SC接口函数

常用的PC/SC接口函数

   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=http://www.mamicode.com/"http://baike.baidu.com/view/8257336.htm" style="font-family: 'Courier New';font-size:14px;">http://baike.baidu.com/view/8257336.htm>

4 断开与读卡器的连接

/************************************************************************/
/*
	与卡片断开连接
	返回:连接成功0.否则错误http://msdn.microsoft.com/en-us/library/windows/desktop/aa374738(v=vs.85).aspx
*/
/************************************************************************/
extern "C" __declspec(dllexport) long CardDisconnect(
	SCARDHANDLE CardHandle               //_in_ 传入卡片句柄
	)
{
	return SCardDisconnect(CardHandle, SCARD_EJECT_CARD);
}

5 释放资源管理上下文

/************************************************************************/
/*
	与读卡器断开连接
	返回:连接成功0.否则错误http://msdn.microsoft.com/en-us/library/windows/desktop/aa374738(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;
}


文/闫鑫原创 转载请注明出处http://blog.csdn.net/yxstars/article/details/41522467


常用的PC/SC接口函数