Я пытаюсь прочитать личный файл эстонской идентификационной карты.
Мне нужно отправить следующие данные на карту (из
Вот) для чтения записей из личного файла (т.е. идентификационный номер, имя и т. д.):
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 = 1 на неоправданно долгое время, прежде чем принять T = 0. Последовательность событий следующая:
SCardStatusChange
и начинает обрабатывать картуSCardConnect
или же SCardReconnect
) Ошибка SCARD_E_SHARING_VIOLATION
принимается снова и снова, в течение 5 сSCARD_E_PROTO_MISMATCH
принимается от 3 до 30 секунд, может быть, больше.Можно ли как-то быстрее подключиться к нему по протоколу 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!
}
}
Вы спрашиваете 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()
принять и то и другое T0
а также T1
протоколы в то же время, или объединяя их в dwPreferredProtocol
параметр (см. пример в документация). Тогда, если pdwActiveProtocol
выходы параметров, которые T1
является активным протоколом, вы можете ждать изменения статуса на T0
протокол перед чтением карты.
После долгих поисков в интернете я обнаружил, что никаких изменений в командах не требуется.
Мне просто нужно было добавить дополнительный нулевой байт в конец переданных команд чтения / открытия и получить данные в ответ на команду чтения, а также не использовать отдельную команду для получения байтов. (T = 0 использует модель «запрос данных / чтение данных», в то время как T = 1, кажется, просто отвечает данными)
Мне также нужно было изменить все упоминания SCARD_PCI_T0
условно использовать SCARD_PCI_T1
и сделать SCardConnect()
Функция принимает T1 тоже.
Я опубликую достойный пример кода здесь позже.