通过 OmniKey 非接触式卡,如何获取“UID”?

发布于 2024-10-08 06:40:08 字数 940 浏览 0 评论 0原文

我正在尝试使用非接触式 OmniKey 5321 卡从智能卡读取信息 读者。

编辑:添加了赏金。

我正在 .NET 3.5 程序中编写 C# 3,所以这是一个 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号码?在 Windows 中使用 WINSCARD.DLL,我可以看到我得到了“ATR”数据,每个字节,但 UID 显然不存在于该部分中。

这是照片,如果它能给你任何信息的话。

OmniKey 读卡器背面带卡

I am trying to read information off of a smartcard, using a contact-less OmniKey 5321 card
reader.

Edit: Added a bounty.

I'm writing a C# 3 in .NET 3.5 program, so this is a Windows application.

The card has some information stamped onto it, that I would assume, in some way, is present in the card data (there is a photo of the card and reader below.)

The card has the following information stamped onto it:

1* 00447   21091328-32

When using the Diagnostics application that comes with the OmniKey card reader, I get the following information:

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)

Now, here's some conversions I've considered:

  • 447 decimal = 1BF hexadecimal (not found)
  • 447 octal = 295 decimal (not found as BCD-type encoding)
  • 447 octal = 127 hexadecimal (not found)
  • 447 hexadecimal is not found

Here's my questions:

  • Is the "UID" number a unique number that I can rely on? I don't really care about the 447 number, all I need to know is that the information I pick from this card will uniquely identify it later, so that I can link it to the owner of the card
  • How would I go about reading the UID number? Using WINSCARD.DLL in Windows I can see that I get the "ATR" data, every single byte, but the UID is apparently not present in that part.

Here's the photo, if that gives you any information.

OmniKey reader back with card

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(7

浅沫记忆 2024-10-15 06:40:08

我最近花了太多时间寻找如何从 OMNIKEY 感应卡获取 ATR 的完整示例...

现在我的代码可以运行了,我想分享一下,以便其他人可以受益。

我发现的最好的代码来自 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();
        }

    }
}

I recently spend too many hours searching for an complete example of how to get the ATR from and OMNIKEY proximity card...

Now that I have my code working, I would like to share so others can benefit.

The best code that I found was from SpringCard. (thanks!)

Other examples I found were time-wasting and misleading, and did not work because the DllImport was wrong ...

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();
        }

    }
}
柠檬 2024-10-15 06:40:08

UID 可以通过针对 winscard.dll 的 PC/SC 2.01 兼容函数调用来读取。
对于 iCLASS 卡,其长度通常为 8 字节。其他卡返回 4 到 8 字节的 UID(它的作用类似于电话号码,用于识别现场中的多张卡并最终选择一张)。
APDU 可以通过标准 SCardTransmit()CLA=0xFF 进行交换,以指示对非接触式存储卡的访问。

马克

http://www.smartcard-api.com

注意:
UID 并不反映卡上打印的卡号。卡号是存储在该卡第 0 页上应用程序 1 的 HID PAC 应用程序中的 Wiegand 数据的一部分。

UID can be read via PC/SC 2.01 compliant function call against winscard.dll.
For iCLASS cards it is typically 8 bytes long. Other cards return a 4 to 8 byte UID (it acts like a telephone number to identify multiple cards in the field and ultimately select one).
The APDU can be exchanged via standard SCardTransmit() with CLA=0xFF to indicate access to the contactless storage card.

Marc

http://www.smartcard-api.com

NOTE:
The UID does NOT reflect the card number printed on the card. The card number is part of the Wiegand data stored in the HID PAC application of application 1 on page 0 of that card.

海未深 2024-10-15 06:40:08

您可以依赖 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

You can rely on UID but it seems truncated in your case :

UID:EE 74 0E 00 FB FF 12 E0

Uid are usually 16 bytes long.

