歡迎您訪問鄭州興邦電子股份有限公司官方網(wǎng)站!
阿里巴巴誠信通企業(yè)
全國咨詢熱線:40000-63966
興邦電子,中國水控機(jī)第一品牌

聯(lián)系興邦電子

全國咨詢熱線:40000-63966

售后:0371-55132951/55132952

工廠:河南省 鄭州市 高新區(qū)蓮花街電子電器產(chǎn)業(yè)園

VC中PC/SC智能卡接口的編程

文章出處:http://m.compasssalessolutions.com 作者:蔣遂平   人氣: 發(fā)表時間:2011年10月09日

[文章內(nèi)容簡介]:終端應(yīng)用程序需要通過讀卡器來訪問智能卡,在一個系統(tǒng)中,通常存在多家廠商提供的讀卡器,因此需要一個統(tǒng)一的讀卡器設(shè)備驅(qū)動接口。

     1 引言

    完整的智能卡應(yīng)用系統(tǒng)由后臺服務(wù)程序、主機(jī)或終端應(yīng)用程序和智能卡等組成,其中,后臺服務(wù)程序提供了支持智能卡的服務(wù)。例如,在一個電子付款系統(tǒng)中,后臺服務(wù)程序可以提供到信用卡和帳戶信息的訪問;主機(jī)或終端應(yīng)用程序一般存在于臺式機(jī)或者終端、電子付款終端、手機(jī)或者一個安全子系統(tǒng)中,終端應(yīng)用程序要處理用戶、智能卡和后臺服務(wù)程序之間的通訊;智能卡則存儲用戶的一些信息。 

    終端應(yīng)用程序需要通過讀卡器來訪問智能卡,在一個系統(tǒng)中,通常存在多家廠商提供的讀卡器,因此需要一個統(tǒng)一的讀卡器設(shè)備驅(qū)動接口。 

    隨著智能卡的廣泛應(yīng)用,為解決計算機(jī)與各種讀卡器之間的互操作性問題,人們提出了PC/SC(Personal Computer/Smart Card)規(guī)范,PC/SC規(guī)范作為讀卡器和卡與計算機(jī)之間有一個標(biāo)準(zhǔn)接口,實現(xiàn)不同生產(chǎn)商的卡和讀卡器之間的互操作性,其獨(dú)立于設(shè)備的 API使得應(yīng)用程序開發(fā)人員不必考慮當(dāng)前實現(xiàn)形式和將來實現(xiàn)形式之間的差異,并避免了由于基本硬件改變而引起的應(yīng)用程序變更,從而降低了軟件開發(fā)成本。

    Microsoft在其Platform SDK中實現(xiàn)了PC/SC,作為連接智能卡讀卡器與計算機(jī)的一個標(biāo)準(zhǔn)模型,提供了獨(dú)立于設(shè)備的 API,并與Windows平臺集成。因此,我們可以用PC/SC接口來訪問智能卡。

    2 PC/SC概述

    PC/SC接口包含30多個以Scard為前綴的函數(shù),所有函數(shù)的原型都在winscard.h中聲明,應(yīng)用程序需要包含winscard.lib,所有函數(shù)的正常返回值都是SCARD_S_SUCCESS。在這30多個函數(shù)中,常用的函數(shù)只有幾個,與智能卡的訪問流程(圖2)對應(yīng),下面將詳細(xì)介紹這些常用函數(shù)。

    3 PC/SC的使用

    3.1建立資源管理器的上下文

    函數(shù)ScardEstablishContext()用于建立將在其中進(jìn)行設(shè)備數(shù)據(jù)庫操作的資源管理器上下文(范圍)。 

    函數(shù)原型:LONG SCardEstablishContext(DWORD dwScope, LPCVOID pvReserved1, LPCVOID pvReserved2, LPSCARDCONTEXT phContext);
