/// <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());
        }
Ejemplo n.º 4
0
        /// <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());
        }
Ejemplo n.º 8
0
        /// <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);
        }
Ejemplo n.º 10
0
        /// <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.");
        }