2017-08-17 103 views
3

我試圖發送一個APDU,其中包含一些命令數據,然後期望從卡中的一些數據響應。我使用this example code by Ludovic Rousseau作爲開始點(修改後的代碼)。我送使用相同的發送和接收數據T = 1 APDU

的APDU如下:

 
0x80 0x02 0x00 0x00 0x08 0x01 0x02 0x03 0x04 0x05 0x06 0x07 0x08 0x08 

即我選擇了CLA 0x80,INS 0x02,未使用P1和P2,Lc和Le均爲0x08

我回來的數據緩衝區只包含0x90 0x00

我已經檢查了哪個協議得到了協商 - 即T = 1,如預期。如果它是T = 0,我期望得到一個61XX-系列答案(見this related question)。

其他每種APDU格式都可以正常工作(即空,只發送或只接收數據)。我在這裏忽略了什麼嗎?

// source: https://ludovicrousseau.blogspot.nl/2010/04/pcsc-sample-in-c.html 
// This is based on code by Ludovic Rousseau, modified to match our example 

#ifdef WIN32 
#undef UNICODE 
#endif 

#include <stdio.h> 
#include <stdlib.h> 

#ifdef __APPLE__ 
#include <PCSC/winscard.h> 
#include <PCSC/wintypes.h> 
#else 
#include <winscard.h> 
#endif 

#ifdef WIN32 
static char *pcsc_stringify_error(LONG rv) 
{ 
static char out[20]; 
sprintf_s(out, sizeof(out), "0x%08X", rv); 

return out; 
} 
#endif 

#define CHECK(f, rv) \ 
if (SCARD_S_SUCCESS != rv) \ 
{ \ 
    printf(f ": %s\n", pcsc_stringify_error(rv)); \ 
    return -1; \ 
} 

int main(void) 
{ 
LONG rv; 

SCARDCONTEXT hContext; 
LPTSTR mszReaders; 
SCARDHANDLE hCard; 
DWORD dwReaders, dwActiveProtocol, dwRecvLength; 

SCARD_IO_REQUEST pioSendPci; 
BYTE pbRecvBuffer[258]; 
BYTE selectapdu[] = { 0x00, 0xA4, 0x04, 0x00, 0x0A, 
         0x01, 0x02, 0x03, 0x04, 0x05, 
         0x48, 0x45, 0x4C, 0x4C, 0x4F }; 
BYTE echoapdu[] = { 0x80, 0x02, 0x00, 0x00, 0x08, 
         0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 
         0x08 }; 

unsigned int i; 

rv = SCardEstablishContext(SCARD_SCOPE_SYSTEM, NULL, NULL, &hContext); 
CHECK("SCardEstablishContext", rv) 

#ifdef SCARD_AUTOALLOCATE 
dwReaders = SCARD_AUTOALLOCATE; 

rv = SCardListReaders(hContext, NULL, (LPTSTR)&mszReaders, &dwReaders); 
CHECK("SCardListReaders", rv) 
#else 
rv = SCardListReaders(hContext, NULL, NULL, &dwReaders); 
CHECK("SCardListReaders", rv) 

mszReaders = calloc(dwReaders, sizeof(char)); 
rv = SCardListReaders(hContext, NULL, mszReaders, &dwReaders); 
CHECK("SCardListReaders", rv) 
#endif 
printf("reader name: %s\n", mszReaders); 

rv = SCardConnect(hContext, mszReaders, SCARD_SHARE_SHARED, 
    SCARD_PROTOCOL_T0 | SCARD_PROTOCOL_T1, &hCard, &dwActiveProtocol); 
CHECK("SCardConnect", rv) 

switch(dwActiveProtocol) 
{ 
    case SCARD_PROTOCOL_T0: 
    printf("T0\n"); 
    pioSendPci = *SCARD_PCI_T0; 
    break; 

    case SCARD_PROTOCOL_T1: 
    printf("T1\n"); 
    pioSendPci = *SCARD_PCI_T1; 
    break; 
} 

// selecting the application 

dwRecvLength = sizeof(pbRecvBuffer); 
rv = SCardTransmit(hCard, &pioSendPci, selectapdu, sizeof(selectapdu), 
    NULL, pbRecvBuffer, &dwRecvLength); 
CHECK("SCardTransmit", rv) 

printf("response (%d): ", dwRecvLength); 
for(i=0; i<dwRecvLength; i++) 
    printf("%02X ", pbRecvBuffer[i]); 
printf("\n"); 

// sending a non-empty APDU that expects a reply 

dwRecvLength = sizeof(pbRecvBuffer); 
printf("sent (%d): ", sizeof(echoapdu)); 
for(i=0; i<sizeof(echoapdu); i++) 
    printf("%02X ", echoapdu[i]); 
printf("\n"); 
rv = SCardTransmit(hCard, &pioSendPci, echoapdu, sizeof(echoapdu), 
    NULL, pbRecvBuffer, &dwRecvLength); 
CHECK("SCardTransmit", rv) 

printf("response (%d): ", dwRecvLength); 
for(i=0; i<dwRecvLength; i++) 
    printf("%02X ", pbRecvBuffer[i]); 
printf("\n"); 

// disconnecting 

rv = SCardDisconnect(hCard, SCARD_LEAVE_CARD); 
CHECK("SCardDisconnect", rv) 

#ifdef SCARD_AUTOALLOCATE 
rv = SCardFreeMemory(hContext, mszReaders); 
CHECK("SCardFreeMemory", rv) 

#else 
free(mszReaders); 
#endif 

rv = SCardReleaseContext(hContext); 

CHECK("SCardReleaseContext", rv) 

return 0; 
} 