各個參數(shù)的含義:(1)dwScope:輸入類型;表示資源管理器上下文范圍,取值為:SCARD_SCOPE_USER(在用戶域中完成設(shè)備數(shù)據(jù)庫操作)、SCARD_SCOPE_SYSTEM(在系統(tǒng)域中完成設(shè)備數(shù)據(jù)庫操作)。要求應(yīng)用程序具有相應(yīng)的操作權(quán)限。(2)pvReserved1:輸入類型;保留,必須為NULL。(3)pvReserved2:輸入類型;保留,必須為NULL。(4)phContext:輸出類型;建立的資源管理器上下文的句柄。 

    下面是建立資源管理器上下文的代碼:

    SCARDCONTEXT hSC; 
    LONG lReturn; 
    lReturn = SCardEstablishContext(SCARD_SCOPE_USER, NULL, NULL, &hSC); 
    if ( lReturn!=SCARD_S_SUCCESS ) 
    printf("Failed SCardEstablishContext\n");

    3.2 獲得系統(tǒng)中安裝的讀卡器列表

    函數(shù)ScardListReaders()可以列出系統(tǒng)中安裝的讀卡器的名字。 

    函數(shù)原型:LONG SCardListReaders(SCARDCONTEXT hContext, LPCTSTR mszGroups, LPTSTR mszReaders, LPDWORD pcchReaders);
