Introduction
This article has two demo projects to show you how to access SmartCards in Windows simply and effectively. If you are not familiar with this field, this is an opportunity to learn something about it.
The basic demo is very simple, it can show you how to list the readers in the system, connect/disconnect it, get the card's ATR, and transmit one APDU with a card.
Note: The basic demo only accepts one APDU without any non-number chars, such as: "0084000008" (get an 8-bytes challenge from the card).
The advanced demo demonstrates how to use SmartCards effectively over the basic layer:
- There is an APDU processor: it can process the user input
- One APDU with spaces separating: "0084 0000 08"
- Multi-APDUs: "0084 0000 08; 00A4 0000 02 3F;"
- Even an APDU's script file: "c:\myapdu.txt"
- ATR analyzing, we demonstrate the protocol: T=0, or T=1.
- Multi-APDU console: This consols is useful for these kinds of tests:
- You can prompt the user for a 'Press any key to continue' if there is something wrong
- You can select the console texts by holding the left-button and dragging a rectangle, releasing the left-button - the text is just in your clipboard - just like the Windows cmd.exe does, but this works on Win9x too ;-)
And if you want to know how they are running, you should prepare a SmartCard reader or install a virtual SmartCard reader, and at least one SmartCard.
But if not, there is also the multi-APDU console for you to run scripts in a console during certain occasions in your project, I think.
Background
I have used SmartCards in many projects since 2004, but I really could not find useful classes to access them here in CodeProject or anywhere else on the internet. So I decided to write several classes to show you the process we usually do.
For the SmartCard subsystem in Windows, we should know: the SmartCard reader should be connected to the system, the vender should present us a PC/SC driver, and there is a so called 'SmartCard Resource Manager' for Win32 programs to access all kinds of readers via a set of APIs.
For using these APIs, we should call:
SCardEstablishContext()
: establishes the SmartCard's context;SCardListReaders()
: gets a reader's list from the resource manager;SCardConnect()
: connects a reader by giving the reader name;SCardStatus()
: gets the ATR from the selected card which is in the 'connected reader';SCardTransmit()
: transmits the APDU between the card and our program;SCardDisconnect()
: disconnects the connected reader;SCardReleaseContext()
: releases the context.
Among these, 4 is optional.
Using the code
As we can see, the basic demo has the core class CSCardmgr
. And, its interface is very simple:
BOOL SCardGetPcscList();
BOOL SCardOpen(int nInx);
BOOL SCardReset(LPCTSTR strResp);
BOOL SCardTransmit(LPCTSTR strApdu, LPCTSTR strResp, UINT *nSW);
BOOL SCardClose();
Here are two very useful functions to do the string <-> hex, from Mr. Yu, one of my best leaders.
int Hex2Asc(char *Dest,char *Src,int SrcLen)
{
int i;
for ( i = 0; i < SrcLen; i ++ )
{
sprintf(Dest + i * 2,"%02X",(unsigned char)Src[i]);
}
Dest[i * 2] = 0;
return TRUE;
}
int Asc2Hex(char *Dest,char *Src,int SrcLen)
{
int i;
for ( i = 0; i < SrcLen / 2; i ++ )
{
sscanf(Src + i * 2,"%02X",(unsigned char *)&Dest[i]);
}
return TRUE;
}
For the advanced demo, we improve the ATR processing and the APDU processing - these are useful in the SmartCard application. First, the ATR processing: we extract the SmartCard supported protocol only, and displays its protocol at the end of the ATR string.
void CSCardDemoDlg::OnReset()
{
CHAR szATR[ATR_MAX_SIZE * 2 +1] = {0};
BYTE byATR[ATR_MAX_SIZE] = {0};
CHAR szFullAtr[MAX_RESPONSE] = {0};
UINT np = 0; BYTE protocol = ATR_PROTOCOL_TYPE_T0;
ZeroMemory(szATR, sizeof(szATR));
if(m_SCard.SCardReset(szATR))
{
int nLenAtr = strlen(szATR);
Asc2Hex((char*)byATR, szATR, nLenAtr);
CAtr objATR(byATR, nLenAtr/2);
objATR.ATR_GetNumberOfProtocols(&np);
INT nType = objATR.ATR_GetProtocolType(np, &protocol);
sprintf(szFullAtr, "%s (T=%d).", szATR, protocol);
SetDlgItemText(IDS_MSG, szFullAtr);
}
}
Second, we provide the CApduProcesser
and CConsoleWindow
to improve the APDU transmission experience: the user input is more flexible as described, and we use CApduProcesser
for this. We use this code block to eat the non-number chars:
for(int i=0, j=0; i < strlen(szSLBuf); i++)
{
if(szSLBuf[i] >='0' && szSLBuf[i] <= '9')
{
szString[j] = szSLBuf[i];
j ++;
}
else if(szSLBuf[i] >='A' && szSLBuf[i] <= 'F')
{
szString[j] = szSLBuf[i];
j ++;
}
else if(szSLBuf[i] >='a' && szSLBuf[i] <= 'f')
{
szString[j] = szSLBuf[i];
j ++;
}
else
continue;
}
CConsoleWindow
is used for the Console output. Its highlight is 'Any key to continue', and mouse selection:
void CConsoleWindow::WaitForAnyKeyEx()
{
... ...
for(;;)
{
bSuccess = ReadConsoleInput(hStdIn, &inputBuffer, 1, &dwInputEvents);
PERR(bSuccess, "ReadConsoleInput");
switch (inputBuffer.EventType)
{
case KEY_EVENT:
... ...
break;
case MOUSE_EVENT:
... ...
break;
}
}
... ...
}
The 'KEY_EVENT
' is for 'any key to continue', and 'MOUSE_EVENT
', yes, for the mouse selection.
Points of Interest
What's a SmartCard? I have no idea, but what I'm sure is my iPhone has a SIM card and it is just a SmartCard. ^_^
If I had a SmartCard reader, I can read the contacts-list from my SIM card by using this demo.
Furthermore, the SmartCard is not just used in reading the SIM card, the PKI also needs it: the CSP and PKCS#11 all need it - (but not must ^_^ ). As I know, many vendors have combined the reader and card, that's the USB token; and the token has a finger scanner or OTP embedded. So, this field is very wonderful.
BTW, SmartCard is a contact card (restricted by ISO7816). There are the so called contactless card (restricted by ISO14443), such as Mifare S50/S70/DES/UL etc. Some venders also provide the so called two-module cards - these cards have two interfaces, and supports both ISO7816 and ISO14443.
History
- 2008-01-19: First version.
- 2008-04-12: Virtual PC/SC driver (virtual-pcsc_drv.zip) added for using/testing these demos.
For the driver's license, please refer to the readme.txt. :: It is recommend using this driver in a Virtual Machine. ::