這給作爲輸出:

reader name: OMNIKEY AG CardMan 3121 00 00 
T1 
response (2): 90 00 
sent (14): 80 02 00 00 08 01 02 03 04 05 06 07 08 08 
response (2): 90 00 

當我嘗試使用pyscard做在Python同樣的事情,一切正常,即調用data, sw1, sw2 = connection.transmit(...)具有相同APDU字節輸入使得data包含預期的數據。

這使我相信卡上的相關代碼是好的(但爲了完整性也在下面發佈)。

private void getEcho(APDU apdu) { 
    byte[] buffer = apdu.getBuffer(); 
    short numBytes = (short) (buffer[ISO7816.OFFSET_LC] & 0x00FF); 
    short bytesRead = apdu.setIncomingAndReceive(); 
    short pos = 0; 

    while (pos < numBytes) { 
     Util.arrayCopyNonAtomic(buffer, ISO7816.OFFSET_CDATA, transientMemory, pos, bytesRead); 
     pos += bytesRead; 
     bytesRead = apdu.receiveBytes(ISO7816.OFFSET_CDATA); 
    } 
    apdu.setOutgoing(); 
    apdu.setOutgoingLength(numBytes); 
    apdu.sendBytesLong(transientMemory, (short)0, bytesRead); 
} 

回答

3

我不知道爲什麼你會通過pyscard收到正確的數據。然而,使用bytesReadapdu.sendBytesLong()長度顯然是錯誤的:

  • 這要麼導致被髮送零個字節。如果命令數據的所有字節都適合APDU緩衝區並且已通過setIncomingAndReceive()檢索到,則情況就是如此。在這種情況下,apdu.receiveBytes(ISO7816.OFFSET_CDATA)將返回零,bytesRead在調用sendBytesLong()時將爲零。
  • 或者它會導致只有幾個字節被髮送。如果命令數據字段中的字節數多於APDU緩衝區中的字節數,則是這種情況。在這種情況下,bytesRead將被設置爲最後一次調用apdu.receiveBytes(ISO7816.OFFSET_CDATA)時收到的字節數(N)。 sendBytesLong()將從命令數據的開始處精確返回這個數量(N)的字節。

因此,計數可能應該numBytes

apdu.sendBytesLong(transientMemory, (short)0, numBytes); 
+0

你是正確的,它應該已經'numBytes'(我已經相應改變的話),但不具有任何實際這種情況下的世界影響,因爲看起來我的測試足夠小以適應單個APDU緩衝區。 – Joost

+1

這讓我看到了這一行,然後我改變了一下代碼,使用'sendBytes'而不是'sendBytesLong'。這_does_產生預期的結果..但顯然只適用於適合實現的APDU緩衝區的小數據序列。任何想法在這裏有什麼不同之處?這能解釋爲什麼'pyscard'確實有效嗎? – Joost

+2

哎呀,看起來我搞砸了我的測試,你的修復確實是唯一需要改變的東西。現在我已經恢復到與修復相結合的問題中發佈的狀態,它按預期工作。謝謝! – Joost