TECHNOLOGIES
FORUMS
JOBS
BOOKS
EVENTS
INTERVIEWS
Live
MORE
LEARN
Training
CAREER
MEMBERS
VIDEOS
NEWS
BLOGS
Sign Up
Login
No unread comment.
View All Comments
No unread message.
View All Messages
No unread notification.
View All Notifications
Answers
Post
An Article
A Blog
A News
A Video
An EBook
An Interview Question
Ask Question
Forums
Monthly Leaders
Forum guidelines
cedric conte
NA
111
0
Driving a ACR122 NFC reader with the WINSCARD.DLL
Aug 4 2010 11:20 AM
Hi folks,
I recently acquired a touchatag reader which is in fact anr ACR122 NFC reader NFC disguised.
Unfortunately, the software supplied with the touchatag is not satisfacting, cause it only works on web applications.
So I have looked at the ACR122 technical detail, I have noted that works on USB CCID and compliant PC/SC. So that mean we have the ability to manipulate it through the DLL WINSCARD.
Following this discovery, I have begun to develop a small test program to communicate between my PC and the reader.
So far, I have managed to open a communication channel but all packages (ADPDU) sent to the reader come back with an error.
Example, when I want to get the firmware, I get the error 0x0A (See page 29 of the PDF below) and when I retrieve data, I get error 0x02 (A CRC error has-been detected).
My problem is that I don't understand why I get these errors.
Any ideas or suggestions are welcome. Thank you in advance for your help.
best regards,
Cédric.
Below, the PDF link « Application programming interface » of the reader ACR122:
http://www.acs.com.hk/drivers/eng/API_ACR122U.pdf
Ci-Below, C# code from the application 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);
}
}
Reply
Answers (
4
)
how to use .tlb or .exe files in .net application ?
Windows Service application not opening a PST file