Library yang dipakai untuk mengimplementasi komunikasi tersebut adalah library winscard. Berikut adalah contoh aplikasi komunikasi PC dengan Smart Card via Reader dalam C++. Code dibuat sesimple mungkin dan dalam satu file saja, dengan tujuan untuk memudahkan mempelajari dan mengadaptasinya. Code dibagi atas tiga bagian besar:
- Scan
- Men-scan Smart Card Reader
- Me-list daftar Reader yang terdeteksi
- Connect
- Men-connect ke Reader
- Transmit/Receive
- Mengirim command APDU ke Reader
- Menerima respon dan dan menampilkan responnya
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/*-filename: main.cpp | |
* | |
* Description : This is console application that will scan SimCard readers, connect to one of it, | |
* send an APDU command, and check for the response--written in a single C++ file. | |
* Author : Yan Syafri H. (yansyaf@gmail.com) | |
* Date : 13-Februari 2013 | |
* Reference : SCM SDK | |
* | |
* NOTE : Compiler option, do not use Unicode, use "Multi-Byte Character" instead. | |
*/ | |
#include <iostream> | |
#include "winscard.h" | |
#include "assert.h" | |
#pragma comment(lib, "Winscard.lib") | |
#define NUMBER_OF_READERS 4 | |
#define NAME_LENGTH 100 | |
#define MAX_INPUT 1024 | |
#define MAX_OUTPUT 4000 | |
#define MAX_RESPONSE 2000 | |
void GetErrorCode(long ret); | |
short AToHex(char *mhstr, BYTE *buf, long *InBufferLength); | |
int main() { | |
SCARD_READERSTATE ReaderState[NUMBER_OF_READERS]; | |
SCARD_IO_REQUEST IO_Request; | |
SCARDCONTEXT ContextHandle; | |
SCARDHANDLE CardHandle; | |
long ret; | |
PBYTE pInBuffer; | |
long InBufferLength; | |
PBYTE pOutBuffer; | |
int OutBufferLine; | |
short line; | |
short act_Name; | |
short ReaderCount; | |
char ReaderName[NUMBER_OF_READERS][NAME_LENGTH]; | |
PBYTE pResponseBuffer; | |
unsigned long ResponseLength; | |
long ProtocolType; | |
/////////////////////////////// SCAN READER //////////////////////////////////// | |
memset(ReaderName[0], 0, NAME_LENGTH); | |
memset(ReaderName[1], 0, NAME_LENGTH); | |
memset(ReaderName[2], 0, NAME_LENGTH); | |
memset(ReaderName[3], 0, NAME_LENGTH); | |
pOutBuffer = (PBYTE) malloc(MAX_OUTPUT); | |
assert( pOutBuffer ); | |
OutBufferLine = 0; | |
line = 0; | |
pResponseBuffer = (PBYTE) malloc( MAX_RESPONSE ); | |
assert( pResponseBuffer ); | |
memset(pResponseBuffer, 0x00, MAX_RESPONSE); | |
ret = SCardEstablishContext(SCARD_SCOPE_USER, NULL, NULL, &ContextHandle); | |
if (ret != SCARD_S_SUCCESS) { | |
return(IDCANCEL); | |
} | |
ReaderCount = 0; | |
ResponseLength = MAX_RESPONSE; | |
ret = SCardListReaders(ContextHandle, 0, (char *) pResponseBuffer, &ResponseLength); | |
if (ret != SCARD_S_SUCCESS) { | |
return(IDCANCEL); | |
} | |
else { | |
unsigned int StringLen = 0; | |
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) { | |
std::cout << "Reader Unavailable" << endl; | |
return (IDCANCEL); | |
} | |
else | |
{ | |
std::cout << "List of Detected Reader: " << endl; | |
for (int i=0; i<ReaderCount; i++) | |
{ | |
std::wcout << i << ": " << ReaderName[i] << endl; | |
} | |
/////////////////////////////// CONNECT //////////////////////////////////// | |
DWORD ActiveProtocol = 0; | |
ProtocolType = SCARD_PROTOCOL_T0 | SCARD_PROTOCOL_T1; | |
//For this example, we use reader index 0 (ReaderName[0]) | |
ret = SCardConnect(ContextHandle, ReaderName[0], SCARD_SHARE_EXCLUSIVE, ProtocolType, &CardHandle, &ActiveProtocol); | |
if (ret != SCARD_S_SUCCESS){ | |
GetErrorCode(ret); | |
return 1; | |
} | |
ProtocolType = ActiveProtocol; | |
switch (ProtocolType) { | |
case SCARD_PROTOCOL_T0: | |
std::cout << "Function SCardConnect ok\nProtocoltype = T0" << endl; | |
break; | |
case SCARD_PROTOCOL_T1: | |
std::cout << "Function SCardConnect ok\nProtocoltype = T1" << endl; | |
break; | |
default: | |
sprintf((char *) pOutBuffer, "Function SCardConnect ok\n%.8x", ActiveProtocol); | |
break; | |
} | |
OutBufferLine = 0; | |
/////////////////////////////// TRANSMIT //////////////////////////////////// | |
char mhstr[MAX_INPUT]; | |
char buf[MAX_INPUT/2]; | |
PBYTE pInBuffer; | |
memset(mhstr, 0, MAX_INPUT); | |
sprintf(mhstr , "51020000FF"); //Insert your custom APDU command here! | |
if ( !AToHex((char *) &mhstr, (BYTE *) &buf, &InBufferLength) ) { | |
return 1; | |
} | |
IO_Request.dwProtocol = ProtocolType; | |
IO_Request.cbPciLength = (DWORD) sizeof(SCARD_IO_REQUEST); | |
pInBuffer = (PBYTE) malloc( MAX_INPUT ); | |
assert( pInBuffer ); | |
memcpy(pInBuffer, buf, InBufferLength); | |
ResponseLength = MAX_RESPONSE; | |
ret = SCardTransmit(CardHandle, &IO_Request, pInBuffer, InBufferLength, 0, pResponseBuffer, &ResponseLength); | |
if (ret != SCARD_S_SUCCESS){ | |
GetErrorCode(ret); | |
free(pInBuffer); | |
return 1; | |
} | |
PBYTE pStartAddress = pOutBuffer; | |
for (unsigned long i=0; i<ResponseLength; i++) { | |
sprintf((char *) pOutBuffer, "%.2x ",(BYTE) pResponseBuffer[i]); | |
pOutBuffer += 3; | |
if (i > MAX_RESPONSE-3) | |
break; | |
} | |
pOutBuffer = pStartAddress; | |
std::cout << "Response: " << endl; | |
for (unsigned long i=0; i<ResponseLength; i++) { | |
std::cout << pOutBuffer[i]; | |
} | |
free(pInBuffer); | |
} | |
return 0; | |
} | |
void GetErrorCode(long ret) | |
{ | |
switch (ret) { | |
case SCARD_E_CANCELLED: | |
std::cout << "ERROR: The action was cancelled by an SCardCancel request." << endl; | |
break; | |
case SCARD_E_CANT_DISPOSE: | |
std::cout << "ERROR: The system could not dispose of the media in the requested manner" << endl; | |
break; | |
case SCARD_E_CARD_UNSUPPORTED: | |
std::cout << "ERROR: The smart card does not meet minimal requirements for support" << endl; | |
break; | |
case SCARD_E_DUPLICATE_READER: | |
std::cout << "ERROR: The reader driver didn't produce a unique reader name" << endl; | |
break; | |
case SCARD_E_INSUFFICIENT_BUFFER: | |
std::cout << "ERROR: The data buffer to receive returned data is too small for the returned data" << endl; | |
break; | |
case SCARD_E_INVALID_ATR: | |
std::cout << "ERROR: An ATR obtained from the registry is not a valid ATR string" << endl; | |
break; | |
case SCARD_E_INVALID_HANDLE: | |
std::cout << "ERROR: The supplied handle was invalid" << endl; | |
break; | |
case SCARD_E_INVALID_PARAMETER: | |
std::cout << "ERROR: One or more of the supplied parameters could not be properly interpreted" << endl; | |
break; | |
case SCARD_E_INVALID_TARGET: | |
std::cout << "ERROR: Registry startup information is missing or invalid" << endl; | |
break; | |
case SCARD_E_INVALID_VALUE: | |
std::cout << "ERROR: One or more of the supplied parameters’ values could not be properly interpreted" << endl; | |
break; | |
case SCARD_E_NOT_READY: | |
std::cout << "ERROR: The reader or card is not ready to accept commands" << endl; | |
break; | |
case SCARD_E_NOT_TRANSACTED: | |
std::cout << "ERROR: An attempt was made to end a non-existent transaction" << endl; | |
break; | |
case SCARD_E_NO_MEMORY: | |
std::cout << "ERROR: Not enough memory available to complete this command" << endl; | |
break; | |
case SCARD_E_NO_SERVICE: | |
std::cout << "ERROR: The Smart card resource manager is not running" << endl; | |
break; | |
case SCARD_E_NO_SMARTCARD: | |
std::cout << "ERROR: The operation requires a smart card but no smart card is currently in the device" << endl; | |
break; | |
case SCARD_E_PCI_TOO_SMALL: | |
std::cout << "ERROR: The PCI Receive buffer was too small" << endl; | |
break; | |
case SCARD_E_PROTO_MISMATCH: | |
std::cout << "ERROR: The requested protocols are incompatible with the protocol currently in use with the card" << endl; | |
break; | |
case SCARD_E_READER_UNAVAILABLE: | |
std::cout << "ERROR: The specified reader is not currently available for use" << endl; | |
break; | |
case SCARD_E_READER_UNSUPPORTED: | |
std::cout << "ERROR: The reader driver does not meet minimal requirements for support" << endl; | |
break; | |
case SCARD_E_SERVICE_STOPPED: | |
std::cout << "ERROR: The Smart card resource manager has shut down" << endl; | |
break; | |
case SCARD_E_SHARING_VIOLATION: | |
std::cout << "ERROR: The card cannot be accessed because of other connections outstanding" << endl; | |
break; | |
case SCARD_E_SYSTEM_CANCELLED: | |
std::cout << "ERROR: The action was cancelled by the system presumably to log off or shut down" << endl; | |
break; | |
case SCARD_E_TIMEOUT: | |
std::cout << "ERROR: The user-specified timeout value has expired" << endl; | |
break; | |
case SCARD_E_UNKNOWN_CARD: | |
std::cout << "ERROR: The specified card name is not recognized" << endl; | |
break; | |
case SCARD_E_UNKNOWN_READER: | |
std::cout << "ERROR: The specified reader name is not recognized" << endl; | |
break; | |
case SCARD_F_COMM_ERROR: | |
std::cout << "ERROR: An internal communications error has been detected" << endl; | |
break; | |
case SCARD_F_INTERNAL_ERROR: | |
std::cout << "ERROR: An internal consistency check failed" << endl; | |
break; | |
case SCARD_F_UNKNOWN_ERROR: | |
std::cout << "ERROR: An internal error has been detected but the source is unknown" << endl; | |
break; | |
case SCARD_F_WAITED_TOO_LONG: | |
std::cout << "ERROR: An internal consistency timer has expired" << endl; | |
break; | |
case SCARD_S_SUCCESS: | |
std::cout << "ERROR: OK" << endl; | |
break; | |
case SCARD_W_REMOVED_CARD: | |
std::cout << "ERROR: The card has been removed so that further communication is not possible" << endl; | |
break; | |
case SCARD_W_RESET_CARD: | |
std::cout << "ERROR: The card has been reset so any shared state information is invalid" << endl; | |
break; | |
case SCARD_W_UNPOWERED_CARD: | |
std::cout << "ERROR: Power has been removed from the card so that further communication is not possible" << endl; | |
break; | |
case SCARD_W_UNRESPONSIVE_CARD: | |
std::cout << "ERROR: The card is not responding to a reset" << endl; | |
break; | |
case SCARD_W_UNSUPPORTED_CARD: | |
std::cout << "ERROR: The reader cannot communicate with the card due to ATR configuration conflicts" << endl; | |
break; | |
default: | |
std::cout << "ERROR: Function returned " << ret << " error code." << endl; | |
break; | |
} | |
} | |
short AToHex(char *mhstr, BYTE *buf, long *InBufferLength) | |
{ | |
*InBufferLength = strlen(mhstr); | |
if (*InBufferLength < 3) { | |
std::cout << "ERROR: Input not complete!" << endl; | |
return(0); | |
} | |
for (short i=0; i<*InBufferLength; i++) { | |
if (mhstr[i] == '\n') { | |
mhstr[i] = 0; | |
} | |
else if (((mhstr[i] < '0') || (mhstr[i] > '9')) && | |
((mhstr[i] != ' ') && (mhstr[i] != ',')) && | |
((mhstr[i] < 'a') || (mhstr[i] > 'f')) && | |
((mhstr[i] < 'A') || (mhstr[i] > 'F'))) { | |
std::cout << "Only hex. input allowed!" << endl; | |
return(0); | |
} | |
} | |
char *s; | |
if (mhstr[1] == ',') { | |
*InBufferLength = (*InBufferLength - 4)/2; | |
s = mhstr+4; | |
} | |
else { | |
*InBufferLength = *InBufferLength/2; | |
s = mhstr; | |
} | |
memset(buf, 0, sizeof (buf)); | |
int i = 0; | |
while (isspace(*s)) s++; | |
short k = FALSE; | |
while (*s) { | |
if (isxdigit(*s)) { | |
buf[i] <<= 4; | |
if (isalpha(*s)) | |
buf[i] |= (tolower(*s) - 0x57); | |
else | |
buf[i] |= (*s - 0x30); | |
if (k) { | |
i++; | |
k= FALSE; | |
} | |
else { | |
k= TRUE; | |
} | |
s++; | |
} | |
else break; | |
} | |
return(1); | |
} |