2017-08-17 90 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