2010-12-21 18 views
8

Estoy tratando de leer información de una tarjeta inteligente, utilizando un lector de la tarjeta OmniKey 5321 sin contacto.Tarjeta sin contacto a través de una OmniKey, ¿cómo obtener "UID"?

Editar: Se ha añadido una recompensa.

Estoy escribiendo un C# 3 en el programa .NET 3.5, por lo que esta es una aplicación de Windows.

la tarjeta tiene alguna información estampada en ella, que iba a suponer, de alguna manera, está presente en los datos de la tarjeta (hay una foto de la tarjeta y el lector a continuación.)

La tarjeta tiene la siguiente información estampada en él:

 
1* 00447 21091328-32 

al utilizar la aplicación de diagnóstico que viene con el lector de tarjetas OmniKey, me da la siguiente información:

 
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) 

n w, aquí es algunas conversiones He examinado:

  • 447 decimal = hexadecimal 1BF (no encontrado)
  • 447 octal = 295 decimal (no encontrado como de tipo codificación BCD)
  • 447 octal = 127 hexadecimal (no encontrado)
  • 447 hexadecimal no se encuentra

Aquí están mis preguntas:

  • ¿El número "UID" es un número único en el que puedo confiar? Realmente no me importa el número 447, todo lo que necesito saber es que la información que elijo de esta tarjeta la identificará de manera única más adelante, para que pueda vincularla con el propietario de la tarjeta
  • ¿Cómo podría ir? acerca de leer el número UID? Usando WINSCARD.DLL en Windows, puedo ver que obtengo los datos "ATR", cada byte, pero el UID aparentemente no está presente en esa parte.

Aquí está la foto, si eso le da alguna información.

OmniKey reader back with card

+0

¿ha encontrado la manera de obtener la identificación de la tarjeta? Tengo el mismo problema, el lector devolvió el número de serie Hex, que no tiene nada que ver con el número de tarjeta escrito. Gracias :) –

+0

@Abfam Lo sentimos, no encontramos una manera de encontrar ese número en particular estampado en la tarjeta, y un sombrero para usar los datos realmente recuperados de la tarjeta. Esto significaba agregar otro deslizamiento de la tarjeta durante el registro, para leer los datos en él, en lugar de simplemente escribir un número como esperábamos. –

+0

El ATR está hecho por PCSC y es un formato "estándar" para ATR sin contacto de un PICC/VICC con lógica por cable. Esto se determina a partir de los bytes ATR iniciales: 3B 8F 80 01 (TS, T0, TD1-2). El significado de la mayor parte del paquete es "fijo"; los únicos campos de "datos" están en el desplazamiento de bytes 12 (PIX.SS) y 13-4 (NOMBRE PIX) que no ayudan a obtener UID o cualquiera de los otros campos que desee. –

Respuesta

2

Puede confiar en UID pero parece truncado en su caso:

UID:EE 74 0E 00 FB FF 12 E0 

Uid son por lo general de 16 bytes de longitud.

Puede leer este Identificador único (UID): Todas las tarjetas inteligentes que cumplen con la norma ISO cuentan con un número UID (similar a un número VIN en un vehículo). Para propósitos de interoperabilidad , el UID de una tarjeta está abierto y disponible para ser leído por todos los lectores compatibles. Como este número único no está protegido por claves, leer el UID de una tarjeta inteligente es comparable a leer una tarjeta de proximidad, una tarjeta de banda magnética u otra tecnología que utiliza números abiertos no asegurados.

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

0

Incluso si el número de UID es parte del estándar y el estándar dice que debe ser único en todo el mundo, debe tener en cuenta que a menudo hay fabricantes que producen tarjetas que no cumplen (pero funcionan).

Conclusión: siempre que no ponga algo único en la tarjeta, no puede estar seguro de que el enlace de la tarjeta al propietario de la tarjeta esté satisfecho.

4

UID se puede leer a través de PC/SC 2.01 llamada de función compatible contra winscard.dll. Para tarjetas iCLASS, generalmente tiene 8 bytes de longitud. Otras tarjetas devuelven un UID de 4 a 8 bytes (actúa como un número de teléfono para identificar múltiples tarjetas en el campo y finalmente selecciona una). La APDU se puede cambiar a través del estándar SCardTransmit() con CLA=0xFF para indicar el acceso a la tarjeta de almacenamiento sin contacto.

Marc

http://www.smartcard-api.com

NOTA: El UID no refleja el número de la tarjeta impresa en la tarjeta. El número de tarjeta es parte de los datos de Wiegand almacenados en la aplicación HID PAC de la aplicación 1 en la página 0 de esa tarjeta.

12

