连接爱沙尼亚身份证时,WinSCard SCARD_E_PROTO_MISMATCH

WinSCard SCARD_E_PROTO_MISMATCH when connecting to Estonian ID card

本文关键字:PROTO MISMATCH SCARD WinSCard 爱沙尼亚 身份证 连接      更新时间:2023-10-16

我正在尝试读取爱沙尼亚身份证的个人文件。

我需要将以下数据发送到卡(从)在这里),以便从个人文件中读取记录(如ID号,姓名等):

00 A4 00 0C          # We choose root folder
00 A4 01 0C 02 EE EE # We choose folder EEEE
00 A4 02 04 02 50 44 # We choose file 5044, which contains personal data
00 B2 XX 04          # We read record XX (see table) from the personal data file
                     # The card responds 61 YY, where YY denotes the number of bytes waiting to be read
00 C0 00 00 YY       # We read YY bytes from the card
                     # Card responds ... 90 00, where ... is the requested data and 90 00 means status OK 

然而,原始字节是在T=0协议中,并且卡在接受T=0之前被卡在T=1中不合理的长时间。事件顺序如下:

  1. 卡连接到读取器
  2. 程序从SCardStatusChange返回并开始处理卡片
  3. 在尝试连接到卡(SCardConnectSCardReconnect)错误SCARD_E_SHARING_VIOLATION被反复接收,大约5s
  4. 然后,在尝试连接时,错误SCARD_E_PROTO_MISMATCH被接收到3到30秒之间,可能更长。
  5. 之后,卡连接成功并读取数据。

我能在T=0协议中更快地连接到它吗?

源代码的简化版本如下:

// NOTE: this is approximately what I do.
// I haven't tested this code yet - it's almost 1 AM here.
#include <winscard.h>
void readSmartCard() {
    LONG sCardErrorCode;
    SCARDCONTEXT sCardContext;
    DWORD sCardReaderStrLen = 1024;
    wchar_t sCardReaderStr[1024];
    SCARDHANDLE sCardHandle;
    DWORD sCardActiveProtocol;
    SCARD_READERSTATE readerState;
    sCardErrorCode = SCardEstablishContext(SCARD_SCOPE_SYSTEM, NULL, NULL, &sCardContext);
    // error handling macro
    ZeroMemory(&sCardReaderState, sizeof(sCardReaderState));
    sCardReaderState.szReader = L"\\?PnP?\Notification";
    sCardReaderState.pvUserData = nullptr;
    sCardReaderState.dwEventState = SCARD_STATE_PRESENT;
    sCardErrorCode = SCardGetStatusChange(sCardContext, INFINITE, &readerState, 1);
    // e.h.m
    if (readerState.dwCurrentState == 65538) {
        sCardErrorCode = SCardListReaders(sCardContext, NULL, sCardReaderStr, &sCardReaderStrLen);
        // e.h.m
        readerState.szReader = sCardReaderStr;
    }
    sCardErrorCode = SCardGetStatusChange(sCardContext, INFINITE, &readerState, 1);
    // e.h.m
    if (sCardReaderState.dwEventState & SCARD_STATE_PRESENT) {
        while (true) {
            sCardErrorCode = SCardConnect(sCardContext, readerState.szReader, SCARD_SHARE_EXCLUSIVE,
                SCARD_PROTOCOL_T0, &sCardHandle, &sCardActiveProtocol);
            // e.h.m
            printf("%x", sCardErrorCode); 
            // this will print:
            // 8010000b (for around 5s)
            // 8010000f (for around 20s)
            if (sCardErrorCode == SCARD_S_SUCCESS) {
                break;
            }
            Sleep(1000);
        }
        // open personal file and read data, yay!
    }
}

在互联网上搜索了很长时间后,我发现不需要对命令进行任何更改。

我只需要在传输的read/open命令的末尾添加一个额外的零字节,并作为read命令的响应接收数据,而不是使用单独的命令来接收字节。(T=0使用"请求数据/读取数据"模型,而T=1只是响应数据,似乎)

我还需要更改SCARD_PCI_T0的所有提及,以有条件地使用SCARD_PCI_T1,并使SCardConnect()函数也接受T1。

我将在这里发布一个像样的代码示例。

您只要求SCardConnect()独占访问T0协议,因此,如果卡正在使用中,则返回SCARD_E_SHARING_VIOLATION,如果卡未使用但T1处于活动状态,则返回SCARD_E_PROTO_MISMATCH

SCardGetStatusChange()报告通知时,您只检查SCARD_STATE_PRESENT标志,但是您忽略的其他标志也可以存在,例如SCARD_STATE_INUSE

如果你需要独占访问阅读器,你将不得不等到SCARD_STATE_INUSE被清除,和/或SCARD_E_SHARING_VIOLATION不再开始报告。对此你无能为力,除非你改变你的逻辑,允许以共享模式而不是独占模式连接。

如果你想连接阅读器,而不管它的当前协议(从而获得排他访问更快),你可以要求SCardConnect()同时接受 T0T1协议,通过在dwPreferredProtocol参数中将它们放在一起(参见文档中的示例)。然后,如果pdwActiveProtocol参数显示T1为活动协议,则可以等待状态变为T0协议后再读卡。