Бесконтактная карта через OmniKey, как получить «UID»? - PullRequest
8 голосов
/ 21 декабря 2010

Я пытаюсь прочитать информацию со смарт-карты, используя бесконтактную карту 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, очевидно, не присутствует в этой части.

Вот фотография, если она дает вам какую-либо информацию.

OmniKey reader back with card

Ответы [ 6 ]

12 голосов
/ 18 февраля 2013

Я недавно потратил слишком много часов на поиск полного примера того, как получить ATR от бесконтактной карты OMNIKEY ...

Теперь, когда мой код работает, я бынравится делиться, чтобы другие могли извлечь выгоду.

Лучший код, который я нашел, был от 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();
        }

    }
}
4 голосов
/ 28 марта 2011

UID может быть считан с помощью вызова функции, совместимого с PC / SC 2.01, против winscard.dll. Для карт iCLASS это обычно 8 байт. Другие карты возвращают UID длиной от 4 до 8 байтов (он действует как телефонный номер, чтобы идентифицировать несколько карт в поле и в конечном итоге выбрать одну) APDU может быть заменен через стандартный SCardTransmit() на CLA=0xFF для указания доступа к бесконтактной карте памяти.

Марк

http://www.smartcard -api.com

Примечание: UID НЕ отражает номер карты, напечатанный на карте. Номер карты является частью данных Wiegand, хранящихся в приложении HID PAC приложения 1 на странице 0 этой карты.

2 голосов
/ 27 декабря 2010

Вы можете положиться на UID, но в вашем случае он выглядит усеченным:

UID:EE 74 0E 00 FB FF 12 E0

Uid обычно имеет длину 16 байтов.

Вы можете прочитать это Уникальный идентификатор (UID). Все совместимые с ISO смарт-карты снабжены номером UID (схожим с номером VIN на автомобиле). За Для обеспечения совместимости UID карты открыт и доступен для чтения всем совместимым читателям. Поскольку этот уникальный номер не защищен ключами, считывание UID смарт-карты сравнимо с чтением бесконтактной карты, карты с магнитной полосой или другой технологии, использующей открытые незащищенные номера.

http://www.xceedid.com/pdf/XC5937_Smart_whitepaper.pdf

0 голосов
/ 02 марта 2018

ATR ( A nswer T o R eset) с карты только указывает, какой формат карты, протокол и контрольные суммы этих данных,См. Разбор ATR онлайн

Теперь вы знаете, какую именно карту вам необходимо применить (RS232 / RS485 / ZPL II / APDU - Смарт-карта A приложениеКоманды P rotocol D ata U nit, как определено в ISO / IEC 7816-4) для получения UID (в зависимости от того, является ли это каскадом 1, 2 или3, см. ACG HF Multi ISO RFID Reader v1.0, стр. 50 (он же Omnikey5553 RS232) - для 14443 он имеет длину до 14 байт, занимая первые два блока в первом 16-байтовом секторе (сектор 0), и имеет встроенные контрольные суммы.(См. Протоколы ISO14443 или спецификации NXP Semiconductors MF1ICS50, стр. 8).

Для 15693 команды RS232 / RS485 вернут полный 16-байтовый UID при (S) elect (Omnikey5553 RS232 и USB), но при APDU будетвернуть только последние 8 байтов (0xFF 0xCA 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 символа, иначе вывозникают проблемы с предыдущими данными, когда вы пытаетесь записать нулевой «0x00» поверх существующего символа в блоке, поэтому после обновления / записи на карту используйте код пояса, фигурных скобок и фрагмента строки, чтобы убедиться, что данные необходимы.Если весь блок имеет нулевое значение, нет проблем, поскольку данные записываются как блок из 4 символов.НЕ очищайте область пользователя карты без необходимости, так как они основаны на NAND и имеют конечные записи.(После записи прочитайте карту еще раз, чтобы убедиться, что записанные данные совпадают с записанными!)

UID должен быть уникальным в пределах набора выпуска карты, но это также зависит от соответствующего порядка количества карт - поэтому серийный номерномер был увеличен в 2 раза (каскадный номер), и на этот раз вам следует уменьшить доверие к UID с телефона Apple, поскольку он нарушает U в UID - его можно настроить для эмуляции другого UID.

NDEFЭто еще один аспект карт, который следует понимать - он хорошо объяснен в стандартах NFC NDEF, просто будьте осторожны с областями OTP и LOCK 14443, так как они односторонние, только однажды настроенные.

Вы всегдадля проверки используйте телефон Android с NFC и приложение TagData.

0 голосов
/ 28 декабря 2010

Даже если номер UID является частью стандарта и стандарт гласит, что он должен быть уникальным во всем мире, следует иметь в виду, что часто существуют производители, производящие несовместимые (но работающие) карты.

Заключение: Пока вы не помещаете на карту что-то уникальное, вы не можете быть уверены, что ссылка с карты на владельца карты удовлетворена.

0 голосов
/ 28 декабря 2010
  1. Уникальный идентификатор (UID): полностью соответствует ISO смарт-карты имеют номер UID (сродни номеру VIN на транспортном средстве). Источник: http://www.accentalarms.com/specsheets/xceed/XceedIDSmartcardOverview.pdf
  2. О чтении UID: отметьте http://www.hidglobal.com/faqs.php?techCat=19,, похоже, оно показывает, как это сделать.
...