poco me pasan muchas horas en busca de una completa ejemplo de cómo sacar el ATR de tarjeta de proximidad y OMNIKEY ...

Ahora que tengo mi código de trabajo, me gustaría compartir lo demás puede beneficiar.

El mejor código que encontré fue el de SpringCard. (Gracias!)

Otros ejemplos que encontré eran una pérdida de tiempo y engañosa, y no funciona porque el DllImport estaba mal ...

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

    } 
} 
+0

Gracias por el código, y para cualquier otra persona que mire esto, aquí hay un buen comienzo para usar el código: var x = new ReaderList(); x.CardPresented + = x_CardPresented; x.Refrescar(); – StormRider01

+0

@ StormRider01, parece que el código no se ejecuta, ¿podría extender su explicación? –

+0

@ chris, ¿podría proporcionarnos una muestra del código? –

0

El ATR (Un espuesta T o R eset) de una tarjeta solo indica cuál es el formato de la tarjeta, el protocolo y las sumas de comprobación de esos datos. Ver ATR Parsing online

Ahora usted sabe lo que la tarjeta es usted entonces tiene que aplicar el apropiado (RS232/RS485/ZPL II/APDU - Tarjeta inteligente Un plicación P rotocolo D ata T nit como se define en ISO/IEC 7816-4) comandos para obtener el UID (dependiendo de si se trata de cascada 1, 2 o 3, consulte Lector de RFID multi ISO ACG v1.0 página 50 también conocido como Omnikey5553 RS232) - para 14443 es hasta 14 bytes de longitud, ocupando los primeros dos bloques en el primer sector de 16 bytes (sector 0), y tiene sumas de comprobación incrustadas. (Consulte los protocolos ISO14443 o las especificaciones de NXP Semiconductors MF1ICS50 página 8).

Para 15693, los comandos RS232/RS485 devolverán un UID completo de 16 bytes en (S) electo (Omnikey5553 RS232 y USB) pero en APDU solo devolverán los últimos 8 bytes (0xFF 0xCA 0x00 0x00 0x00 en un Omnikey5022) a pesar de ser el estándar, en cuanto a 15693, la tarjeta solo responde con 8 bytes a la vez. Para 14443 puede leer un sector a la vez (32 bytes de 4 bloques de 8 bytes, iniciando sesión en el sector antes de leer/escribir para el S50) pero para 15693 solo puede leer/escribir un bloque de 8 bytes a la vez y tiene otros datos en el búfer devuelto. Tiene que codificar si 'bloquea' los datos en 32 o en 8. Esto está utilizando el protocolo estándar de tarjeta inteligente SCardTransmit para la API de Windows. Como la aplicación de diagnóstico ha devuelto 16 bytes, ese es el UID de la tarjeta. Además, algunas propiedades 15693 utilizan la clave invertida 14443 para ofuscar y evitar modificaciones, otras tienen un microprograma que realiza la validación, ya sea en la PC o en la propia tarjeta.

En todos los casos, esto no guarda relación con lo que está impreso en la tarjeta: usamos códigos de barras CODE128C impresos en tarjetas de plástico EV1 que contienen un GUID que hace referencia al UID correspondiente en una base de datos para transacciones. Otros (como Wiegand, etc.) imprimen otros datos como códigos de área, juegos de llaves, etc.

Ocurren problemas adicionales cuando intenta escribir datos en el 15693 - asegúrese de finalizar sus datos en la interfaz de 4 caracteres; de lo contrario, tendrá problemas con datos anteriores cuando intenta escribir un nulo '0x00' sobre un carácter existente en un bloque, así que codifique el cinturón, llaves y un trozo de cuerda después de actualizar/escribir en una tarjeta para asegurarse de que los datos sean los necesarios. Si todo el bloque es nulo, no hay problema, ya que los datos se escriben como un bloque de 4 caracteres. NO limpie innecesariamente el área de usuario de la tarjeta, ya que están basados ​​en NAND y tienen escrituras finitas. (¡Después de escribir, vuelva a leer la tarjeta para asegurarse de que los datos escritos estén como están escritos!)

El UID debe ser único dentro del conjunto de emisión de la tarjeta, pero también depende de la cantidad de la tarjeta en cuestión, por lo que el número de serie se ha expandido 2 veces (el número de cascada) y, por una vez, debes moderar tu confianza en el UID desde un teléfono Apple, ya que infringe la U en el UID: se puede configurar para emular otro UID.

NDEF es otro aspecto de las tarjetas que debe entenderse: está bien explicado en los estándares NFC NDEF, solo tenga cuidado con las áreas OTP y LOCK de 14443, ya que solo son de una vía una vez configuradas.

Siempre puede usar un teléfono Android con NFC y la aplicación TagData para verificar.

Cuestiones relacionadas