简体   繁体   English

使用相同的T = 1 APDU发送和接收数据

[英]Sending and receiving data using same T=1 APDU

I'm trying to send an APDU that contains some command data, and then expect some data in response from the card. 我正在尝试发送包含一些命令数据的APDU,然后期望从卡中响应一些数据。 I'm using this example code by Ludovic Rousseau as a starting to point (modified code below). 我正在使用Ludovic Rousseau的这个示例代码作为起点(下面的修改代码)。

The APDU I'm sending is the following: 我发送的APDU如下:

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

Ie I've picked CLA 0x80 , INS 0x02 , not using P1 and P2, Lc and Le both 0x08 . 即我已经选择了CLA 0x80 ,INS 0x02 ,不使用P1和P2,Lc和Le都是0x08

The data buffer I get back only contains 0x90 0x00 . 我得到的数据缓冲区只包含0x90 0x00

I've checked which protocol gets negotiated - that's T=1, as expected. 我已经检查了哪个协议得到了协商 - 这是T = 1,正如预期的那样。 If it were T=0, I would expect to get a 61XX -series answer (see this related question ). 如果它是T = 0,我希望得到一个61XX系列的答案(参见这个相关的问题 )。

Every other APDU format works just fine (ie empty, only sending or only receiving data). 每个其他APDU格式都可以正常工作(即空,只发送或仅接收数据)。 Is there something I'm overlooking here? 有什么东西我在这里俯瞰吗?

// 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;
}

This gives as output: 这给出了输出:

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 

When I try to do the same thing in Python using pyscard , everything works as expected, ie calling data, sw1, sw2 = connection.transmit(...) with the same APDU bytes as input makes data contain the expected data. 当我尝试使用pyscard在Python中做同样的事情时,一切都按预期工作,即调用data, sw1, sw2 = connection.transmit(...)与输入相同的APDU字节使data包含预期的数据。

This makes me believe that the relevant code on the card is fine (but also posted below for completeness). 这让我相信卡上的相关代码很好(但为了完整性也在下面发布)。

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);
}

I'm not sure why you would receive the correct data through pyscard. 我不确定为什么你会通过pyscard收到正确的数据。 However, using bytesRead as the length in apdu.sendBytesLong() is obviously a mistake: 但是,使用bytesRead作为apdu.sendBytesLong()的长度显然是一个错误:

  • This would either result zero bytes being sent. 这将导致发送零字节。 This is the case if all bytes of the command data fit into the APDU buffer and were already retrieved through setIncomingAndReceive() . 如果命令数据的所有字节都适合APDU缓冲区并且已经通过setIncomingAndReceive()检索, setIncomingAndReceive() In that case, apdu.receiveBytes(ISO7816.OFFSET_CDATA) will return zero and bytesRead would be zero upon invoking sendBytesLong() . 在这种情况下, apdu.receiveBytes(ISO7816.OFFSET_CDATA)将返回零,并且在调用sendBytesLong()bytesRead将为零。
  • Or it would result in only a few bytes being sent. 或者它只会导致发送几个字节。 This is the case if there were more bytes in the command data field than would fit into the APDU buffer. 如果命令数据字段中的字节数多于适合APDU缓冲区的字节数,则会出现这种情况。 In that case, bytesRead would be set to the number (N) of bytes that were received with the last call of apdu.receiveBytes(ISO7816.OFFSET_CDATA) . 在这种情况下, bytesRead将被设置为最后一次调用apdu.receiveBytes(ISO7816.OFFSET_CDATA)收到的字节数(N apdu.receiveBytes(ISO7816.OFFSET_CDATA) sendBytesLong() would return exactly this amount (N) of bytes from the beginning of the command data. sendBytesLong()将从命令数据的开头准确返回此数量(N)的字节。

Consequently, the count should probably be numBytes : 因此,计数应该是numBytes

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

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM