none
Pilotage d'un lecteur ACR122 NFC via "winscard.dll" RRS feed

  • Question

  • Bonjour amis développeurs,

    J'ai récemment acquis un lecteur Touchatag  qui n’est en faite qu’un lecteur ACR122 NFC déguisé.

    Malheureusement, le logiciel fournit avec le Touchatag n’est pas satisfassent puisque celui-ci fonctionne uniquement via des applications WEB. En regardant en détail le descriptif technique du ACR122, j’ai noté qu’il est compatible avec la norme USB CCID et les applications PC/SC, donc possibilité de le piloter via la DLL WINSCARD.

    Suite à cette découverte, j’ai commencé à développer un petit programme pour tester la communication entre mon PC et le lecteur. Jusqu’à présent, j’ai réussi à ouvrir un canal de communication mais tous les paquets (APDU) que j’envoi au lecteur son acquitté d’une erreur.

    Exemple, lorsque je souhaite récupérer la version firmware, j’obtiens l’erreur 0x0A (voir page 29 du PDF ci-dessous) et lorsque je récupère des données, j’obtiens l’erreur 0x02 (A CRC error has been detected).

    Mon problème, c’est que je ne comprends pas pourquoi j’obtiens ces erreurs.

    Toutes idées ou suggestions seront les bienvenues. Merci par avance de votre aide.

    Cédric.

    Ci-dessous, le lien du PDF de l’ « Application programming interface » du lecteur ACR122:

    http://www.acs.com.hk/drivers/eng/API_ACR122U.pdf

    Ci-dessous, le source C# de mon application de test :

     

    using System;

    using System.Collections;

    using System.Runtime.InteropServices;

     

    namespace SimpleSmartcardDemo

    {

     

        class Program

        {

            static void Main(string[] args)

            {

                if (SmartcardManager.EstablishContext())

                {

                    //enumerating existing groups of readers

                    ArrayList readerGroups = SmartcardManager.ListReaderGroups();

     

                    if (readerGroups.Count != 0)

                    {

                        Console.WriteLine("Groups of readers:");

                        foreach (object group in readerGroups)

                        {

                            Console.WriteLine("\t{0}", 

                                group.ToString());

                        }

                        Console.WriteLine();

                    }

     

                    //enumerating available readers

                    ArrayList readers = SmartcardManager.ListReaders();

     

                    if (readers.Count == 0)

                    {

                        Console.WriteLine("No readers found.");

                    }

                    else

                    {

                        Console.WriteLine("Readers found:");

                        foreach (object reader in readers)

                        {

                            Console.WriteLine("\t{0}", reader.ToString());

     

                            if (SmartcardManager.EstablishConnexion(reader.ToString()))

                            {

                                Console.WriteLine("\tEstablishConnexion {0}", reader.ToString());

     

     

                                if (SmartcardManager.BeginTransaction())

                                {

                                    Console.WriteLine("\tBeginTransaction {0}", reader.ToString());

     

                                    if (SmartcardManager.Transmit())

                                        Console.WriteLine("\tTransmit {0}", SmartcardManager._error);

     

                                    if (SmartcardManager.EndTransaction())

                                        Console.WriteLine("\tEndTransaction {0}", reader.ToString());

                                }

     

                                if (SmartcardManager.ReleaseConnexion())

                                    Console.WriteLine("\tReleaseConnexion {0}", reader.ToString());

     

                            }

                        }

                    }

     

                    SmartcardManager.ReleaseContext();

                }

     

                Console.Write("\nPress any key...");

                Console.ReadKey();

            }

        }

     

        public enum ResourceManagerContext

        {

            User = 0,

            System = 2

        }

     

        public static class SmartcardManager

        {

            private static IntPtr _context;

            private static IntPtr pCardHandle;

            private static uint byActiveprotocol;

     

            public static string _error = string.Empty;

     

            public static bool EstablishContext()

            {

                ReleaseContext();

                uint result = SCardEstablishContext(ResourceManagerContext.System,IntPtr.Zero, IntPtr.Zero, ref _context);

                return (result == 0);

            }

     

            public static bool EstablishConnexion(string szReader)

            {

                // 3 param : dwShareMode (SCARD_SHARE_SHARED, SCARD_SHARE_EXCLUSIVE, SCARD_SHARE_DIRECT)

                // This application is allocating the reader for its private use, and will be controlling it directly. No other applications are allowed access to it.

     

                // 4 param : dwPreferredProtocols (SCARD_PROTOCOL_T0, SCARD_PROTOCOL_T1, 0)

                //  T=0 protocol: An asynchronous, character-oriented half-duplex transmission protocol.

                //  T=1 protocol: An asynchronous, block-oriented half-duplex transmission protocol.

     

                // 5 param :  pCardHandle : A handle that identifies the connection to the smart card in the designated reader.

     

                uint result = SCardConnect(_context, szReader, 2, (0x00000001), out pCardHandle, out byActiveprotocol);

                return (result == 0);

     

                // (0x00000001 | 0x00000002)

            }

     

     

            // The function waits for the completion of all other transactions before it begins. 

            // After the transaction starts, all other applications are blocked from accessing the smart card while the transaction is in progress.

            public static bool BeginTransaction()

            {

                uint result = SCardBeginTransaction(SmartcardManager.pCardHandle);

                return (result == 0);

            }

     

            public static bool EndTransaction()

            {

                // SCARD_LEAVE_CARD : No action occurs (0x0000)

                uint result = SCardEndTransaction(SmartcardManager.pCardHandle, (0x0000));

                return (result == 0);

            }

     

            public static bool ReleaseConnexion()

            {

                // SCARD_LEAVE_CARD : No action occurs (0x0000)

                uint result = SCardEndTransaction(SmartcardManager.pCardHandle, (0x0000));

                return (result == 0);

     

                // Error : The specified reader is not currently available for use

            }

     

            public static void ReleaseContext()

            {

                if (_context != IntPtr.Zero)

                {

                    SCardReleaseContext(_context);

                    _context = IntPtr.Zero;

                }

            }

     

            public static ArrayList ListReaders()

            {

                int bufsize = 0;

     

                //first call returns required buffer size

                SCardListReaders(_context, null, null, ref bufsize);

                String buffer = new String((char)0, bufsize);

     

                //retrieving list of connected readers

                uint result = SCardListReaders(_context, null, buffer, ref bufsize);

                string[] readers = buffer.Split(new char[] { (char)0 }, StringSplitOptions.RemoveEmptyEntries);

                return new ArrayList(readers);

            }

     

            public static ArrayList ListReaderGroups()

            {

                int bufsize = 0;

     

                //first call returns required buffer size

                SCardListReaderGroups(_context, null, ref bufsize);

                string buffer = new string((char)0, bufsize);

     

                //retrieving list of existing groups of readers

                uint result = SCardListReaderGroups(_context, buffer, ref bufsize);

     

                string[] groups = buffer.Split(new char[] { (char)0 }, StringSplitOptions.RemoveEmptyEntries);

                return new ArrayList(groups);

            }

     

            public static bool Transmit()

            {

                //  hCardHandle was set by a previous call to SCardConnect.

     

                // A pointer to the protocol header structure for the instruction :

                //The PCI info sent to the smart card,

                //I get the address of this PCI from "Winscard.dll",

                //and method "GetPciT0()" is defined bellow.

                IntPtr pioSendPci = GetPciT0();

     

                // A pointer to the actual data to be written to the card

                byte[] pbsendBuffer = SmartcardManager.GetSendBuffer();

     

                // The length, in bytes, of the pbSendBuffer parameter

                uint pbsendBufLen = (uint)pbsendBuffer.Length;

     

                // Pointer to the protocol header structure for the instruction, followed by a buffer in which to receive any returned protocol control information (PCI) specific to the protocol in use. This parameter can be NULL if no PCI is returned.

                SCARD_IO_REQUEST pioRecvPci = new SCARD_IO_REQUEST(0, 0);

     

                // Pointer to any data returned from the card

                byte[] pbRecvBuffer = new byte[255];

     

                // Supplies the length, in bytes, of the pbRecvBuffer parameter and receives the actual number of bytes received from the smart card. 

                uint pcbRecvLength = 255;

     

                uint result = SCardTransmit(pCardHandle, pioSendPci, pbsendBuffer, pbsendBufLen, pioRecvPci, pbRecvBuffer, pcbRecvLength);

     

                return (result == 0);

            }

     

     

            private static byte[] GetSendBuffer()

            {

                // APDU

     

                // Get the firmware (0x0A)

                /*

                string cla = "FF";  // the instruction class (The T=0 instruction class)

                string ins = "00";  // the instruction code (An instruction code in the T=0 instruction class)

                string p1 = "48";   // parameter to the instruction (Reference codes that complete the instruction code)

                string p2 = "00";   // parameter to the instruction (Reference codes that complete the instruction code)

                string lc = "00";   // size of I/O transfer (The number of data bytes to be transmitted during the command, per ISO 7816-4, Section 8.2.1)

                string body = "";

                */

     

                // Get data (0x02)

     

                string cla = "FF";  // the instruction class (The T=0 instruction class)

                string ins = "CA";  // the instruction code (An instruction code in the T=0 instruction class)

                string p1 = "00";   // parameter to the instruction (Reference codes that complete the instruction code)

                string p2 = "00";   // parameter to the instruction (Reference codes that complete the instruction code)

                string lc = "00";   // size of I/O transfer (The number of data bytes to be transmitted during the command, per ISO 7816-4, Section 8.2.1)

                string body = "";

     

     

                // Turn on green on RED and GREEN color LEDs (0x02)

                /*

                string cla = "FF";  // the instruction class (The T=0 instruction class)

                string ins = "00";  // the instruction code (An instruction code in the T=0 instruction class)

                string p1 = "40";   // parameter to the instruction (Reference codes that complete the instruction code)

                string p2 = "0F";   // parameter to the instruction (Reference codes that complete the instruction code)

                string lc = "04";   // size of I/O transfer (The number of data bytes to be transmitted during the command, per ISO 7816-4, Section 8.2.1)

                string body = "00000000";

                */

     

                // Get the current setting of the contactless interface (0x02)

                /*

                string cla = "FF";  // the instruction class (The T=0 instruction class)

                string ins = "00";  // the instruction code (An instruction code in the T=0 instruction class)

                string p1 = "00";   // parameter to the instruction (Reference codes that complete the instruction code)

                string p2 = "00";   // parameter to the instruction (Reference codes that complete the instruction code)

                string lc = "02";   // size of I/O transfer (The number of data bytes to be transmitted during the command, per ISO 7816-4, Section 8.2.1)

                string body = "D404";

                */

     

                string script = String.Format("{0}{1}{2}{3}{4}{5}", cla, ins, p1, p2,lc, body);

                byte[] buffer = new byte[script.Length / 2];

                for (int i = 0; i < script.Length; i = i + 2)

                {

                    string temp = script.Substring(i, 2);

                    buffer[i / 2] = byte.Parse(temp, System.Globalization.NumberStyles.HexNumber);

                }

                return buffer;

            }

     

     

            //

            // Private/Internal Types

            //

     

            [StructLayout(LayoutKind.Sequential)]

            public struct SCARD_IO_REQUEST

            {

                public SCARD_IO_REQUEST(int protocol, int length)

                {

                    this.protocol = protocol;

                    this.pciLength = length;

                }

                public int protocol;

                public int pciLength;

            }

     

            //Get the address of Pci from "Winscard.dll".

            static public IntPtr GetPciT0()

            {

                IntPtr handle = LoadLibrary("Winscard.dll");

                IntPtr pci = GetProcAddress(handle, "g_rgSCardT0Pci");

                FreeLibrary(handle);

                return pci;

            }

     

            // Prototypes :

            /*

            * 1 - SCardEstablishContext

            * 2 - SCardListReaders

            * 3 - SCardConnect

            * 4 - SCardBeginTransaction

            * 5 - SCardTransmit

            * 6 - SCardEndTransaction

            * 7 - SCardDisconnect

            */

     

            [DllImport("winscard.dll", CharSet = CharSet.Unicode)]

            static internal extern uint SCardEstablishContext(ResourceManagerContext scope, IntPtr reserved1, IntPtr reserved2, ref IntPtr context);

     

            [DllImport("winscard.dll", CharSet = CharSet.Unicode)]

            static internal extern uint SCardReleaseContext(IntPtr context);

     

            [DllImport("winscard.dll", CharSet = CharSet.Unicode)]

            static internal extern uint SCardListReaderGroups(IntPtr context, string groups, ref int size);

     

            [DllImport("winscard.dll", CharSet = CharSet.Unicode)]

            static internal extern uint SCardListReaders(IntPtr context, string groups, string readers, ref int size);

     

            [DllImport("winscard.dll", CharSet = CharSet.Unicode)]

            static extern uint SCardConnect(IntPtr hContext, string szReader, uint dwShareMode, uint dwPreferredProtocols, out IntPtr phCard, out uint pdwActiveProtocol);

     

            [DllImport("winscard.dll", CharSet = CharSet.Unicode)]

            static extern uint SCardBeginTransaction(IntPtr hCard);

     

            [DllImport("winscard.dll", CharSet = CharSet.Unicode)]

            static extern uint SCardEndTransaction(IntPtr hCard, uint dwDisposition);

     

            [DllImport("winscard.dll", CharSet = CharSet.Unicode)]

            static extern uint SCardDisconnect(IntPtr hCard, uint dwDisposition);

     

            [DllImport("winscard.dll", CharSet = CharSet.Unicode)]

            static extern uint SCardTransmit(IntPtr hCard, IntPtr sendPci, byte[] sendBuffer, uint sbLength, [In, Out] SCARD_IO_REQUEST recvPci, [Out] byte[] pbRecvBuffer, [In, Out] uint rbLength);

     

            //

            //

            //

     

            [DllImport("kernel32.dll")]

            private extern static IntPtr LoadLibrary(string fileName);

     

            [DllImport("kernel32.dll")]

            private extern static void FreeLibrary(IntPtr handle);

     

            [DllImport("kernel32.dll")]

            private extern static IntPtr GetProcAddress(IntPtr handle, string procName);

     

        }

    }

    mercredi 4 août 2010 15:45