You can read this
Unique Identifier (UID): All ISO-compliant smart cards are provided with a UID number (akin to a VIN number on a vehicle). For
interoperability purposes, a card’s UID is open and available to be read by all compliant readers. Since this unique number is not secured by keys, reading the UID of a smart card is comparable to reading a proximity card, mag stripe card or other technology that utilizes open, unsecured numbers.

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

风追烟花雨 2024-10-15 06:40:08
  1. 唯一标识符 (UID):全部符合 ISO 标准
    智能卡带有 UID 号
    (类似于车辆上的 VIN 号码)。资料来源:http://www.accentalarms.com/specsheets/xceed/XceedIDSmartcardOverview.pdf
  2. 关于读取UID:查看http://www.hidglobal.com/faqs.php? techCat=19,它似乎展示了如何做到这一点。
  1. Unique Identifier (UID): All ISO-compliant
    smart cards are provided with a UID number
    (akin to a VIN number on a vehicle). Source: http://www.accentalarms.com/specsheets/xceed/XceedIDSmartcardOverview.pdf
  2. About reading UID: check http://www.hidglobal.com/faqs.php?techCat=19, it seems to show how to do it.
祁梦 2024-10-15 06:40:08

即使 UID 号是标准的一部分,并且标准规定它应该在全球范围内是唯一的,您也必须记住,经常有制造商生产不合规(但有效)的卡。

结论:只要您不在卡上添加独特的内容,您就无法确定从卡到卡所有者的链接是否令人满意。

Even if the UID number is part of the standard and the standard says it should be unique worldwide you have to keep in mind that there are often manufacturers producing non-compliant (but working) cards.

Conclusion: As long you don't put something unique on the card you can not be sure that the link from card to a card owner is satisfied.

つ可否回来 2024-10-15 06:40:08

卡的 ATR(Answer To Reset)仅说明卡格式是什么、协议以及该数据的校验和。请参阅在线 ATR 解析

现在您知道它是什么卡,然后必须应用适当的(RS232/ RS485/ZPL II/APDU - 智能卡A应用程序P协议数据数据Unit,如 ISO/ 中定义IEC 7816-4) 用于获取 UID 的命令(取决于级联 1、2 还是 3,请参阅 ACG HF Multi ISO RFID 读取器 v1.0 第 50 页,又名 Omnikey5553 RS232) - 对于 14443,其长度最多为 14 个字节,占据第一个 16 字节扇区(扇区 0)的前两个块,并且嵌入了校验和。 (请参阅 ISO14443 协议或 NXP Semiconductors MF1ICS50 规格第 8 页)。

对于 15693,RS232/RS485 命令将在 (S)elect(Omnikey5553 RS232 和 USB)上返回完整的 16 字节 UID,但在 APDU 上将仅返回最后 8 个字节(Omnikey5022 上的 0xFF 0xCA 0x00 0x00 0x00),尽管这是标准,至于15693,卡一次仅响应8个字节。对于 14443,您可以一次读取一个扇区(4 个 8 字节块的 32 个字节,在 S50 读/写之前登录到扇区),但对于 15693,您一次只能读/写 8 个字节的块,并且返回的缓冲区中有其他数据。无论是在 32 还是在 8 处“阻止”数据,您都必须进行编码。这是使用 Windows API 的标准 SCardTransmit 智能卡协议。由于诊断应用程序返回了 16 个字节,即卡的 UID。此外,一些专有 15693 使用 14443 倒置密钥来混淆和防止修改,其他专有 15693 拥有执行验证的微程序 - 无论是在 PC 上还是在卡本身上。

在所有情况下,这与卡上打印的内容没有任何关系 - 我们使用 EV1 塑料卡上打印的 CODE128C 条形码,其中包含 GUID,然后该 GUID 引用数据库上的相应 UID 进行交易。其他(如 Wiegand 等)打印其他数据,例如区号、按键设置等。