各個參數(shù)的含義:(1)hContext:輸入類型;ScardEstablishContext()建立的資源管理器上下文的句柄,不能為NULL。(2)mszGroups:輸入類型;讀卡器組名,為NULL時,表示列出所有讀卡器。(3)mszReaders:輸出類型;系統(tǒng)中安裝的讀卡器的名字,各個名字之間用’\0’分隔,最后一個名字后面為兩個連續(xù)的’\0’。(4)pcchReaders:輸入輸出類型;mszReaders的長度。

    系統(tǒng)中可能安裝多個讀卡器,因此,需要保存各個讀卡器的名字,以便以后與需要的讀卡器建立連接。

    下面是獲得系統(tǒng)中安裝的讀卡器列表的代碼:

    char mszReaders[1024];
    LPTSTR pReader, pReaderName[2]; 
    DWORD dwLen=sizeof(mzsReaders); 
    int nReaders=0; 
    lReturn = SCardListReaders(hSC, NULL, (LPTSTR)mszReaders, &dwLen); 
    if ( lReturn==SCARD_S_SUCCESS ) 
    { 
    pReader = (LPTSTR)pmszReaders; 
    while (*pReader !='\0' ) 
    { 
    if ( nReaders<2 ) //使用系統(tǒng)中前2個讀卡器 
    pReaderName[nReaders++]=pReader; 
    printf("Reader: %S\n", pReader ); 
    //下一個讀卡器名 
    pReader = pReader + strlen(pReader) + 1; 
    } 
    }

    3.3 與讀卡器(智能卡)連接 

    函數(shù)ScardConnect()在應(yīng)用程序與讀卡器上的智能卡之間建立一個連接。

     函數(shù)原型:LONG SCardConnect(SCARDCONTEXT hContext, LPCTSTR szReader, DWORD dwShareMode, DWORD dwPreferredProtocols, LPSCARDHANDLE phCard, LPDWORD pdwActiveProtocol);

    各個參數(shù)的含義:(1)hContext:輸入類型;ScardEstablishContext()建立的資源管理器上下文的句柄。(2)szReader:輸入類型;包含智能卡的讀卡器名稱(讀卡器名稱由ScardListReaders()給出)。(3)dwShareMode:輸入類型;應(yīng)用程序?qū)χ悄芸ǖ牟僮鞣绞?,SCARD_SHARE_SHARED(多個應(yīng)用共享同一個智能卡)、SCARD_SHARE_EXCLUSIVE(應(yīng)用獨(dú)占智能卡)、SCARD_SHARE_DIRECT(應(yīng)用將智能卡作為私有用途,直接操縱智能卡,不允許其它應(yīng)用訪問智能卡)。(4)dwPreferredProtocols:輸入類型;連接使用的協(xié)議,SCARD_PROTOCOL_T0(使用T=0協(xié)議)、SCARD_PROTOCOL_T1(使用T=1協(xié)議)。(5)phCard:輸出類型;與智能卡連接的句柄。(6)PdwActiveProtocol:輸出類型;實際使用的協(xié)議。

    下面是與智能卡建立連接的代碼:

    SCARDHANDLE hCardHandle[2]; 
    DWORD dwAP; 
    lReturn = SCardConnect( hContext, pReaderName[0], SCARD_SHARE_SHARED, 
    SCARD_PROTOCOL_T0 | SCARD_PROTOCOL_T1, &hCardHandle[0], &dwAP ); 
    if ( lReturn!=SCARD_S_SUCCESS ) 
    { 
    printf("Failed SCardConnect\n"); 
    exit(1); 
    }

    與智能卡建立連接后,就可以向智能卡發(fā)送指令,與其交換數(shù)據(jù)了。

    3.4 向智能卡發(fā)送指令

    函數(shù)ScardTransmit()向智能卡發(fā)送指令,并接受返回的數(shù)據(jù)。

    函數(shù)原型:LONG SCardTransmit(SCARDHANDLE hCard, LPCSCARD_I0_REQUEST pioSendPci, LPCBYTE pbSendBuffer, DWORD cbSendLength, LPSCARD_IO_REQUEST pioRecvPci, LPBYTE pbRecvBuffer, LPDWORD pcbRecvLength);

    各個參數(shù)的含義:(1)hCard:輸入類型;與智能卡連接的句柄。(2)pioSendPci:輸入類型;指令的協(xié)議頭結(jié)構(gòu)的指針,由SCARD_IO_REQUEST結(jié)構(gòu)定義。后面是使用的協(xié)議的協(xié)議控制信息。一般使用系統(tǒng)定義的結(jié)構(gòu),SCARD_PCI_T0(T=0協(xié)議)、 SCARD_PCI_T1(T=1協(xié)議)、SCARD_PCI_RAW(原始協(xié)議)。(3)pbSendBuffer:輸入類型;要發(fā)送到智能卡的數(shù)據(jù)的指針。(4)cbSendLength:輸入類型;pbSendBuffer的字節(jié)數(shù)目。(5)pioRecvPci:輸入輸出類型;指令協(xié)議頭結(jié)構(gòu)的指針,后面是使用的協(xié)議的協(xié)議控制信息,如果不返回協(xié)議控制信息,可以為NULL。(6)pbRecvBuffer:輸入輸出類型;從智能卡返回的數(shù)據(jù)的指針。(7)pcbRecvLength:輸入輸出類型;pbRecvBuffer的大小和實際大小。

    對于T=0協(xié)議,收發(fā)緩沖的用法如下: 

    (a)向智能卡發(fā)送數(shù)據(jù):要向智能卡發(fā)送n>0字節(jié)數(shù)據(jù)時,pbSendBuffer 前4字節(jié)分別為T=0的CLA、INS、P1、P2,第5字節(jié)是n,隨后是n字節(jié)的數(shù)據(jù);cbSendLength值為n+5(4字節(jié)頭+1字節(jié)Lc+n字節(jié)數(shù)據(jù))。PbRecvBuffer將接收SW1、SW2狀態(tài)碼;pcbRecvLength值在調(diào)用時至少為2,返回后為2。 

    BYTE recvBuffer[260]; 
    int sendSize, recvSize; 
    BTYE sw1, sw2; 
    BYTE select_mf[]={0xC0, 0xA4, 0x00, 0x00, 0x02, 0x3F, 0x00}; 
    sendSize=7; 
    recvSize=sizeof(recvBuffer); 
    lReturn = SCardTransmit(hCardHandle[0], SCARD_PCI_T0, select_mf, sendSize, 
    NULL, recvBuffer, &recvSize); 
    if ( lReturn != SCARD_S_SUCCESS ) 
    { 
    printf("Failed SCardTransmit\n"); 
    exit(1); 
    } 
    //返回的數(shù)據(jù),recvSize=2 
    sw1=recvBuffer[recvSize-2]; 
    sw2=recvBuffer[recvSize-1];

    (b)從智能卡接收數(shù)據(jù):為從智能卡接收n>0字節(jié)數(shù)據(jù),pbSendBuffer 前4字節(jié)分別為T=0的CLA、INS、P1、P2,第5字節(jié)是n(即Le),如果從智能卡接收256字節(jié),則第5字節(jié)為0;cbSendLength值為5(4字節(jié)頭+1字節(jié)Le)。PbRecvBuffer將接收智能卡返回的n字節(jié),隨后是SW1、SW2狀態(tài)碼;pcbRecvLength的值在調(diào)用時至少為 n+2,返回后為n+2。

 

    BYTE get_challenge[]={0x00, 0x84, 0x00, 0x00, 0x08}; 
    sendSize=5; 
    recvSize=sizeof(recvBuffer); 
    lReturn = SCardTransmit(hCardHandle[0], SCARD_PCI_T0, get_challenge, 
    sendSize, NULL, recvBuffer, &recvSize); 
    if ( lReturn != SCARD_S_SUCCESS ) 
    { 
    printf("Failed SCardTransmit\n"); 
    exit(1); 
    } 
    //返回的數(shù)據(jù), recvSize=10 
    sw1=recvBuffer[recvSize-2]; 
    sw2=recvBuffer[recvSize-1]; 
    //data=recvBuffer[0]----recvBuffer[7]

    (c)向智能卡發(fā)送沒有數(shù)據(jù)交換的命令:應(yīng)用程序既不向智能卡發(fā)送數(shù)據(jù),也不從智能卡接收數(shù)據(jù),pbSendBuffer 前4字節(jié)分別為T=0的CLA、INS、P1、P2,不發(fā)送P3;cbSendLength 值必須為4。PbRecvBuffer從智能卡接收SW1、SW2狀態(tài)碼;pcbRecvLength值在調(diào)用時至少為2,返回后為2。

    BYTE set_flag[]={0x80, 0xFE, 0x00, 0x00}; 
    sendSize=4; 
    recvSize=sizeof(recvBuffer); 
    lReturn = SCardTransmit(hCardHandle[0], SCARD_PCI_T0, set_flag, sendSize, 
    NULL, recvBuffer, &recvSize); 
    if ( lReturn != SCARD_S_SUCCESS ) 
    { 
    printf("Failed SCardTransmit\n"); 
    exit(1); 
    } 
    //返回的數(shù)據(jù),recvSize=2 
    sw1=recvBuffer[recvSize-2]; 
    sw2=recvBuffer[recvSize-1];

    (d)向智能卡發(fā)送具有雙向數(shù)據(jù)交換的命令:T=0協(xié)議中,應(yīng)用程序不能同時向智能卡發(fā)送數(shù)據(jù),并從智能卡接收數(shù)據(jù),即發(fā)送到智能卡的指令中,不能同時有Lc和Le。這只能分兩步實現(xiàn):向智能卡發(fā)送數(shù)據(jù),接收智能卡返回的狀態(tài)碼,其中,SW2是智能卡將要返回的數(shù)據(jù)字節(jié)數(shù)目;從智能卡接收數(shù)據(jù)(指令為0x00、0xC0、0x00、0x00、Le)。

    BYTE get_response={0x00, 0xc0, 0x00, 0x00, 0x00}; 
    BYTE internal_auth[]={0x00, 0x88, 0x00, 0x00, 0x08, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08}; 
    sendSize=13; 
    recvSize=sizeof(recvBuffer); 
    lReturn = SCardTransmit(hCardHandle[0], SCARD_PCI_T0, internal_auth, 
    sendSize, NULL, recvBuffer, &recvSize); 
    if ( lReturn != SCARD_S_SUCCESS ) 
    { 
    printf("Failed SCardTransmit\n"); 
    exit(1); 
    } 
    //返回的數(shù)據(jù),recvSize=2 
    sw1=recvBuffer[recvSize-2]; 
    sw2=recvBuffer[recvSize-1]; 
    if ( sw1!=0x61 ) 
    { 
    printf("Failed Command\n"); 
    exit(1); 
    } 
    get_response[4]=sw2; 
    sendSize=5; 
    recvSize=sizeof(recvBuffer); 
    lReturn = SCardTransmit(hCardHandle[0], SCARD_PCI_T0, get_response, 
    sendSize, NULL, recvBuffer, &recvSize); 
    if ( lReturn != SCARD_S_SUCCESS ) 
    { 
    printf("Failed SCardTransmit\n"); 
    exit(1); 
    } 
    //返回的數(shù)據(jù),recvSize=10 
    sw1=recvBuffer[recvSize-2]; 
    sw2=recvBuffer[recvSize-1]; 
    //data=recvBuffer[0]----recvBuffer[7]

    3.5 斷開與讀卡器(智能卡)的連接

    在與智能卡的數(shù)據(jù)交換完成后,可以使用函數(shù)ScardDisconnect()終止應(yīng)用與智能卡之間的連接。 

    函數(shù)原型:LONG SCardDisconnect(SCARDHANDLE hCard, DWORD dwDisposition); 
    各個參數(shù)的含義:(1)hCard:輸入類型;與智能卡連接的句柄。(2)dwDisposition:輸入類型;斷開連接時,對智能卡的操作,SCARD_LEAVE_CARD(不做任何操作)、SCARD_RESET_CARD(復(fù)位智能卡)、SCARD_UNPOWER_CARD(給智能卡掉電)、SCARD_EJECT_CARD(彈出智能卡)。 

    下面是斷開與智能卡連接的代碼:

    lReturn = SCardDisconnect(hCardHandle[0], SCARD_LEAVE_CARD); 
    if ( lReturn != SCARD_S_SUCCESS ) 
    { 
    printf("Failed SCardDisconnect\n"); 
    exit(1); 
    }

    3.6 釋放資源管理上下文

    在應(yīng)用程序終止前時,應(yīng)該調(diào)用函數(shù)ScardReleaseContext()釋放資源管理器的上下文。 
    函數(shù)原型:LONG SCardReleaseContext(SCARDCONTEXT hContext); 
    各個參數(shù)含義:(1)hContext:輸入類型;ScardEstablishContext()建立的資源管理器上下文的句柄,不能為NULL。 
    下面是釋放資源管理上下文的代碼: 
    lReturn = SCardReleaseContext(hSC); 
    if ( lReturn!=SCARD_S_SUCCESS ) 
    printf("Failed SCardReleaseContext\n");

    4 小結(jié)

    以上介紹的通過PC/SC來操作智能卡的流程,可以封裝在一個類中。例如,我們可以設(shè)計一個類: 
    class CSmartReader 
    { 
    private: 
    SCARDCONTEXT hSC; 
    LONG lReturn; 
    char mszReaders[1024]; 
    LPTSTR pReader, pReaderName[2]; 
    DWORD dwLen; 
    int nReaders, nCurrentReader; 
    SCARDHANDLE hCardHandle[2]; 
    DWORD dwAP; 
    public: 
    CSmartReader(); //建立上下文、取讀卡器列表 
    ~CSmartReader(); //釋放上下文 
    void SetCurrentReader(int currentReader); 
    int GetReaders(); //獲得讀卡器數(shù)目 
    int ConnectReader(); //與當(dāng)前讀卡器建立連接 
    int DisConnectReader(); //與當(dāng)前讀卡器斷開連接 
    int SendCommand(BYTE command[], int commandLength, BYTE result[], int *resultLength); //向讀卡器發(fā)送命令,并接收返回的數(shù)據(jù)。返回值為sw 
    };

    這樣,我們就可以方便地使用PC/SC接口了。

本文關(guān)鍵詞:智能卡,接口,讀卡器
回到頂部