Карта без контактов через Omnikey, как получить «uid»?
-
12-10-2019 - |
Вопрос
Я пытаюсь прочитать информацию с SmartCard, используя считыватель карт Omnikey 5321.
Редактировать: Добавил щедрость.
Я пишу C# 3 в программе .NET 3.5, так что это приложение Windows.
На карточке есть некоторая информация, нанесенная на нее, что я бы предположил, каким -то образом присутствует в данных карты (ниже карты и читателя ниже.)
На карте есть следующая информация, отмеченная на ней:
1* 00447 21091328-32
При использовании приложения для диагностики, которое поставляется с считывателем карт Omnikey, я получаю следующую информацию:
Smart Card Name: iCLASS 16KS UID:EE 74 0E 00 FB FF 12 E0 ATR : 3B 8F 80 01 80 4F 0C A0 00 00 03 06 0A 00 1A 00 00 00 00 78 Protocol : ISO 15693 (Part 2)
Теперь вот некоторые преобразования, которые я рассмотрел:
- 447 Десятичный = 1BF шестнадцатеричный (не найденный)
- 447 окт = 295 десятичных знаков (не найдено в виде кодирования типа BCD)
- 447 окт = 127 шестнадцатеричный (не найденный)
- 447 шестнадцатеричный не найден
Вот мои вопросы:
- Является ли номер «UID» уникальным номером, на который я могу положиться? Меня не волнует номер 447, все, что мне нужно знать, это то, что информация, которую я выбираю из этой карты, будет однозначно идентифицировать ее позже, чтобы я мог связать ее с владельцем карты
- Как я бы прочитал номер UID? Используя winscard.dll в Windows, я вижу, что я получаю данные «ATR», каждый байт, но UID, по -видимому, нет в этой части.
Вот фото, если это дает вам какую -либо информацию.
Решение
Вы можете положиться на UID, но это кажется усеченным в вашем случае:
UID:EE 74 0E 00 FB FF 12 E0
UID обычно 16 байт длиной.
Вы можете прочитать этот уникальный идентификатор (UID): все ISO-совместимые смарт-карты предоставляются с номером UID (сродни номеру VIN на транспортном средстве). Для целей совместимости открыт UID карты и доступен для прочтения всем соответствующим читателям. Поскольку это уникальное число не закреплено ключами, чтение UID смарт -карты сопоставимо с чтением карты близости, карты магнитной полосы или другой технологии, которая использует открытые, незащищенные цифры.
Другие советы
Недавно я провожу слишком много часов на поиски полный Пример того, как получить ATR и Omnikey Bitxity Card ...
Теперь, когда у меня работает код, я хотел бы поделиться, чтобы другие могли извлечь выгоду.
Лучший код, который я нашел, был из SpringCard. (Благодарность!)
Другие примеры, которые я нашел, были вводящими во время и вводящими в заблуждение и не работали, потому что Dllimport был неправ ...
using System;
using System.Collections.Generic;
using System.Text;
using System.Threading;
using System.Runtime.InteropServices;
namespace Test
{
public delegate void VoidDelegate();
public delegate void CardPresented(string reader, byte[] cardData);
public class ReaderList : IDisposable, IEnumerable<string>
{
public ReaderList()
{ }
public void Dispose()
{
StopThread();
}
private Thread thread;
private void StartThread()
{
if (thread != null)
StopThread();
thread = new Thread(Run);
thread.IsBackground = true;
bStopThread = false;
thread.Start();
}
private void StopThread()
{
if (thread != null)
{
bStopThread = true;
Thread.Sleep(50);
}
if (thread != null)
thread.Abort();
if (thread != null)
thread.Join();
thread = null;
}
private List<string> readerNames = new List<string>();
private Dictionary<string, string> lastCardFound = new Dictionary<string, string>();
public int ReaderCount
{ get { return readerNames.Count; } }
public void Refresh()
{
if (thread == null)
StartThread();
}
public event VoidDelegate ListChanged;
public event CardPresented CardPresented;
private bool bStopThread = true;
private void Run()
{
IntPtr hContext = IntPtr.Zero;
try
{
uint result = SCARD.EstablishContext(SCARD.SCOPE_SYSTEM, IntPtr.Zero, IntPtr.Zero, ref hContext);
if (result != SCARD.S_SUCCESS)
{
thread = null;
return;
}
uint notification_state = SCARD.STATE_UNAWARE;
while (!bStopThread) // loop 1 - build list, then iterate
{
SCARD.ReaderState[] states = new SCARD.ReaderState[ReaderCount + 1];
states[0] = new SCARD.ReaderState(@"\\?PNP?\NOTIFICATION");
states[0].dwCurrentState = notification_state;
int iState = 0;
if (readerNames != null)
foreach (string s in readerNames)
{
iState++;
states[iState] = new SCARD.ReaderState(s);
states[iState].dwCurrentState = SCARD.STATE_UNAWARE;
}
while (!bStopThread) // loop 2 - iterate over list built above
{
result = SCARD.GetStatusChange(hContext, 250, states, (uint)states.Length);
if (result == SCARD.E_TIMEOUT)
continue;
if (result != SCARD.S_SUCCESS)
break;
bool bReaderListChanged = false;
for (int i = 0; i < states.Length; i++)
if ((states[i].dwEventState & SCARD.STATE_CHANGED) != 0)
if (i == 0)
{
// reader added or removed
notification_state = states[0].dwEventState;
// we want to replace the member in one step, rather than modifying it...
List<string> tmp = GetReaderList(hContext, SCARD.GROUP_ALL_READERS);
if (tmp == null)
readerNames.Clear();
else
readerNames = tmp;
if (ListChanged != null)
ListChanged();
bReaderListChanged = true;
}
else
{
// card added or removed
states[i].dwCurrentState = states[i].dwEventState;
if ((states[i].dwEventState & SCARD.STATE_PRESENT) != 0)
{
byte[] cardData = new byte[states[i].cbATR];
for (int j=0; j<cardData.Length; j++)
cardData[j] = states[i].rgbATR[j];
string thisCard = SCARD.ToHex(cardData, "");
string lastCard;
lastCardFound.TryGetValue(states[i].szReader, out lastCard);
if (thisCard != lastCard)
{
lastCardFound[states[i].szReader] = thisCard;
if (CardPresented != null)
CardPresented(states[i].szReader, cardData);
}
}
else
lastCardFound[states[i].szReader] = "";
}
if (bReaderListChanged)
break; // break out of loop 2, and re-build our 'states' list
} // end loop 2
} // end loop 1
}
catch (Exception ex)
{
//TODO: error logging
}
finally
{
if (hContext != IntPtr.Zero)
SCARD.ReleaseContext(hContext);
thread = null;
}
}
private List<string> GetReaderList(IntPtr hContext, string sGroup)
{
uint nStringLength = 0;
uint result = SCARD.ListReaders(hContext, sGroup, null, ref nStringLength);
if (result != SCARD.S_SUCCESS)
return null;
string sReaders = new string(' ', (int)nStringLength);
result = SCARD.ListReaders(hContext, sGroup, sReaders, ref nStringLength);
if (result != SCARD.S_SUCCESS)
return null;
List<string> list = new List<string> (sReaders.Split('\0'));
for (int i = 0; i < list.Count; )
if (list[i].Trim().Length > 0)
i++;
else
list.RemoveAt(i);
return list;
}
public IEnumerator<string> GetEnumerator()
{ return readerNames.GetEnumerator(); }
System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
{ return readerNames.GetEnumerator(); }
}
public class SCARD
{
[DllImport("WinScard.dll", EntryPoint = "SCardEstablishContext")]
public static extern uint EstablishContext(
uint dwScope,
IntPtr nNotUsed1,
IntPtr nNotUsed2,
ref IntPtr phContext);
[DllImport("WinScard.dll", EntryPoint = "SCardReleaseContext")]
public static extern uint ReleaseContext(
IntPtr hContext);
[DllImport("winscard.dll", EntryPoint = "SCardGetStatusChangeW", CharSet = CharSet.Unicode)]
public static extern uint GetStatusChange(
IntPtr hContext,
uint dwTimeout,
[In, Out, MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 3)]
SCARD.ReaderState[] rgReaderState,
uint cReaders);
[DllImport("winscard.dll", EntryPoint = "SCardListReadersW", CharSet = CharSet.Unicode)]
public static extern uint ListReaders(
IntPtr hContext,
string groups,
string readers,
ref uint size);
#region Error codes
public const uint S_SUCCESS = 0x00000000;
public const uint F_INTERNAL_ERROR = 0x80100001;
public const uint E_CANCELLED = 0x80100002;
public const uint E_INVALID_HANDLE = 0x80100003;
public const uint E_INVALID_PARAMETER = 0x80100004;
public const uint E_INVALID_TARGET = 0x80100005;
public const uint E_NO_MEMORY = 0x80100006;
public const uint F_WAITED_TOO_LONG = 0x80100007;
public const uint E_INSUFFICIENT_BUFFER = 0x80100008;
public const uint E_UNKNOWN_READER = 0x80100009;
public const uint E_TIMEOUT = 0x8010000A;
public const uint E_SHARING_VIOLATION = 0x8010000B;
public const uint E_NO_SMARTCARD = 0x8010000C;
public const uint E_UNKNOWN_CARD = 0x8010000D;
public const uint E_CANT_DISPOSE = 0x8010000E;
public const uint E_PROTO_MISMATCH = 0x8010000F;
public const uint E_NOT_READY = 0x80100010;
public const uint E_INVALID_VALUE = 0x80100011;
public const uint E_SYSTEM_CANCELLED = 0x80100012;
public const uint F_COMM_ERROR = 0x80100013;
public const uint F_UNKNOWN_ERROR = 0x80100014;
public const uint E_INVALID_ATR = 0x80100015;
public const uint E_NOT_TRANSACTED = 0x80100016;
public const uint E_READER_UNAVAILABLE = 0x80100017;
public const uint P_SHUTDOWN = 0x80100018;
public const uint E_PCI_TOO_SMALL = 0x80100019;
public const uint E_READER_UNSUPPORTED = 0x8010001A;
public const uint E_DUPLICATE_READER = 0x8010001B;
public const uint E_CARD_UNSUPPORTED = 0x8010001C;
public const uint E_NO_SERVICE = 0x8010001D;
public const uint E_SERVICE_STOPPED = 0x8010001E;
public const uint E_UNEXPECTED = 0x8010001F;
public const uint E_ICC_INSTALLATION = 0x80100020;
public const uint E_ICC_CREATEORDER = 0x80100021;
public const uint E_UNSUPPORTED_FEATURE = 0x80100022;
public const uint E_DIR_NOT_FOUND = 0x80100023;
public const uint E_FILE_NOT_FOUND = 0x80100024;
public const uint E_NO_DIR = 0x80100025;
public const uint E_NO_FILE = 0x80100026;
public const uint E_NO_ACCESS = 0x80100027;
public const uint E_WRITE_TOO_MANY = 0x80100028;
public const uint E_BAD_SEEK = 0x80100029;
public const uint E_INVALID_CHV = 0x8010002A;
public const uint E_UNKNOWN_RES_MNG = 0x8010002B;
public const uint E_NO_SUCH_CERTIFICATE = 0x8010002C;
public const uint E_CERTIFICATE_UNAVAILABLE = 0x8010002D;
public const uint E_NO_READERS_AVAILABLE = 0x8010002E;
public const uint E_COMM_DATA_LOST = 0x8010002F;
public const uint E_NO_KEY_CONTAINER = 0x80100030;
public const uint W_UNSUPPORTED_CARD = 0x80100065;
public const uint W_UNRESPONSIVE_CARD = 0x80100066;
public const uint W_UNPOWERED_CARD = 0x80100067;
public const uint W_RESET_CARD = 0x80100068;
public const uint W_REMOVED_CARD = 0x80100069;
public const uint W_SECURITY_VIOLATION = 0x8010006A;
public const uint W_WRONG_CHV = 0x8010006B;
public const uint W_CHV_BLOCKED = 0x8010006C;
public const uint W_EOF = 0x8010006D;
public const uint W_CANCELLED_BY_USER = 0x8010006E;
public const uint W_CARD_NOT_AUTHENTICATED = 0x8010006F;
#endregion
public const uint SCOPE_USER = 0;
public const uint SCOPE_TERMINAL = 1;
public const uint SCOPE_SYSTEM = 2;
public const string GROUP_ALL_READERS = "SCard$AllReaders\0\0";
public const string GROUP_DEFAULT_READERS = "SCard$DefaultReaders\0\0";
public const string GROUP_LOCAL_READERS = "SCard$LocalReaders\0\0";
public const string GROUP_SYSTEM_READERS = "SCard$SystemReaders\0\0";
public const uint STATE_UNAWARE = 0x00000000;
public const uint STATE_IGNORE = 0x00000001;
public const uint STATE_CHANGED = 0x00000002;
public const uint STATE_UNKNOWN = 0x00000004;
public const uint STATE_UNAVAILABLE = 0x00000008;
public const uint STATE_EMPTY = 0x00000010;
public const uint STATE_PRESENT = 0x00000020;
public const uint STATE_ATRMATCH = 0x00000040;
public const uint STATE_EXCLUSIVE = 0x00000080;
public const uint STATE_INUSE = 0x00000100;
public const uint STATE_MUTE = 0x00000200;
public const uint STATE_UNPOWERED = 0x00000400;
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
public struct ReaderState
{
public ReaderState(string sName)
{
szReader = sName;
pvUserData = IntPtr.Zero;
dwCurrentState = 0;
dwEventState = 0;
cbATR = 0;
rgbATR = null;
}
internal string szReader;
internal IntPtr pvUserData;
internal uint dwCurrentState;
internal uint dwEventState;
internal uint cbATR; // count of bytes in rgbATR
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 0x24, ArraySubType = UnmanagedType.U1)]
internal byte[] rgbATR;
}
public static string ToHex(byte[] ab, string sDelim)
{
if (ab == null) return "<NULL>";
return ToHex(ab, 0, ab.Length, sDelim);
}
public static string ToHex(byte[] ab, int offset, int len, string sDelim)
{
if (ab == null) return "<NULL>";
StringBuilder sb = new StringBuilder();
len = Math.Min(offset + len, ab.Length);
for (int i = offset; i < len; i++)
sb.Append(String.Format("{0:x02}", ab[i]).ToUpper() + sDelim);
return sb.ToString();
}
}
}
UID может быть прочитал с помощью вызова функции совместимости PC/SC 2.01 против winscard.dll
Анкет Для карт ICLASS это обычно 8 байтов длиной. Другие карты возвращают UID от 4 до 8 байтов (он действует как номер телефона, чтобы идентифицировать несколько карт в поле и в конечном итоге выбрать одну). APDU можно обменять по стандарту SCardTransmit()
с CLA=0xFF
Чтобы указать доступ к бесконтактной карте хранения.
Марк
Примечание. UID не отражает номер карты, напечатанную на карте. Номер карты является частью данных Wiegand, хранящихся в приложении Hid PAC приложения 1 на странице 0 этой карты.
- Уникальный идентификатор (UID): все ISO-совместимые смарт-карты предоставляются с номером UID (сродни номеру VIN на транспортном средстве). Источник: http://www.accentalarms.com/specsheets/xeedceed/xeedidsmartcardoverview.pdf
- О чтении UID: проверьте http://www.hidglobal.com/faqs.php?techcat=19, кажется, показывает, как это сделать.
Даже если номер UID является частью стандарта, и стандарт говорит, что он должен быть уникальным во всем мире, вы должны помнить, что часто бывают производители, производящие несоответствующие (но работающие) карты.
Вывод: до тех пор, пока вы не ставите что -то уникальное на карту, вы не можете быть уверены, что ссылка от карты на владелец карты удовлетворена.
ATR (Аnswer То рESET) из карты только указывает, что такое формат карты, протокол и контрольные суммы этих данных. Видеть ATR SAINGING ONLINE
Теперь вы знаете, какую карту вы должны применить соответствующее (RS232/RS485/ZPL II/APDU - Смарт -карта АPpplication протокол Дюймовыйата UNIT, как определено в командах ISO/IEC 7816-4) для получения UID (в зависимости от того, является ли он каскадом 1, 2 или 3, см. ACG HF Multi ISO RFID Reader v1.0 Page 50 AKA Omnikey5553 RS232) - для 1443 До 14 байтов длиной, занимая первые два блока в первом 16 -байтовом секторе (сектор 0), и имеет встроенные контрольные суммы. (См. Протоколы ISO14443 или NXP Semiconductors MF1ICS50.
В 15693 году команды RS232/RS485 вернут полного 16 -байтового UID на (S) избранных (Omnikey55553 RS232 и USB), но на APDU вернет только последние 8 байтов (0xff 0x. 0x00 0x00 0x00 на omnikey5022), несмотря на то, что он стандарт. Что касается 15693 года, карта отвечает только 8 байтами за раз. В течение 14443 вы можете читать сектор за раз (32 байта из 4 блоков из 8 байтов, входящих в сектор перед чтением/записи для S50), но в течение 15693 вы можете читать/написать блок из 8 байтов за раз и имеет другие данные в возвращенном буфере. Вы должны кодировать, блокируете ли вы данные на 32 или в 8. Это использует стандартный протокол смарт -карт ScardTransmit для Windows API. Поскольку диагностическое приложение вернулось на 16 байтов, то есть UID карты. Кроме того, некоторые уместность 15693 используют 14443 инвертированный ключ для запугивания и предотвращения модификации, другие имеют микропрограмму, которая выполняет проверку - либо на ПК, либо на самой карте.
Во всех случаях это не имеет отношения к тому, что напечатано на карте - мы используем штрих -коды Code128C, напечатанные на пластиковых картах EV1, которые содержат GUID, который затем относится к соответствующему UID в базе данных для транзакций. Другие (например, Wiegand и т. Д.) Печать другие данные, такие как коды области, наборы ключей и т. Д.
Дополнительные проблемы возникают, когда вы пытаетесь записать данные в 15693 - убедитесь, что вы заканчиваете свои данные на интерфейсе 4 char, иначе у вас есть проблемы с предыдущими данными, когда вы пытаетесь написать нулевой '0x00' над существующим символом в блоке - так что Код для пояса, скобок и кусок строки после обновления/записи на карту, чтобы убедиться, что данные по мере необходимости. Если весь блок имеет Null's, без проблем, так как данные написаны как блок из 4 символов. Не пускайте область пользователя карты без необходимости, так как они основаны на NAND и имеют конечные записи. (После записи снова читайте карту, чтобы убедиться, что письменные данные были написаны!)
UID должен быть уникальным в рамках набора выпуска карты, но также зависит от соответствующего порядка количества карт, поэтому серийный номер был расширен 2 раза (каскадный номер), и для этого вы должны смягчить свое доверие к UID от Телефон Apple, когда он нарушает U в UID - он может быть настроен на эмуляцию другого UID.
NDEF - это еще один аспект карт, который следует понимать - это хорошо объяснено в стандартах NFC NDEF, просто будьте осторожны с участками OTP и блокировки 14443, так как они находятся в одном месте только один раз.
Вы всегда можете использовать телефон Android с NFC и приложением Tagdata для проверки.