Frage

Ich versuche, Informationen von einer Smartcard mit einer kontaktlosen OmniKey 5321-Karte zu lesen Leser.

Bearbeiten:Ein Kopfgeld hinzugefügt.

Ich schreibe ein C# 3 in .NET 3.5-Programm, es handelt sich also um eine Windows-Anwendung.

Auf der Karte sind einige Informationen eingeprägt, von denen ich annehmen würde, dass sie in irgendeiner Weise in den Kartendaten enthalten sind (unten finden Sie ein Foto der Karte und des Lesegeräts).

Auf der Karte sind folgende Informationen eingeprägt:

1* 00447   21091328-32

Wenn ich die Diagnoseanwendung verwende, die mit dem OmniKey-Kartenleser geliefert wird, erhalte ich die folgenden Informationen:

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)

Hier sind einige Konvertierungen, die ich in Betracht gezogen habe:

  • 447 dezimal = 1BF hexadezimal (nicht gefunden)
  • 447 Oktal = 295 Dezimal (nicht als BCD-Kodierung gefunden)
  • 447 Oktal = 127 Hexadezimal (nicht gefunden)
  • 447 Hexadezimal wurde nicht gefunden

Hier sind meine Fragen:

  • Ist die „UID“-Nummer eine eindeutige Nummer, auf die ich mich verlassen kann?Die 447-Nummer ist mir eigentlich egal, ich muss nur wissen, dass die Informationen, die ich von dieser Karte auswähle, sie später eindeutig identifizieren, sodass ich sie dem Besitzer der Karte zuordnen kann
  • Wie würde ich beim Auslesen der UID-Nummer vorgehen?Wenn ich WINSCARD.DLL in Windows verwende, kann ich sehen, dass ich die „ATR“-Daten für jedes einzelne Byte erhalte, aber die UID ist in diesem Teil offenbar nicht vorhanden.

Hier ist das Foto, falls Ihnen das Aufschluss gibt.

OmniKey reader back with card

War es hilfreich?

Lösung

Sie können sich auf UID verlassen, aber es scheint in Ihrem Fall abgeschnitten zu sein:

UID:EE 74 0E 00 FB FF 12 E0

UIDs sind normalerweise 16 Byte lang.

Das können Sie nachlesen Eindeutige Kennung (UID):Alle ISO-konformen Smartcards sind mit einer UID-Nummer versehen (ähnlich der VIN-Nummer eines Fahrzeugs).Für Aus Gründen der Interoperabilität ist die UID einer Karte offen und kann von allen konformen Lesegeräten gelesen werden.Da diese eindeutige Nummer nicht durch Schlüssel gesichert ist, ist das Lesen der UID einer Smartcard vergleichbar mit dem Lesen einer Proximity-Karte, einer Magnetstreifenkarte oder einer anderen Technologie, die offene, ungesicherte Nummern verwendet.

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

Andere Tipps

Ich habe in letzter Zeit zu viele Stunden damit verbracht, nach einem zu suchen vollständig Beispiel dafür, wie man die ATR von einer OMNIKEY Proximity-Karte erhält ...

Nachdem mein Code nun funktioniert, möchte ich ihn gerne teilen, damit andere davon profitieren können.

Der beste Code, den ich gefunden habe, war von SpringCard.(Danke!)

Andere Beispiele, die ich fand, waren zeitraubend und irreführend und funktionierten nicht, weil der DllImport falsch war ...

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

    }
}

Die UID kann über einen PC/SC 2.01-kompatiblen Funktionsaufruf ausgelesen werden winscard.dll.Bei iCLASS-Karten ist es typischerweise 8 Byte lang.Andere Karten geben eine 4 bis 8 Byte große UID zurück (sie fungiert wie eine Telefonnummer, um mehrere Karten im Feld zu identifizieren und letztendlich eine auszuwählen).Die APDU kann per Standard ausgetauscht werden SCardTransmit() mit CLA=0xFF um den Zugriff auf die kontaktlose Speicherkarte anzuzeigen.

Marc

http://www.smartcard-api.com

NOTIZ:Die UID spiegelt NICHT die auf der Karte aufgedruckte Kartennummer wider.Die Kartennummer ist Teil der Wiegand-Daten, die in der HID PAC-Anwendung von Anwendung 1 auf Seite 0 dieser Karte gespeichert sind.

  1. Eindeutiger Identifikator (UID):Alles ISO-konform Smartcards sind mit einer UID-Nummer versehen (vergleichbar mit einer Fahrgestellnummer auf einem Fahrzeug).Quelle: http://www.accentalarms.com/specsheets/xceed/XceedIDSmartcardOverview.pdf
  2. Informationen zum Lesen der UID:überprüfen http://www.hidglobal.com/faqs.php?techCat=19, es scheint zu zeigen, wie es geht.

Auch wenn die UID-Nummer Teil des Standards ist und laut Standard weltweit eindeutig sein soll, muss man bedenken, dass es häufig Hersteller gibt, die nicht konforme (aber funktionierende) Karten herstellen.