当您尝试将数据写入 15693 时,会出现其他问题 - 确保在 4 字符接口处结束数据,否则在以下情况下您会遇到先前数据的问题您尝试在块中的现有字符上写入空“0x00” - 因此在更新/写入卡后编写皮带、大括号和一段字符串的代码,以确保数据符合需要。如果整个块都是“null”,没问题,因为数据是作为 4 个字符的块写入的。不必要时请勿清空卡用户区域,因为它们基于 NAND 并且写入有限。 (写入后再次读卡,以确保写入的数据与写入的一样!)

UID 在卡发行集中应该是唯一的,但这也取决于所涉及的卡数量订单 - 这就是为什么序列号已扩大 2 倍(级联数字),这一次您应该降低对 Apple 手机 UID 的信任度,因为它违反了 UID 中的 U - 它可以设置为模拟另一个 UID。

NDEF 是卡的另一个需要理解的方面 - 它在 NFC NDEF 标准中得到了很好的解释,只是要小心 14443 的 OTP 和 LOCK 区域,因为它们只有一次设置后才是单向的。

您始终可以使用具有 NFC 功能的 Android 手机和 TagData 应用程序进行验证。

The ATR (Answer To Reset) from a card only states what the card format is, the protocol, and checksums of that data. See ATR Parsing online

Now you know what card it is you then have to apply the appropriate (RS232/RS485/ZPL II/APDU - Smart card Application Protocol Data Unit as defined in ISO/IEC 7816-4) commands to get the UID (depending on whether it is cascade 1, 2 or 3, see ACG HF Multi ISO RFID Reader v1.0 page 50 aka Omnikey5553 RS232) - for 14443 it is up to 14 bytes long, occupying the first two blocks in the first 16 byte sector (sector 0), and has checksums embedded. (See the ISO14443 protocols or NXP Semiconductors MF1ICS50 specifications page 8).

For 15693, the RS232/RS485 commands will return a full 16 byte UID on (S)elect (Omnikey5553 RS232 and USB) but on APDU will only return the last 8 bytes (0xFF 0xCA 0x00 0x00 0x00 on a Omnikey5022) despite being the standard, as for the 15693 the card only responds with 8 bytes at a time. For 14443 you can read a sector at a time (32 bytes of 4 blocks of 8 bytes, logging into the sector prior to read/write for the S50) but for 15693 you can only read/write a block of 8 bytes at a time, and has other data in the returned buffer. You have to code whether you 'block' the data at 32 or at 8. This is using the standard SCardTransmit smart card protocol for Windows API. As the diagnostic application has returned 16 bytes, that is the UID of the card. In addition, some propriety 15693 use the 14443 inverted key to obfuscate and prevent modification, others hold a micro-program that performs the validation - either on the PC or on the card itself.

In all cases, this bears no relationship to what is printed ON the card - we use CODE128C barcodes printed on EV1 plastic cards that contain a GUID which then refers to the corresponding UID on a database for transactions. Others (like Wiegand etc) print other data such as area codes, key sets etc.

Additional problems occur when you attempt to write data to the 15693 - make sure you end your data at the 4 char interface else you have problems with prior data when you attempt to write a null '0x00' over an existing character in a block - so code for belt, braces and a piece of string after updating/writing to a card to make sure the data is as needed. If the whole block is of 'null's, no problem, as the data is written as a block of 4 characters. DO NOT blank the card user area unnecessarily, as they are NAND based and have finite writes. (After write read the card again to ensure the written data is as written!)

The UID should be unique within the card issue set, but it also depends on the card quantity order concerned - which is why the serial number has been expanded 2 times (the cascade number) and for once you should moderate your trust in the UID from an Apple phone as it violates the U in UID - it can be set to emulate another UID.

NDEF is another aspect of cards that should be understood - it is well explained in NFC NDEF standards, just be careful with the OTP and LOCK areas of 14443, as they are one-way only once set.

You could always use an Android phone with NFC and the TagData app to verify.

画▽骨i 2024-10-15 06:40:08

在花费了太多时间阅读有关智能卡的信息之后,我成功地使用 .NET 的 PC/SC 包装类

After also spending too many hour on reading about smart cards, I've managed to retrieve UID of HID Seos iCLASS card, using PC/SC wrapper classes for .NET

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文