/// <summary> /// Locate the smartcard type with the identified ATR bytes /// </summary> /// <param name="atr">ATR byte array</param> /// <returns>Cardname matching the ATR</returns> private string FindCardWithAtr(byte[] atr) { var cardLen = 1024; var cards = new char[cardLen]; var result = SmartcardInterop.SCardListCardsW(IntPtr.Zero, atr, IntPtr.Zero, 0, cards, out cardLen); if (result != SmartcardException.SCardSuccess) { this.logger.DebugFormat("Failed to find smartcard by ATR: error 0x{0}", result); throw new SmartcardException(result); } var card = SmartcardInterop.MultiStringToArray(cards); if (card.Count == 0) { throw new SmartcardException(SmartcardException.SCardECardUnsupported); } if (card.Count > 1) { throw new SmartcardException(SmartcardException.SCardEUnexpected); } return(card[0]); }
/// <summary> /// Find all supported smartcard types /// </summary> /// <returns>List of supported card types</returns> private List <string> FetchCards() { var cardLen = 16384; var cards = new char[cardLen]; var result = SmartcardInterop.SCardListCardsW(this.readerContext, null, IntPtr.Zero, 0, cards, out cardLen); if (result != SmartcardException.SCardSuccess) { this.logger.ErrorFormat("Failed to fetch smartcard names, result = 0x{0:X}", result); throw new SmartcardException(result); } var c = SmartcardInterop.MultiStringToArray(cards); if (this.logger.IsDebugEnabled) { this.logger.DebugFormat("Known cards; count = {0}", c.Count); foreach (var card in c) { this.logger.DebugFormat(" {0}", card); } } return(c.ToList()); }
/// <summary> /// Find all attached smartcard readers /// </summary> /// <returns>List of attached readers</returns> private List <string> FetchReaders() { var readerLen = 1024; var readers = new char[readerLen]; var result = SmartcardInterop.SCardListReadersW(this.readerContext, null, readers, out readerLen); if (result != SmartcardException.SCardSuccess && result != SmartcardException.SCardENoReadersAvailable) { this.logger.ErrorFormat("Failed to fetch smartcard readers, result = 0x{0:X}", result); throw new SmartcardException(result); } var r = SmartcardInterop.MultiStringToArray(readers); if (this.logger.IsDebugEnabled) { this.logger.DebugFormat("Known readers; count = {0}", r.Count); foreach (var reader in r) { this.logger.DebugFormat(" {0}", reader); } } return(r.ToList()); }
/// <summary> /// Unlock the smartcard /// </summary> /// <param name="pin">PIN to unlock the card</param> /// <exception cref="SmartcardException">Exception thrown on unlock error</exception> public void UnlockCard(string pin) { var pinBytes = Encoding.ASCII.GetBytes(pin); var success = SmartcardInterop.CryptSetProvParam(this.cardContext, SmartcardInterop.ProviderParamSet.KeyExchangePin, pinBytes, 0); if (!success) { var err = Marshal.GetLastWin32Error(); this.logger.DebugFormat("Failed to unlock smartcard; error 0x{0:X}", err); throw new SmartcardException(err); } }
/// <summary> /// Stop the smartcard monitor /// </summary> private void StopMonitoring() { this.logger.Debug("Stopping monitoring"); if (this.monitorTask == null) { return; } this.logger.Debug("Cancelling requests"); SmartcardInterop.SCardCancel(this.readerContext); this.monitorTask.Wait(); this.monitorTask = null; }
/// <summary> /// Free unmanaged resources /// </summary> /// <param name="disposing">Called from Dispose() flag</param> public void Dispose(bool disposing) { if (this.disposedValue) { return; } if (this.readerContext != IntPtr.Zero) { SmartcardInterop.SCardReleaseContext(this.readerContext); } this.disposedValue = true; }
/// <summary> /// Fetch the state of named smartcard readers /// </summary> /// <param name="readers">List of readers for which to fetch state</param> /// <returns>Smartcard reader state</returns> private List <SmartcardInterop.ScardReaderState> FetchState(IList <string> readers) { IList <SmartcardInterop.ScardReaderState> scardstatelist = null; if (this.currentState == null) { this.currentState = new List <SmartcardInterop.ScardReaderState>(); } var state = this.currentState.Where(x => readers.Contains(x.reader)).ToList(); if (state.Count == 0) { scardstatelist = this.CreatePendingReaderState(readers); } if (scardstatelist == null) { return(null); } if (readers.Count != scardstatelist.Count) { foreach (var reader in readers) { if (!scardstatelist.Any(x => x.reader.Equals(reader))) { scardstatelist.Add(this.CreatePendingReaderState(reader)); } } } var scardstate = scardstatelist.ToArray(); if (readers.Count > 0) { var d = SmartcardInterop.ArrayToMultiString(this.cardNames); var result = SmartcardInterop.SCardLocateCards(this.readerContext, d, scardstate, scardstate.Length); if (result != SmartcardException.SCardSuccess) { this.logger.ErrorFormat("Failed to fetch smartcard state, result = 0x{0:X}", result); throw new SmartcardException(result); } } return(scardstate.ToList()); }
/// <summary> /// Get the smartcard crypto provider driver ID /// </summary> /// <param name="cardname">Card type name for which to identify the crypto provider</param> /// <returns>Crypto provider identity</returns> private string GetSmartcardCryptoProvider(string cardname) { var provider = new StringBuilder(); var len = 256; provider.EnsureCapacity(len); var result = SmartcardInterop.SCardGetCardTypeProviderNameW(IntPtr.Zero, cardname, SmartcardInterop.Provider.Csp, provider, out len); if (result != SmartcardException.SCardSuccess) { var err = Marshal.GetLastWin32Error(); this.logger.DebugFormat("Failed to obtain smartcard provider; error 0x{0:X}", err); throw new SmartcardException(err); } return(provider.ToString()); }
/// <summary> /// Create a new smartcard tracking context /// </summary> private void ResetContext() { this.logger.Debug("Resetting scard monitor context"); if (this.readerContext != IntPtr.Zero) { SmartcardInterop.SCardReleaseContext(this.readerContext); } var result = SmartcardInterop.SCardEstablishContext(SmartcardInterop.Scope.User, IntPtr.Zero, IntPtr.Zero, out this.readerContext); if (result != SmartcardException.SCardSuccess) { this.logger.ErrorFormat("Failed to create smartcard context, result = 0x{0:X}", result); throw new SmartcardException(result); } this.cardNames = this.FetchCards(); this.readerNames = this.FetchReaders(); this.currentState = this.FetchState(this.readerNames); }
/// <summary> /// Dispose this class, release smartcard context /// </summary> /// <param name="disposing">Called from dispose()</param> public void Dispose(bool disposing) { if (!this.disposedValue) { if (disposing) { } if (this.certStoreHandle != IntPtr.Zero) { SmartcardInterop.CertCloseStore(this.certStoreHandle, 0); this.certStoreHandle = IntPtr.Zero; this.certStore = null; } if (this.cardContext != IntPtr.Zero) { SmartcardInterop.CryptReleaseContext(this.cardContext, 0); this.cardContext = IntPtr.Zero; } this.disposedValue = true; } }
/// <summary> /// Monitor for smartcard change events and fire events on detected changes /// </summary> private void WaitForReaderStateChange() { var awaitNewReader = new SmartcardInterop.ScardReaderState { reader = NotificationReader, currentState = 0, eventState = 0, atrLength = 0 }; this.currentState.Add(awaitNewReader); var i = 0; var result = 0; while (!this.cancelToken.IsCancellationRequested) { try { i++; var scardstate = this.currentState.ToArray(); this.logger.DebugFormat("StateChange - start: {0}; last status = 0x{1}; statecount = {2}", i, result.ToString("X"), scardstate.Length); // We have a 30 second timeout rather than infinite. As of Windows 8, the smartcard // service shuts down if no readers attached and more than a couple of minutes have // elapsed since the last SCard API call. So a 30 second timeout will keep the service // alive and mean we're not reseting context (and thus restarting smartcard service) // every couple of minutes. result = SmartcardInterop.SCardGetStatusChange(this.readerContext, 30000, scardstate, scardstate.Length); if (this.cancelToken.IsCancellationRequested || result == SmartcardException.SCardECancelled) { this.logger.Debug("Cancellation requested"); break; } if (result == SmartcardException.SCardETimeout) { continue; } this.logger.Debug("StateChange - result: 0x" + result.ToString("X")); var scardstatelist = scardstate.ToList(); // If the service has stopped, then we need to flag all existing cards as removed if (this.HandleStoppedService(scardstatelist, result)) { // Need to reset the smartcard context this.ResetContext(); this.currentState.Add(awaitNewReader); continue; } // All other errors, throw an exception if (result != SmartcardException.SCardSuccess) { this.logger.DebugFormat("Failed to get smartcard state: error 0x{0}", result); throw new SmartcardException(result); } // Now deal with the actual smartcard changes var scardChanges = scardstatelist.Where(x => (x.eventState & SmartcardInterop.State.Changed) != 0).ToList(); if (scardChanges.Count == 0) { continue; } this.DumpState(scardChanges); this.HandleRemovedReaders(scardChanges, result); this.HandleRemovedCards(scardChanges, result); this.HandleInsertedCards(ref scardChanges, result); this.SaveState(scardChanges); } catch (SmartcardException ex) { if (ex.Status == SmartcardException.SCardEServiceStopped || ex.Status == SmartcardException.SCardENoService) { this.logger.Debug("Smartcard service stopped; resetting context"); this.ResetContext(); this.currentState.Add(awaitNewReader); } else { throw; } } } this.logger.Debug("Monitoring stopped."); }