Abschluss:Solange Sie nichts Einzigartiges auf die Karte setzen, können Sie nicht sicher sein, dass die Verbindung zwischen der Karte und dem Karteninhaber besteht.

Die ATR (AAntwort TÖ Reset) von einer Karte gibt nur das Kartenformat, das Protokoll und die Prüfsummen dieser Daten an.Sehen ATR-Parsing online

Nachdem Sie nun wissen, um welche Karte es sich handelt, müssen Sie die entsprechende (RS232/RS485/ZPL II/APDU – Smartcard) anwenden AAnwendung Protocol Dan einer Unit wie in ISO/IEC 7816-4 definiert) Befehle zum Abrufen der UID (je nachdem, ob es sich um Kaskade 1, 2 oder 3 handelt, siehe ACG HF Multi ISO RFID Reader v1.0 Seite 50, auch bekannt als Omnikey5553 RS232) – für 14443 ist es das Bis zu 14 Byte lang, belegt die ersten beiden Blöcke im ersten 16-Byte-Sektor (Sektor 0) und verfügt über eingebettete Prüfsummen.(Siehe die ISO14443-Protokolle oder die MF1ICS50-Spezifikationen von NXP Semiconductors, Seite 8).

Für 15693 geben die RS232/RS485-Befehle eine vollständige 16-Byte-UID auf (S)elect (Omnikey5553 RS232 und USB) zurück, aber auf APDU werden nur die letzten 8 Bytes zurückgegeben (0xFF 0xCA 0x00 0x00 0x00 auf einem Omnikey5022), obwohl dies der Standard ist , da die Karte beim 15693 jeweils nur mit 8 Bytes antwortet.Für 14443 können Sie jeweils einen Sektor lesen (32 Bytes in 4 Blöcken mit jeweils 8 Bytes, Anmeldung im Sektor vor dem Lesen/Schreiben für den S50), aber für 15693 können Sie jeweils nur einen Block mit 8 Bytes lesen/schreiben und hat andere Daten im zurückgegebenen Puffer.Sie müssen codieren, ob Sie die Daten bei 32 oder bei 8 „blockieren“.Dabei wird das Standard-Smartcard-Protokoll SCardTransmit für die Windows-API verwendet.Da die Diagnoseanwendung 16 Bytes zurückgegeben hat, handelt es sich um die UID der Karte.Darüber hinaus verwenden einige proprietäre 15693 den invertierten Schlüssel 14443, um Änderungen zu verschleiern und zu verhindern, andere verfügen über ein Mikroprogramm, das die Validierung durchführt – entweder auf dem PC oder auf der Karte selbst.

In allen Fällen hat dies keinen Bezug zu dem, was auf der Karte aufgedruckt ist – wir verwenden CODE128C-Barcodes, die auf EV1-Plastikkarten gedruckt sind und eine GUID enthalten, die dann auf die entsprechende UID in einer Datenbank für Transaktionen verweist.Andere (wie Wiegand usw.) drucken andere Daten wie Ortsvorwahlen, Schlüsselsätze usw.

Zusätzliche Probleme treten auf, wenn Sie versuchen, Daten in 15693 zu schreiben. Stellen Sie sicher, dass Sie Ihre Daten an der 4-Zeichen-Schnittstelle beenden. Andernfalls treten Probleme mit vorherigen Daten auf, wenn Sie versuchen, einen Nullwert „0x00“ über ein vorhandenes Zeichen in einem Block zu schreiben Code für Gürtel, Hosenträger und ein Stück Schnur nach dem Aktualisieren/Schreiben auf eine Karte, um sicherzustellen, dass die Daten den Anforderungen entsprechen.Wenn der gesamte Block aus Nullen besteht, ist das kein Problem, da die Daten als Block mit 4 Zeichen geschrieben werden.Leeren Sie den Benutzerbereich der Karte NICHT unnötig, da sie NAND-basiert sind und begrenzte Schreibzugriffe haben.(Lesen Sie die Karte nach dem Schreiben erneut, um sicherzustellen, dass die geschriebenen Daten so sind, wie sie geschrieben wurden!)

Die UID sollte innerhalb des Kartenausgabesatzes eindeutig sein, hängt aber auch von der jeweiligen Kartenmengenbestellung ab – weshalb die Seriennummer um das 2-fache erweitert wurde (die Kaskadennummer) und Sie sollten ausnahmsweise einmal Ihr Vertrauen in die UID mäßigen B. ein Apple-Telefon, da es gegen das U in der UID verstößt – es kann so eingestellt werden, dass es eine andere UID emuliert.

NDEF ist ein weiterer Aspekt von Karten, der verstanden werden sollte – er wird in den NFC-NDEF-Standards gut erklärt. Seien Sie jedoch vorsichtig mit den OTP- und LOCK-Bereichen von 14443, da diese nur einmal festgelegt unidirektional sind.

Sie können zur Überprüfung jederzeit ein Android-Telefon mit NFC und die TagData-App verwenden.

Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top