使用相同的 T=1 APDU 发送和接收数据
Sending and receiving data using same T=1 APDU
我正在尝试发送一个包含一些命令数据的 APDU,然后期望从卡响应一些数据。我使用 this example code by Ludovic Rousseau 作为起点(修改后的代码)。
我发送的 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 中做同样的事情时,一切都按预期工作,即使用与输入相同的 APDU 字节调用 data, sw1, sw2 = connection.transmit(...)
使得 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);
}
我不确定为什么您会通过 pyscard 收到正确的数据。但是,在apdu.sendBytesLong()
中使用bytesRead
作为长度显然是错误的:
- 这将导致发送零字节。如果命令数据的所有字节都适合 APDU 缓冲区并且已经通过
setIncomingAndReceive()
检索,就会出现这种情况。在这种情况下,apdu.receiveBytes(ISO7816.OFFSET_CDATA)
将 return 为零,而 bytesRead
在调用 sendBytesLong()
. 时将为零
- 否则只会发送几个字节。如果命令数据字段中的字节数超过 APDU 缓冲区的字节数,就会出现这种情况。在这种情况下,
bytesRead
将被设置为上次调用 apdu.receiveBytes(ISO7816.OFFSET_CDATA)
时接收到的字节数 (N)。 sendBytesLong()
会 return 从命令数据开始的字节数 (N)。
因此,计数应该是 numBytes
:
apdu.sendBytesLong(transientMemory, (short)0, numBytes);
我正在尝试发送一个包含一些命令数据的 APDU,然后期望从卡响应一些数据。我使用 this example code by Ludovic Rousseau 作为起点(修改后的代码)。
我发送的 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 中做同样的事情时,一切都按预期工作,即使用与输入相同的 APDU 字节调用 data, sw1, sw2 = connection.transmit(...)
使得 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);
}
我不确定为什么您会通过 pyscard 收到正确的数据。但是,在apdu.sendBytesLong()
中使用bytesRead
作为长度显然是错误的:
- 这将导致发送零字节。如果命令数据的所有字节都适合 APDU 缓冲区并且已经通过
setIncomingAndReceive()
检索,就会出现这种情况。在这种情况下,apdu.receiveBytes(ISO7816.OFFSET_CDATA)
将 return 为零,而bytesRead
在调用sendBytesLong()
. 时将为零
- 否则只会发送几个字节。如果命令数据字段中的字节数超过 APDU 缓冲区的字节数,就会出现这种情况。在这种情况下,
bytesRead
将被设置为上次调用apdu.receiveBytes(ISO7816.OFFSET_CDATA)
时接收到的字节数 (N)。sendBytesLong()
会 return 从命令数据开始的字节数 (N)。
因此,计数应该是 numBytes
:
apdu.sendBytesLong(transientMemory, (short)0, numBytes);