Réponses

  • Bonjour,

    Votre problème est très spécifique et hors du scope de Microsoft. Je vous conseil de contacter le support/communauté du fabricant de votre carte.

    Cordialement


    Gilles TOURREAU - MVP C# - MCTS ADO .NET 3.5 - MCPD Windows Developper 3.5 - Architecte .NET/Consultant/Formateur - http://gilles.tourreau.fr
    • Marqué comme réponse Alex Petrescu vendredi 6 août 2010 07:50
    mercredi 4 août 2010 19:37
    Modérateur

Toutes les réponses

  • Bonjour,

    Votre problème est très spécifique et hors du scope de Microsoft. Je vous conseil de contacter le support/communauté du fabricant de votre carte.

    Cordialement


    Gilles TOURREAU - MVP C# - MCTS ADO .NET 3.5 - MCPD Windows Developper 3.5 - Architecte .NET/Consultant/Formateur - http://gilles.tourreau.fr
    • Marqué comme réponse Alex Petrescu vendredi 6 août 2010 07:50
    mercredi 4 août 2010 19:37
    Modérateur
  • Bonjour cconte,

    J'espere que t'as trouvé une solution à ton probleme. car j'ai le meme soucis que toi, j'arrive pas a me connecter au lecteur. je peux récupérer son nom, mais pas me connecter.

    Est ce que t'as trouvé une solution?

    Merci d'avance.


    Helmus

    lundi 28 mai 2012 13:01