【问题标题】:Contact-less card through an OmniKey, how to get "UID"?通过OmniKey的非接触式卡,如何获得“UID”?
【发布时间】:2011-05-28 17:08:50
【问题描述】:

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

编辑:添加了赏金。

我正在用 .NET 3.5 程序编写 C# 3,所以这是一个 Windows 应用程序。

卡上印有一些信息,我认为这些信息以某种方式存在于卡数据中(下面有一张卡和读卡器的照片。)

卡上印有以下信息:

1* 00447 21091328-32

使用 OmniKey 读卡器附带的诊断应用程序时,我收到以下信息:

智能卡名称: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 协议:ISO 15693(第 2 部分)

现在,我考虑了一些转换:

  • 447 十进制 = 1BF 十六进制(未找到)
  • 447 八进制 = 295 十进制(未找到 BCD 类型编码)
  • 447 八进制 = 127 十六进制(未找到)
  • 447 十六进制未找到

这是我的问题:

  • “UID”编号是我可以依赖的唯一编号吗? 447这个号码我倒不是很在意,我只需要知道我从这张卡中挑选的信息以后会唯一标识它,这样我就可以将它链接到卡的所有者
  • 如何读取 UID 号?在 Windows 中使用 WINSCARD.DLL 我可以看到我得到了“ATR”数据,每个字节,但 UID 显然不存在于该部分中。

这是照片,如果它可以为您提供任何信息。

【问题讨论】:

  • 你找到卡ID的方法了吗?我遇到了同样的问题,读卡器返回了十六进制序列号,这与写入的卡号无关。谢谢:)
  • @Abfam 抱歉,我们没有找到方法找到印在卡上的特定数字,并且无法使用从卡上实际检索到的数据。这意味着在注册过程中再次刷卡以读取其中的数据,而不是像我们希望的那样只输入数字。
  • ATR 由 PCSC 制造,是有线逻辑 PICC/VICC 的非接触式 ATR 的“标准”格式。这由初始 ATR 字节确定:3B 8F 80 01 (TS,T0,TD1-2)。大部分数据包的含义是“固定”;唯一的“数据”字段位于字节偏移量 12 (PIX.SS) 和 13-4 (PIX.NAME) 处,它们无助于获取 UID 或您想要的任何其他字段。
  • 来自您的 ATR 数据包,字节 12-14:PIX.SS 0A = ISO 15693,第 2 部分 PIX.NN 00 1A = PicoPass 16Ks
  • 对于它的价值(而且我知道我参加这个聚会真的很晚了),示例代码 here 让我开始这样做。

标签: smartcard uniqueidentifier smartcard-reader winscard iso-15693


【解决方案1】:

我最近花了太多时间寻找一个完整的示例,说明如何从 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();
        }

    }
}

【讨论】:

  • 感谢您的代码,以及其他看到此代码的人,这是使用代码的一个快速开始: var x = new ReaderList(); x.CardPresented += x_CardPresented; x.Refresh();
  • @StormRider01,代码似乎没有运行,能否请您扩展一下解释?
【解决方案2】:

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

马克

http://www.smartcard-api.com

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

【讨论】:

    【解决方案3】:

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

    【讨论】:

      【解决方案4】:
      1. 唯一标识符 (UID):所有符合 ISO 标准 智能卡配有 UID 号 (类似于车辆上的 VIN 号)。来源:http://www.accentalarms.com/specsheets/xceed/XceedIDSmartcardOverview.pdf
      2. 关于读取UID:查看http://www.hidglobal.com/faqs.php?techCat=19,好像说明了怎么做。

      【讨论】:

        【解决方案5】:

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

        结论:只要你没有在卡上放一些独特的东西,你就不能确定从卡到卡所有者的链接是否满足。

        【讨论】:

          【解决方案6】:

          卡的 ATR (Answer To Reset) 仅说明卡的格式、协议和校验和该数据。见ATR Parsing online

          现在您知道它是什么卡,然后您必须应用适当的(RS232/RS485/ZPL II/APDU - 智能卡A应用Protocol Data 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 反转密钥来混淆和防止修改,另一些持有一个执行验证的微程序 - 无论是在 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 应用进行验证。

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2016-11-14
            • 1970-01-01
            相关资源
            最近更新 更多