Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles / containers / virtual-machine

How to access SmartCards simply and effectively

4.52/5 (16 votes)
12 Apr 2008CPOL4 min read 1   29K  
Using a set of classes to access SmartCard readers, to test SmartCards via scripts - even to transmit APDUs one by one.

Image 1

Image 2

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:

C++
// Get the readers' list
BOOL SCardGetPcscList();

// open the reader using the index
BOOL SCardOpen(int nInx);

// Get ATR from the card.
BOOL SCardReset(LPCTSTR strResp);

// Transmit APDU
BOOL SCardTransmit(LPCTSTR strApdu, LPCTSTR strResp, UINT *nSW);

// Close the connected reader
BOOL SCardClose();

Here are two very useful functions to do the string <-> hex, from Mr. Yu, one of my best leaders.

C++
///////////////////////////////////////////////////////////////////////////////
// hex to asc: 0x22 -> "22"
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;
}

///////////////////////////////////////////////////////////////////////////////
// asc to hex: "22" -> 0x22
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.

C++
///////////////////////////////////////////////////////////////////////////////
// Get the ATR information of the open Reader
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;                // Prot.
    BYTE        protocol = ATR_PROTOCOL_TYPE_T0;
    
    ZeroMemory(szATR, sizeof(szATR));

    if(m_SCard.SCardReset(szATR))
    // Do Reset it
    {
        int nLenAtr = strlen(szATR);

        // Convert ATR to Hex format
        Asc2Hex((char*)byATR, szATR, nLenAtr);                

        CAtr            objATR(byATR, nLenAtr/2);

        // get prots supported.
        objATR.ATR_GetNumberOfProtocols(&np);        
        INT nType = objATR.ATR_GetProtocolType(np, &protocol);

        sprintf(szFullAtr, "%s (T=%d).", szATR, protocol);

        // Display the ATR Information
        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:

C++
// Eat the non-numeric 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:

C++
void CConsoleWindow::WaitForAnyKeyEx()
{
    ... ...

    for(;;)
    {
        /* get an input event */
        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. ::

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)