├── .gitattributes ├── .gitignore ├── Card.cs ├── Helper.cs ├── LICENSE ├── NFCReader.cs └── README.md /.gitattributes: -------------------------------------------------------------------------------- 1 | # Auto detect text files and perform LF normalization 2 | * text=auto 3 | 4 | # Custom for Visual Studio 5 | *.cs diff=csharp 6 | 7 | # Standard to msysgit 8 | *.doc diff=astextplain 9 | *.DOC diff=astextplain 10 | *.docx diff=astextplain 11 | *.DOCX diff=astextplain 12 | *.dot diff=astextplain 13 | *.DOT diff=astextplain 14 | *.pdf diff=astextplain 15 | *.PDF diff=astextplain 16 | *.rtf diff=astextplain 17 | *.RTF diff=astextplain 18 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Windows image file caches 2 | Thumbs.db 3 | ehthumbs.db 4 | 5 | # Folder config file 6 | Desktop.ini 7 | 8 | # Recycle Bin used on file shares 9 | $RECYCLE.BIN/ 10 | 11 | # Windows Installer files 12 | *.cab 13 | *.msi 14 | *.msm 15 | *.msp 16 | 17 | # Windows shortcuts 18 | *.lnk 19 | 20 | # ========================= 21 | # Operating System Files 22 | # ========================= 23 | 24 | # OSX 25 | # ========================= 26 | 27 | .DS_Store 28 | .AppleDouble 29 | .LSOverride 30 | 31 | # Thumbnails 32 | ._* 33 | 34 | # Files that might appear in the root of a volume 35 | .DocumentRevisions-V100 36 | .fseventsd 37 | .Spotlight-V100 38 | .TemporaryItems 39 | .Trashes 40 | .VolumeIcon.icns 41 | 42 | # Directories potentially created on remote AFP share 43 | .AppleDB 44 | .AppleDesktop 45 | Network Trash Folder 46 | Temporary Items 47 | .apdisk 48 | -------------------------------------------------------------------------------- /Card.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Runtime.InteropServices; 3 | public class Card 4 | { 5 | [StructLayout(LayoutKind.Sequential)] 6 | public struct SCARD_IO_REQUEST 7 | { 8 | public int dwProtocol; 9 | public int cbPciLength; 10 | } 11 | 12 | [StructLayout(LayoutKind.Sequential)] 13 | public struct APDURec 14 | { 15 | public byte bCLA; 16 | public byte bINS; 17 | public byte bP1; 18 | public byte bP2; 19 | public byte bP3; 20 | [MarshalAs(UnmanagedType.ByValArray, SizeConst = 256)] 21 | public byte[] Data; 22 | [MarshalAs(UnmanagedType.ByValArray, SizeConst = 3)] 23 | public byte[] SW; 24 | public bool IsSend; 25 | } 26 | 27 | [StructLayout(LayoutKind.Sequential)] 28 | public struct SCARD_READERSTATE 29 | { 30 | public string RdrName; 31 | public int UserData; 32 | public int RdrCurrState; 33 | public int RdrEventState; 34 | public int ATRLength; 35 | [MarshalAs(UnmanagedType.ByValArray, SizeConst = 37)] 36 | public byte[] ATRValue; 37 | } 38 | 39 | public const int SCARD_S_SUCCESS = 0; 40 | public const int SCARD_ATR_LENGTH = 33; 41 | 42 | 43 | /* =========================================================== 44 | ' Memory Card type constants 45 | '===========================================================*/ 46 | public const int CT_MCU = 0x00; // MCU 47 | public const int CT_IIC_Auto = 0x01; // IIC (Auto Detect Memory Size) 48 | public const int CT_IIC_1K = 0x02; // IIC (1K) 49 | public const int CT_IIC_2K = 0x03; // IIC (2K) 50 | public const int CT_IIC_4K = 0x04; // IIC (4K) 51 | public const int CT_IIC_8K = 0x05; // IIC (8K) 52 | public const int CT_IIC_16K = 0x06; // IIC (16K) 53 | public const int CT_IIC_32K = 0x07; // IIC (32K) 54 | public const int CT_IIC_64K = 0x08; // IIC (64K) 55 | public const int CT_IIC_128K = 0x09; // IIC (128K) 56 | public const int CT_IIC_256K = 0x0A; // IIC (256K) 57 | public const int CT_IIC_512K = 0x0B; // IIC (512K) 58 | public const int CT_IIC_1024K = 0x0C; // IIC (1024K) 59 | public const int CT_AT88SC153 = 0x0D; // AT88SC153 60 | public const int CT_AT88SC1608 = 0x0E; // AT88SC1608 61 | public const int CT_SLE4418 = 0x0F; // SLE4418 62 | public const int CT_SLE4428 = 0x10; // SLE4428 63 | public const int CT_SLE4432 = 0x11; // SLE4432 64 | public const int CT_SLE4442 = 0x12; // SLE4442 65 | public const int CT_SLE4406 = 0x13; // SLE4406 66 | public const int CT_SLE4436 = 0x14; // SLE4436 67 | public const int CT_SLE5536 = 0x15; // SLE5536 68 | public const int CT_MCUT0 = 0x16; // MCU T=0 69 | public const int CT_MCUT1 = 0x17; // MCU T=1 70 | public const int CT_MCU_Auto = 0x18; // MCU Autodetect 71 | 72 | /*=============================================================== 73 | ' CONTEXT SCOPE 74 | =============================================================== */ 75 | public const int SCARD_SCOPE_USER = 0; 76 | /*=============================================================== 77 | ' The context is a user context, and any database operations 78 | ' are performed within the domain of the user. 79 | '===============================================================*/ 80 | public const int SCARD_SCOPE_TERMINAL = 1; 81 | /*=============================================================== 82 | ' The context is that of the current terminal, and any database 83 | 'operations are performed within the domain of that terminal. 84 | '(The calling application must have appropriate access permissions 85 | 'for any database actions.) 86 | '===============================================================*/ 87 | 88 | public const int SCARD_SCOPE_SYSTEM = 2; 89 | /*=============================================================== 90 | ' The context is the system context, and any database operations 91 | ' are performed within the domain of the system. (The calling 92 | ' application must have appropriate access permissions for any 93 | ' database actions.) 94 | '===============================================================*/ 95 | /*=============================================================== 96 | ' Context Scope 97 | '===============================================================*/ 98 | public const int SCARD_STATE_UNAWARE = 0x00; 99 | /*=============================================================== 100 | ' The application is unaware of the current state, and would like 101 | ' to know. The use of this value results in an immediate return 102 | ' from state transition monitoring services. This is represented 103 | ' by all bits set to zero. 104 | '===============================================================*/ 105 | public const int SCARD_STATE_IGNORE = 0x01; 106 | /*=============================================================== 107 | ' The application requested that this reader be ignored. No other 108 | ' bits will be set. 109 | '===============================================================*/ 110 | 111 | public const int SCARD_STATE_CHANGED = 0x02; 112 | /*=============================================================== 113 | ' This implies that there is a difference between the state 114 | ' believed by the application, and the state known by the Service 115 | ' Manager.When this bit is set, the application may assume a 116 | ' significant state change has occurred on this reader. 117 | '===============================================================*/ 118 | 119 | public const int SCARD_STATE_UNKNOWN = 0x04; 120 | /*=============================================================== 121 | ' This implies that the given reader name is not recognized by 122 | ' the Service Manager. If this bit is set, then SCARD_STATE_CHANGED 123 | ' and SCARD_STATE_IGNORE will also be set. 124 | '===============================================================*/ 125 | public const int SCARD_STATE_UNAVAILABLE = 0x08; 126 | /*=============================================================== 127 | ' This implies that the actual state of this reader is not 128 | ' available. If this bit is set, then all the following bits are 129 | ' clear. 130 | '===============================================================*/ 131 | public const int SCARD_STATE_EMPTY = 0x10; 132 | /*=============================================================== 133 | ' This implies that there is not card in the reader. If this bit 134 | ' is set, all the following bits will be clear. 135 | ===============================================================*/ 136 | public const int SCARD_STATE_PRESENT = 0x20; 137 | /*=============================================================== 138 | ' This implies that there is a card in the reader. 139 | ===============================================================*/ 140 | public const int SCARD_STATE_ATRMATCH = 0x40; 141 | /*=============================================================== 142 | ' This implies that there is a card in the reader with an ATR 143 | ' matching one of the target cards. If this bit is set, 144 | ' SCARD_STATE_PRESENT will also be set. This bit is only returned 145 | ' on the SCardLocateCard() service. 146 | ===============================================================*/ 147 | public const int SCARD_STATE_EXCLUSIVE = 0x80; 148 | /*=============================================================== 149 | ' This implies that the card in the reader is allocated for 150 | ' exclusive use by another application. If this bit is set, 151 | ' SCARD_STATE_PRESENT will also be set. 152 | ===============================================================*/ 153 | public const int SCARD_STATE_INUSE = 0x100; 154 | /*=============================================================== 155 | ' This implies that the card in the reader is in use by one or 156 | ' more other applications, but may be connected to in shared mode. 157 | ' If this bit is set, SCARD_STATE_PRESENT will also be set. 158 | ===============================================================*/ 159 | public const int SCARD_STATE_MUTE = 0x200; 160 | /*=============================================================== 161 | ' This implies that the card in the reader is unresponsive or not 162 | ' supported by the reader or software. 163 | ' ===============================================================*/ 164 | public const int SCARD_STATE_UNPOWERED = 0x400; 165 | /*=============================================================== 166 | ' This implies that the card in the reader has not been powered up. 167 | ' ===============================================================*/ 168 | 169 | public const int SCARD_SHARE_EXCLUSIVE = 1; 170 | /*=============================================================== 171 | ' This application is not willing to share this card with other 172 | 'applications. 173 | '===============================================================*/ 174 | public const int SCARD_SHARE_SHARED = 2; 175 | /*=============================================================== 176 | ' This application is willing to share this card with other 177 | 'applications. 178 | '===============================================================*/ 179 | public const int SCARD_SHARE_DIRECT = 3; 180 | /*=============================================================== 181 | ' This application demands direct control of the reader, so it 182 | ' is not available to other applications. 183 | '===============================================================*/ 184 | 185 | /*=========================================================== 186 | ' Disposition 187 | '===========================================================*/ 188 | public const int SCARD_LEAVE_CARD = 0; // Don't do anything special on close 189 | public const int SCARD_RESET_CARD = 1; // Reset the card on close 190 | public const int SCARD_UNPOWER_CARD = 2; // Power down the card on close 191 | public const int SCARD_EJECT_CARD = 3; // Eject the card on close 192 | 193 | 194 | /* =========================================================== 195 | ' ACS IOCTL class 196 | '===========================================================*/ 197 | public const long FILE_DEVICE_SMARTCARD = 0x310000; // Reader action IOCTLs 198 | 199 | public const long IOCTL_SMARTCARD_DIRECT = FILE_DEVICE_SMARTCARD + 2050 * 4; 200 | public const long IOCTL_SMARTCARD_SELECT_SLOT = FILE_DEVICE_SMARTCARD + 2051 * 4; 201 | public const long IOCTL_SMARTCARD_DRAW_LCDBMP = FILE_DEVICE_SMARTCARD + 2052 * 4; 202 | public const long IOCTL_SMARTCARD_DISPLAY_LCD = FILE_DEVICE_SMARTCARD + 2053 * 4; 203 | public const long IOCTL_SMARTCARD_CLR_LCD = FILE_DEVICE_SMARTCARD + 2054 * 4; 204 | public const long IOCTL_SMARTCARD_READ_KEYPAD = FILE_DEVICE_SMARTCARD + 2055 * 4; 205 | public const long IOCTL_SMARTCARD_READ_RTC = FILE_DEVICE_SMARTCARD + 2057 * 4; 206 | public const long IOCTL_SMARTCARD_SET_RTC = FILE_DEVICE_SMARTCARD + 2058 * 4; 207 | public const long IOCTL_SMARTCARD_SET_OPTION = FILE_DEVICE_SMARTCARD + 2059 * 4; 208 | public const long IOCTL_SMARTCARD_SET_LED = FILE_DEVICE_SMARTCARD + 2060 * 4; 209 | public const long IOCTL_SMARTCARD_LOAD_KEY = FILE_DEVICE_SMARTCARD + 2062 * 4; 210 | public const long IOCTL_SMARTCARD_READ_EEPROM = FILE_DEVICE_SMARTCARD + 2065 * 4; 211 | public const long IOCTL_SMARTCARD_WRITE_EEPROM = FILE_DEVICE_SMARTCARD + 2066 * 4; 212 | public const long IOCTL_SMARTCARD_GET_VERSION = FILE_DEVICE_SMARTCARD + 2067 * 4; 213 | public const long IOCTL_SMARTCARD_GET_READER_INFO = FILE_DEVICE_SMARTCARD + 2051 * 4; 214 | public const long IOCTL_SMARTCARD_SET_CARD_TYPE = FILE_DEVICE_SMARTCARD + 2060 * 4; 215 | public const long IOCTL_SMARTCARD_ACR128_ESCAPE_COMMAND = FILE_DEVICE_SMARTCARD + 2079 * 4; 216 | 217 | /*=========================================================== 218 | ' Error Codes 219 | '===========================================================*/ 220 | public const int SCARD_F_INTERNAL_ERROR = -2146435071; 221 | public const int SCARD_E_CANCELLED = -2146435070; 222 | public const int SCARD_E_INVALID_HANDLE = -2146435069; 223 | public const int SCARD_E_INVALID_PARAMETER = -2146435068; 224 | public const int SCARD_E_INVALID_TARGET = -2146435067; 225 | public const int SCARD_E_NO_MEMORY = -2146435066; 226 | public const int SCARD_F_WAITED_TOO_LONG = -2146435065; 227 | public const int SCARD_E_INSUFFICIENT_BUFFER = -2146435064; 228 | public const int SCARD_E_UNKNOWN_READER = -2146435063; 229 | 230 | 231 | public const int SCARD_E_TIMEOUT = -2146435062; 232 | public const int SCARD_E_SHARING_VIOLATION = -2146435061; 233 | public const int SCARD_E_NO_SMARTCARD = -2146435060; 234 | public const int SCARD_E_UNKNOWN_CARD = -2146435059; 235 | public const int SCARD_E_CANT_DISPOSE = -2146435058; 236 | public const int SCARD_E_PROTO_MISMATCH = -2146435057; 237 | 238 | 239 | public const int SCARD_E_NOT_READY = -2146435056; 240 | public const int SCARD_E_INVALID_VALUE = -2146435055; 241 | public const int SCARD_E_SYSTEM_CANCELLED = -2146435054; 242 | public const int SCARD_F_COMM_ERROR = -2146435053; 243 | public const int SCARD_F_UNKNOWN_ERROR = -2146435052; 244 | public const int SCARD_E_INVALID_ATR = -2146435051; 245 | public const int SCARD_E_NOT_TRANSACTED = -2146435050; 246 | public const int SCARD_E_READER_UNAVAILABLE = -2146435049; 247 | public const int SCARD_P_SHUTDOWN = -2146435048; 248 | public const int SCARD_E_PCI_TOO_SMALL = -2146435047; 249 | 250 | public const int SCARD_E_READER_UNSUPPORTED = -2146435046; 251 | public const int SCARD_E_DUPLICATE_READER = -2146435045; 252 | public const int SCARD_E_CARD_UNSUPPORTED = -2146435044; 253 | public const int SCARD_E_NO_SERVICE = -2146435043; 254 | public const int SCARD_E_SERVICE_STOPPED = -2146435042; 255 | 256 | public const int SCARD_W_UNSUPPORTED_CARD = -2146435041; 257 | public const int SCARD_W_UNRESPONSIVE_CARD = -2146435040; 258 | public const int SCARD_W_UNPOWERED_CARD = -2146435039; 259 | public const int SCARD_W_RESET_CARD = -2146435038; 260 | public const int SCARD_W_REMOVED_CARD = -2146435037; 261 | 262 | /*=========================================================== 263 | ' PROTOCOL 264 | '===========================================================*/ 265 | public const int SCARD_PROTOCOL_UNDEFINED = 0x00; // There is no active protocol. 266 | public const int SCARD_PROTOCOL_T0 = 0x01; // T=0 is the active protocol. 267 | public const int SCARD_PROTOCOL_T1 = 0x02; // T=1 is the active protocol. 268 | public const int SCARD_PROTOCOL_RAW = 0x10000; // Raw is the active protocol. 269 | //public const int SCARD_PROTOCOL_DEFAULT = 0x80000000; // Use implicit PTS. 270 | /*=========================================================== 271 | ' READER STATE 272 | '===========================================================*/ 273 | public const int SCARD_UNKNOWN = 0; 274 | /*=============================================================== 275 | ' This value implies the driver is unaware of the current 276 | ' state of the reader. 277 | '===============================================================*/ 278 | public const int SCARD_ABSENT = 1; 279 | /*=============================================================== 280 | ' This value implies there is no card in the reader. 281 | '===============================================================*/ 282 | public const int SCARD_PRESENT = 2; 283 | /*=============================================================== 284 | ' This value implies there is a card is present in the reader, 285 | 'but that it has not been moved into position for use. 286 | '===============================================================*/ 287 | public const int SCARD_SWALLOWED = 3; 288 | /*=============================================================== 289 | ' This value implies there is a card in the reader in position 290 | ' for use. The card is not powered. 291 | '===============================================================*/ 292 | public const int SCARD_POWERED = 4; 293 | /*=============================================================== 294 | ' This value implies there is power is being provided to the card, 295 | ' but the Reader Driver is unaware of the mode of the card. 296 | '===============================================================*/ 297 | public const int SCARD_NEGOTIABLE = 5; 298 | /*=============================================================== 299 | ' This value implies the card has been reset and is awaiting 300 | ' PTS negotiation. 301 | '===============================================================*/ 302 | public const int SCARD_SPECIFIC = 6; 303 | /*=============================================================== 304 | ' This value implies the card has been reset and specific 305 | ' communication protocols have been established. 306 | '===============================================================*/ 307 | 308 | /*========================================================================== 309 | ' Prototypes 310 | '==========================================================================*/ 311 | 312 | 313 | [DllImport("winscard.dll")] 314 | public static extern int SCardEstablishContext(int dwScope, int pvReserved1, int pvReserved2, ref int phContext); 315 | 316 | [DllImport("winscard.dll")] 317 | public static extern int SCardReleaseContext(int phContext); 318 | 319 | [DllImport("winscard.dll")] 320 | public static extern int SCardConnect(int hContext, string szReaderName, int dwShareMode, int dwPrefProtocol, ref int phCard, ref int ActiveProtocol); 321 | 322 | [DllImport("winscard.dll")] 323 | public static extern int SCardBeginTransaction(int hCard); 324 | 325 | [DllImport("winscard.dll")] 326 | public static extern int SCardDisconnect(int hCard, int Disposition); 327 | 328 | [DllImport("winscard.dll")] 329 | public static extern int SCardListReaderGroups(int hContext, ref string mzGroups, ref int pcchGroups); 330 | 331 | [DllImport("winscard.DLL", EntryPoint = "SCardListReadersA", CharSet = CharSet.Ansi)] 332 | public static extern int SCardListReaders( 333 | int hContext, 334 | byte[] Groups, 335 | byte[] Readers, 336 | ref int pcchReaders 337 | ); 338 | 339 | 340 | [DllImport("winscard.dll")] 341 | public static extern int SCardStatus(int hCard, string szReaderName, ref int pcchReaderLen, ref int State, ref int Protocol, ref byte ATR, ref int ATRLen); 342 | 343 | [DllImport("winscard.dll")] 344 | public static extern int SCardEndTransaction(int hCard, int Disposition); 345 | 346 | [DllImport("winscard.dll")] 347 | public static extern int SCardState(int hCard, ref uint State, ref uint Protocol, ref byte ATR, ref uint ATRLen); 348 | 349 | [DllImport("WinScard.dll")] 350 | public static extern int SCardTransmit(IntPtr hCard, 351 | ref SCARD_IO_REQUEST pioSendPci, 352 | ref byte[] pbSendBuffer, 353 | int cbSendLength, 354 | ref SCARD_IO_REQUEST pioRecvPci, 355 | ref byte[] pbRecvBuffer, 356 | ref int pcbRecvLength); 357 | 358 | [DllImport("winscard.dll")] 359 | public static extern int SCardTransmit(int hCard, ref SCARD_IO_REQUEST pioSendRequest, ref byte SendBuff, int SendBuffLen, ref SCARD_IO_REQUEST pioRecvRequest, ref byte RecvBuff, ref int RecvBuffLen); 360 | 361 | [DllImport("winscard.dll")] 362 | public static extern int SCardTransmit(int hCard, ref SCARD_IO_REQUEST pioSendRequest, ref byte[] SendBuff, int SendBuffLen, ref SCARD_IO_REQUEST pioRecvRequest, ref byte[] RecvBuff, ref int RecvBuffLen); 363 | 364 | [DllImport("winscard.dll")] 365 | public static extern int SCardControl(int hCard, uint dwControlCode, ref byte SendBuff, int SendBuffLen, ref byte RecvBuff, int RecvBuffLen, ref int pcbBytesReturned); 366 | 367 | [DllImport("winscard.dll")] 368 | public static extern int SCardGetStatusChange(int hContext, int TimeOut, ref SCARD_READERSTATE ReaderState, int ReaderCount); 369 | 370 | public static string GetScardErrMsg(int ReturnCode) 371 | { 372 | switch (ReturnCode) 373 | { 374 | case SCARD_E_CANCELLED: 375 | return ("The action was canceled by an SCardCancel request."); 376 | case SCARD_E_CANT_DISPOSE: 377 | return ("The system could not dispose of the media in the requested manner."); 378 | case SCARD_E_CARD_UNSUPPORTED: 379 | return ("The smart card does not meet minimal requirements for support."); 380 | case SCARD_E_DUPLICATE_READER: 381 | return ("The reader driver didn't produce a unique reader name."); 382 | case SCARD_E_INSUFFICIENT_BUFFER: 383 | return ("The data buffer for returned data is too small for the returned data."); 384 | case SCARD_E_INVALID_ATR: 385 | return ("An ATR string obtained from the registry is not a valid ATR string."); 386 | case SCARD_E_INVALID_HANDLE: 387 | return ("The supplied handle was invalid."); 388 | case SCARD_E_INVALID_PARAMETER: 389 | return ("One or more of the supplied parameters could not be properly interpreted."); 390 | case SCARD_E_INVALID_TARGET: 391 | return ("Registry startup information is missing or invalid."); 392 | case SCARD_E_INVALID_VALUE: 393 | return ("One or more of the supplied parameter values could not be properly interpreted."); 394 | case SCARD_E_NOT_READY: 395 | return ("The reader or card is not ready to accept commands."); 396 | case SCARD_E_NOT_TRANSACTED: 397 | return ("An attempt was made to end a non-existent transaction."); 398 | case SCARD_E_NO_MEMORY: 399 | return ("Not enough memory available to complete this command."); 400 | case SCARD_E_NO_SERVICE: 401 | return ("The smart card resource manager is not running."); 402 | case SCARD_E_NO_SMARTCARD: 403 | return ("The operation requires a smart card, but no smart card is currently in the device."); 404 | case SCARD_E_PCI_TOO_SMALL: 405 | return ("The PCI receive buffer was too small."); 406 | case SCARD_E_PROTO_MISMATCH: 407 | return ("The requested protocols are incompatible with the protocol currently in use with the card."); 408 | case SCARD_E_READER_UNAVAILABLE: 409 | return ("The specified reader is not currently available for use."); 410 | case SCARD_E_READER_UNSUPPORTED: 411 | return ("The reader driver does not meet minimal requirements for support."); 412 | case SCARD_E_SERVICE_STOPPED: 413 | return ("The smart card resource manager has shut down."); 414 | case SCARD_E_SHARING_VIOLATION: 415 | return ("The smart card cannot be accessed because of other outstanding connections."); 416 | case SCARD_E_SYSTEM_CANCELLED: 417 | return ("The action was canceled by the system, presumably to log off or shut down."); 418 | case SCARD_E_TIMEOUT: 419 | return ("The user-specified timeout value has expired."); 420 | case SCARD_E_UNKNOWN_CARD: 421 | return ("The specified smart card name is not recognized."); 422 | case SCARD_E_UNKNOWN_READER: 423 | return ("The specified reader name is not recognized."); 424 | case SCARD_F_COMM_ERROR: 425 | return ("An internal communications error has been detected."); 426 | case SCARD_F_INTERNAL_ERROR: 427 | return ("An internal consistency check failed."); 428 | case SCARD_F_UNKNOWN_ERROR: 429 | return ("An internal error has been detected, but the source is unknown."); 430 | case SCARD_F_WAITED_TOO_LONG: 431 | return ("An internal consistency timer has expired."); 432 | case SCARD_S_SUCCESS: 433 | return ("No error was encountered."); 434 | case SCARD_W_REMOVED_CARD: 435 | return ("The smart card has been removed, so that further communication is not possible."); 436 | case SCARD_W_RESET_CARD: 437 | return ("The smart card has been reset, so any shared state information is invalid."); 438 | case SCARD_W_UNPOWERED_CARD: 439 | return ("Power has been removed from the smart card, so that further communication is not possible."); 440 | case SCARD_W_UNRESPONSIVE_CARD: 441 | return ("The smart card is not responding to a reset."); 442 | case SCARD_W_UNSUPPORTED_CARD: 443 | return ("The reader cannot communicate with the card, due to ATR string configuration conflicts."); 444 | default: 445 | return ("?"); 446 | } 447 | } 448 | } -------------------------------------------------------------------------------- /Helper.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | 7 | static class Helper 8 | { 9 | public static IEnumerable SplitByLength(this string str, int maxLength) 10 | { 11 | int index = 0; 12 | while (true) 13 | { 14 | if (index + maxLength >= str.Length) 15 | { 16 | yield return str.Substring(index); 17 | yield break; 18 | } 19 | yield return str.Substring(index, maxLength); 20 | index += maxLength; 21 | } 22 | } 23 | public static Byte[] Decode(this Byte[] bytes) 24 | { 25 | if (bytes.Length == 0) return bytes; 26 | var i = bytes.Length - 1; 27 | while (bytes[i] == 0 || bytes[i] == 144) 28 | { 29 | i--; 30 | } 31 | Byte[] copy = new Byte[i + 1]; 32 | Array.Copy(bytes, copy, i + 1); 33 | return copy; 34 | } 35 | 36 | public static string Base64Encode(string plainText) 37 | { 38 | var plainTextBytes = System.Text.Encoding.UTF8.GetBytes(plainText); 39 | return System.Convert.ToBase64String(plainTextBytes); 40 | } 41 | 42 | public static string Base64Decode(string base64EncodedData) 43 | { 44 | var base64EncodedBytes = System.Convert.FromBase64String(base64EncodedData); 45 | return System.Text.Encoding.UTF8.GetString(base64EncodedBytes); 46 | } 47 | } 48 | 49 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019 Hüseyin Akbaş 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /NFCReader.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.ComponentModel; 4 | using System.Linq; 5 | using System.Text; 6 | using System.Threading.Tasks; 7 | 8 | public class NFCReader 9 | { 10 | public int retCode, hCard, Protocol; 11 | int hContext; 12 | public bool connActive = false; 13 | public byte[] SendBuff = new byte[263]; 14 | public byte[] RecvBuff = new byte[263]; 15 | public int SendLen, RecvLen; 16 | internal enum SmartcardState 17 | { 18 | None = 0, 19 | Inserted = 1, 20 | Ejected = 2 21 | } 22 | 23 | public delegate void CardEventHandler(); 24 | public event CardEventHandler CardInserted; 25 | public event CardEventHandler CardEjected; 26 | public event CardEventHandler DeviceDisconnected; 27 | private BackgroundWorker _worker; 28 | private Card.SCARD_READERSTATE RdrState; 29 | private string readername; 30 | private Card.SCARD_READERSTATE[] states; 31 | private void WaitChangeStatus(object sender, DoWorkEventArgs e) 32 | { 33 | while (!e.Cancel) 34 | { 35 | int nErrCode = Card.SCardGetStatusChange(hContext, 1000, ref states[0], 1); 36 | 37 | if (nErrCode == Card.SCARD_E_SERVICE_STOPPED) 38 | { 39 | DeviceDisconnected(); 40 | e.Cancel = true; 41 | } 42 | 43 | //Check if the state changed from the last time. 44 | if ((this.states[0].RdrEventState & 2) == 2) 45 | { 46 | //Check what changed. 47 | SmartcardState state = SmartcardState.None; 48 | if ((this.states[0].RdrEventState & 32) == 32 && (this.states[0].RdrCurrState & 32) != 32) 49 | { 50 | //The card was inserted. 51 | state = SmartcardState.Inserted; 52 | } 53 | else if ((this.states[0].RdrEventState & 16) == 16 && (this.states[0].RdrCurrState & 16) != 16) 54 | { 55 | //The card was ejected. 56 | state = SmartcardState.Ejected; 57 | } 58 | if (state != SmartcardState.None && this.states[0].RdrCurrState != 0) 59 | { 60 | switch (state) 61 | { 62 | case SmartcardState.Inserted: 63 | { 64 | //MessageBox.Show("Card inserted"); 65 | CardInserted(); 66 | break; 67 | } 68 | case SmartcardState.Ejected: 69 | { 70 | //MessageBox.Show("Card ejected"); 71 | CardEjected(); 72 | break; 73 | } 74 | default: 75 | { 76 | //MessageBox.Show("Some other state..."); 77 | break; 78 | } 79 | } 80 | } 81 | //Update the current state for the next time they are checked. 82 | this.states[0].RdrCurrState = this.states[0].RdrEventState; 83 | } 84 | } 85 | } 86 | public Card.SCARD_IO_REQUEST pioSendRequest; 87 | private int SendAPDUandDisplay(int reqType) 88 | { 89 | int indx; 90 | string tmpStr = ""; 91 | 92 | pioSendRequest.dwProtocol = Protocol; 93 | pioSendRequest.cbPciLength = 8; 94 | 95 | //Display Apdu In 96 | for (indx = 0; indx <= SendLen - 1; indx++) 97 | { 98 | tmpStr = tmpStr + " " + string.Format("{0:X2}", SendBuff[indx]); 99 | } 100 | 101 | retCode = Card.SCardTransmit(hCard, ref pioSendRequest, ref SendBuff[0], 102 | SendLen, ref pioSendRequest, ref RecvBuff[0], ref RecvLen); 103 | 104 | if (retCode != Card.SCARD_S_SUCCESS) 105 | { 106 | return retCode; 107 | } 108 | 109 | else 110 | { 111 | try 112 | { 113 | tmpStr = ""; 114 | switch (reqType) 115 | { 116 | case 0: 117 | for (indx = (RecvLen - 2); indx <= (RecvLen - 1); indx++) 118 | { 119 | tmpStr = tmpStr + " " + string.Format("{0:X2}", RecvBuff[indx]); 120 | } 121 | 122 | if ((tmpStr).Trim() != "90 00") 123 | { 124 | //MessageBox.Show("Return bytes are not acceptable."); 125 | return -202; 126 | } 127 | 128 | break; 129 | 130 | case 1: 131 | 132 | for (indx = (RecvLen - 2); indx <= (RecvLen - 1); indx++) 133 | { 134 | tmpStr = tmpStr + string.Format("{0:X2}", RecvBuff[indx]); 135 | } 136 | 137 | if (tmpStr.Trim() != "90 00") 138 | { 139 | tmpStr = tmpStr + " " + string.Format("{0:X2}", RecvBuff[indx]); 140 | } 141 | 142 | else 143 | { 144 | tmpStr = "ATR : "; 145 | for (indx = 0; indx <= (RecvLen - 3); indx++) 146 | { 147 | tmpStr = tmpStr + " " + string.Format("{0:X2}", RecvBuff[indx]); 148 | } 149 | } 150 | 151 | break; 152 | 153 | case 2: 154 | 155 | for (indx = 0; indx <= (RecvLen - 1); indx++) 156 | { 157 | tmpStr = tmpStr + " " + string.Format("{0:X2}", RecvBuff[indx]); 158 | } 159 | 160 | break; 161 | } 162 | } 163 | catch (IndexOutOfRangeException) 164 | { 165 | return -200; 166 | } 167 | } 168 | return retCode; 169 | } 170 | private void ClearBuffers() 171 | { 172 | long indx; 173 | 174 | for (indx = 0; indx <= 262; indx++) 175 | { 176 | RecvBuff[indx] = 0; 177 | SendBuff[indx] = 0; 178 | } 179 | } 180 | private bool AuthBlock(String block) 181 | { 182 | ClearBuffers(); 183 | SendBuff[0] = 0xFF; // CLA 184 | SendBuff[2] = 0x00; // P1: same for all source types 185 | SendBuff[1] = 0x86; // INS: for stored key input 186 | SendBuff[3] = 0x00; // P2 : Memory location; P2: for stored key input 187 | SendBuff[4] = 0x05; // P3: for stored key input 188 | SendBuff[5] = 0x01; // Byte 1: version number 189 | SendBuff[6] = 0x00; // Byte 2 190 | SendBuff[7] = (byte)int.Parse(block); // Byte 3: sectore no. for stored key input 191 | SendBuff[8] = 0x61; // Byte 4 : Key B for stored key input 192 | SendBuff[9] = (byte)int.Parse("1"); // Byte 5 : Session key for non-volatile memory 193 | 194 | SendLen = 0x0A; 195 | RecvLen = 0x02; 196 | 197 | retCode = SendAPDUandDisplay(0); 198 | 199 | if (retCode != Card.SCARD_S_SUCCESS) 200 | { 201 | //MessageBox.Show("FAIL Authentication! No:" + retCode.ToString()); 202 | return false; 203 | } 204 | 205 | return true; 206 | } 207 | public string GetCardUID() 208 | { 209 | string cardUID = ""; 210 | byte[] receivedUID = new byte[256]; 211 | Card.SCARD_IO_REQUEST request = new Card.SCARD_IO_REQUEST(); 212 | request.dwProtocol = Card.SCARD_PROTOCOL_T1; 213 | request.cbPciLength = System.Runtime.InteropServices.Marshal.SizeOf(typeof(Card.SCARD_IO_REQUEST)); 214 | byte[] sendBytes = new byte[] { 0xFF, 0xCA, 0x00, 0x00, 0x00 }; 215 | int outBytes = receivedUID.Length; 216 | int status = Card.SCardTransmit(hCard, ref request, ref sendBytes[0], sendBytes.Length, ref request, ref receivedUID[0], ref outBytes); 217 | 218 | if (status != Card.SCARD_S_SUCCESS) 219 | cardUID = ""; 220 | else 221 | cardUID = BitConverter.ToString(receivedUID.Take(4).ToArray()).Replace("-", string.Empty).ToLower(); 222 | return cardUID; 223 | } 224 | public List GetReadersList() 225 | { 226 | string ReaderList = "" + Convert.ToChar(0); 227 | int indx; 228 | int pcchReaders = 0; 229 | string rName = ""; 230 | List lstReaders = new List(); 231 | //Establish Context 232 | retCode = Card.SCardEstablishContext(Card.SCARD_SCOPE_USER, 0, 0, ref hContext); 233 | 234 | if (retCode != Card.SCARD_S_SUCCESS) 235 | { 236 | throw new Exception("Error SCardEstablishContext"); 237 | } 238 | 239 | // 2. List PC/SC card readers installed in the system 240 | 241 | retCode = Card.SCardListReaders(this.hContext, null, null, ref pcchReaders); 242 | 243 | if (retCode != Card.SCARD_S_SUCCESS) 244 | { 245 | throw new Exception("Error SCardListReaders"); 246 | } 247 | 248 | byte[] ReadersList = new byte[pcchReaders]; 249 | 250 | // Fill reader list 251 | retCode = Card.SCardListReaders(this.hContext, null, ReadersList, ref pcchReaders); 252 | 253 | if (retCode != Card.SCARD_S_SUCCESS) 254 | { 255 | throw new Exception("Error SCardListReaders"); 256 | } 257 | 258 | rName = ""; 259 | indx = 0; 260 | 261 | 262 | while (ReadersList[indx] != 0) 263 | { 264 | 265 | while (ReadersList[indx] != 0) 266 | { 267 | rName += (char)ReadersList[indx]; 268 | indx++; 269 | } 270 | 271 | 272 | lstReaders.Add(rName); 273 | rName = ""; 274 | indx++; 275 | 276 | } 277 | return lstReaders; 278 | } 279 | /* 280 | public bool CleanCard(int maxblock) 281 | { 282 | int i = 0; 283 | string clean = "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"; 284 | while (i < maxblock) 285 | { 286 | WriteBlock(clean, (i + 4).ToString()); 287 | i++; 288 | } 289 | return true; 290 | } 291 | */ 292 | public bool WriteBlock(String Text, String Block) 293 | { 294 | 295 | char[] tmpStr = Text.ToArray(); 296 | int indx; 297 | if (AuthBlock(Block)) 298 | { 299 | ClearBuffers(); 300 | SendBuff[0] = 0xFF; // CLA 301 | SendBuff[1] = 0xD6; // INS 302 | SendBuff[2] = 0x00; // P1 303 | SendBuff[3] = (byte)int.Parse(Block); // P2 : Starting Block No. 304 | SendBuff[4] = (byte)int.Parse("16"); // P3 : Data length 305 | 306 | for (indx = 0; indx <= (tmpStr).Length - 1; indx++) 307 | { 308 | SendBuff[indx + 5] = (byte)tmpStr[indx]; 309 | } 310 | SendLen = SendBuff[4] + 5; 311 | RecvLen = 0x02; 312 | 313 | retCode = SendAPDUandDisplay(2); 314 | 315 | if (retCode != Card.SCARD_S_SUCCESS) 316 | return false; 317 | else 318 | return true; 319 | } 320 | else 321 | return false; 322 | } 323 | /* 324 | public string ReadString() 325 | { 326 | int i = 0; 327 | string ret=""; 328 | string tmpStr= String.Concat(ReadBlock((i + 4).ToString())); 329 | ret += String.Concat(tmpStr); 330 | i++; 331 | while (!(tmpStr.Contains("\0\0"))) 332 | { 333 | tmpStr = String.Concat(ReadBlock((i + 4).ToString())); 334 | ret += tmpStr; 335 | i++; 336 | } 337 | return ret.Replace("\0", string.Empty); 338 | 339 | */ 340 | /* 341 | public bool WriteString(String Text) 342 | { 343 | string[] parts = Helper.SplitByLength(Text, 16).ToArray(); 344 | double textlen = Text.Length, delim = 16; 345 | int blocklen = (int)Math.Ceiling(textlen / delim); 346 | for (int i = 0; i < blocklen; i++) 347 | { 348 | if(!WriteBlock(parts[i],(i+4).ToString()))return false; 349 | } 350 | return true; 351 | } 352 | */ 353 | public byte[] ReadBlock(String Block) 354 | { 355 | byte[] tmpStr; 356 | int indx; 357 | 358 | if (AuthBlock(Block)) 359 | { 360 | ClearBuffers(); 361 | SendBuff[0] = 0xFF; // CLA 362 | SendBuff[1] = 0xB0;// INS 363 | SendBuff[2] = 0x00;// P1 364 | SendBuff[3] = (byte)int.Parse(Block);// P2 : Block No. 365 | SendBuff[4] = (byte)int.Parse("16");// Le 366 | 367 | SendLen = 5; 368 | RecvLen = SendBuff[4] + 2; 369 | 370 | retCode = SendAPDUandDisplay(2); 371 | 372 | if (retCode == -200) 373 | { 374 | return new byte[] { }; 375 | } 376 | 377 | if (retCode == -202) 378 | { 379 | return new byte[] { }; 380 | } 381 | 382 | if (retCode != Card.SCARD_S_SUCCESS) 383 | { 384 | return new byte[] { }; 385 | } 386 | 387 | // Display data in text format 388 | List t = new List(); 389 | for (indx = 0; indx <= RecvLen - 1; indx++) 390 | { 391 | t.Add(RecvBuff[indx]); 392 | } 393 | tmpStr = t.ToArray(); 394 | return tmpStr; 395 | } 396 | else return new byte[]{}; 397 | } 398 | public bool Connect() 399 | { 400 | string readerName = GetReadersList()[0]; 401 | connActive = true; 402 | retCode = Card.SCardConnect(hContext, readerName, Card.SCARD_SHARE_SHARED, 403 | Card.SCARD_PROTOCOL_T0 | Card.SCARD_PROTOCOL_T1, ref hCard, ref Protocol); 404 | if (retCode != Card.SCARD_S_SUCCESS) 405 | { 406 | connActive = false; 407 | return false; 408 | } 409 | else 410 | return true; 411 | } 412 | public void Disconnect() 413 | { 414 | if (connActive) 415 | { 416 | retCode = Card.SCardDisconnect(hCard, Card.SCARD_UNPOWER_CARD); 417 | } 418 | //retCode = Card.SCardReleaseContext(hCard); 419 | } 420 | public string Transmit(byte[] buff) 421 | { 422 | string tmpStr = ""; 423 | int indx; 424 | 425 | ClearBuffers(); 426 | 427 | for (int i = 0; i < buff.Length; i++) 428 | { 429 | SendBuff[i] = buff[i]; 430 | } 431 | SendLen = 5; 432 | RecvLen = SendBuff[SendBuff.Length - 1] + 2; 433 | 434 | retCode = SendAPDUandDisplay(2); 435 | 436 | 437 | // Display data in text format 438 | for (indx = 0; indx <= RecvLen - 1; indx++) 439 | { 440 | tmpStr = tmpStr + Convert.ToChar(RecvBuff[indx]); 441 | } 442 | 443 | return tmpStr; 444 | } 445 | public void Watch() 446 | { 447 | this.RdrState = new Card.SCARD_READERSTATE(); 448 | readername = GetReadersList()[0]; 449 | this.RdrState.RdrName = readername; 450 | 451 | states = new Card.SCARD_READERSTATE[1]; 452 | states[0] = new Card.SCARD_READERSTATE(); 453 | states[0].RdrName = readername; 454 | states[0].UserData = 0; 455 | states[0].RdrCurrState = Card.SCARD_STATE_EMPTY; 456 | states[0].RdrEventState = 0; 457 | states[0].ATRLength = 0; 458 | states[0].ATRValue = null; 459 | this._worker = new BackgroundWorker(); 460 | this._worker.WorkerSupportsCancellation = true; 461 | this._worker.DoWork += WaitChangeStatus; 462 | this._worker.RunWorkerAsync(); 463 | } 464 | public NFCReader() 465 | { 466 | } 467 | } 468 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # NfcReader 2 | A simple library that provides to use rfid card readers. 3 | 4 | # Usage 5 | Basic usage of the library are provided. I recommend you using events which are very helpful. 6 | 7 | ## Connection 8 | 9 | ```csharp 10 | //Initializing 11 | NFCReader NFC = new NFCReader(); 12 | 13 | //Connecting 14 | NFC.Connect(); // public bool Connect() 15 | 16 | //Disconnecting 17 | NFC.Disconnect(); // public void Disconnect() 18 | ``` 19 | ```csharp 20 | //Inserted Event 21 | NFC.CardInserted += new NFCReader.CardEventHandler(...Some function); 22 | 23 | //Ejected Event 24 | NFC.CardEjected += new NFCReader.CardEventHandler(... Some function); 25 | 26 | //Enabling Event Watching 27 | NFC.Watch(); //public void Watch() 28 | ``` 29 | 30 | ## Read, Write Authorize 31 | 32 | ```csharp 33 | //Authorizing(which is done automatically by the read and write functions) 34 | NFC.AuthBlock("2"); // private bool AuthBlock(String block) 35 | 36 | //Reading 37 | NFC.ReadBlock("2"); //public byte[] ReadBlock(String Block) 38 | 39 | //Writing 40 | NFC.WriteBlock("Some string data that will not exceed block limit", "2"); //public bool WriteBlock(String Text, String Block) 41 | ``` 42 | ## ReaderList, CardUID 43 | 44 | ```csharp 45 | //Card UID 46 | NFC.GetCardUID(); 47 | 48 | //Available Readers 49 | NFC.GetReadersList(); //public List GetReadersList() 50 | ``` 51 | 52 | ## Example Inserted and Ejected Event Usage 53 | ```csharp 54 | public void Card_Inserted() 55 | { 56 | try 57 | { 58 | if (NFC.Connect()) 59 | { 60 | //Do stuff like NFC.GetCardUID(); ... 61 | } 62 | else 63 | { 64 | //Give error message about connection... 65 | } 66 | } 67 | catch (Exception ex) 68 | { 69 | this.SetStatusText("Hata: Bir Sorun Oluştu Tekrar Deneyiniz",false); 70 | } 71 | } 72 | ``` 73 | 74 | ```csharp 75 | public void Card_Ejected() 76 | { 77 | //Do stuff... 78 | NFC.Disconnect(); 79 | } 80 | ``` 81 | --------------------------------------------------------------------------------