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 :

Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
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);
 
    }
}