Example #1
0
        /// <summary>
        /// Prompts the user for a certificate to authenticate that user on the channel.
        /// </summary>
        /// <param name="state">A generic parameter used to initialize the thread.</param>
        static void PromptForCertificate(CredentialAsyncResult credentialAsyncResult)
        {
            // This will select a list of valid certificates from the store that can be used for client authentication.
            X509Store store = new X509Store(CertificateChannelInitializer.myStoreName, StoreLocation.CurrentUser);

            store.Open(OpenFlags.ReadOnly | OpenFlags.OpenExistingOnly);
            X509Certificate2Collection clientCertificates = store.Certificates.Find(
                X509FindType.FindByApplicationPolicy,
                CertificateChannelInitializer.oidClientAuthentication.Value,
                true);

            // This dialog will prompt the user with a list of certificate that match the
            WindowCertificate windowCertificate = new WindowCertificate();

            foreach (X509Certificate2 x509Certificate2 in clientCertificates)
            {
                windowCertificate.X509Certificate2s.Add(x509Certificate2);
            }
            windowCertificate.X509Certificate2 = credentialAsyncResult.Credentials as X509Certificate2;
            if (windowCertificate.ShowDialog() == true)
            {
                credentialAsyncResult.IsCanceled  = false;
                credentialAsyncResult.Credentials = windowCertificate.X509Certificate2;
            }
            else
            {
                credentialAsyncResult.IsCanceled = true;
            }

            // If a valid callback was provided in the IAsyncResult structure then send a signal that we're through.
            if (credentialAsyncResult.AsyncCallback != null)
            {
                credentialAsyncResult.AsyncCallback(credentialAsyncResult);
            }
        }
        /// <summary>
        /// Completes the user interface that prompts a user for a certificate.
        /// </summary>
        /// <param name="result">Represents the status of an asynchrous operation.</param>
        public void EndDisplayInitializationUI(IAsyncResult result)
        {
            // This will extract the channel from the generic event argument.
            CredentialAsyncResult certificateAsyncResult = result as CredentialAsyncResult;

            // The user has the option to cancel the login operation.  If they cancel, the current operation will be aborted and further attempts to initialize a
            // channel will be suspended.  If not, an attempt is made to use the credentials to authorize communication with the web service.
            if (certificateAsyncResult.IsCanceled)
            {
                // This will generate an exception that the UI threads must handle. The non-UI threads should never have to deal with what is essentially a user
                // generated exception.
                certificateAsyncResult.IClientChannel.Abort();
            }
            else
            {
                // Copy the information out of the dialog box after it has been dismissed.  This information is shared between all channels using DomainUserName
                // authentication.
                this.Credentials = certificateAsyncResult.Credentials;
            }

            // There is no good way to pass the credentials back to the security token manager.  The MSDN help provides an example that uses the properties of the
            // channel to store the credentials, but that property doesn't exist on all communication stacks (in particular, the net.tcp stack).  So we'll use the
            // PromptedClientCredentials class which is acting as an intermediary between the channel and the security token manager anyway.
            this.promptedClientCredentials.Credentials = certificateAsyncResult.Credentials;
        }
Example #3
0
        /// <summary>
        /// Prompts the user for a certificate to authenticate that user on the channel.
        /// </summary>
        /// <param name="credentialAsyncResult">The CredentialAsyncResult that is called to complete the initialization.</param>
        static void PromptForUserName(CredentialAsyncResult credentialAsyncResult)
        {
            // This dialog will prompt the user with a list of certificate that match the
            WindowDistinguishedName windowBasic = new WindowDistinguishedName();

            windowBasic.DistinguishedNameCredential = credentialAsyncResult.Credentials as DistinguishedNameCredential;
            windowBasic.ServerName = credentialAsyncResult.IClientChannel.RemoteAddress.Uri.Host;

            // This will display the dialog and wait for the user to either accept the credentials or cancel out of the login.
            if (windowBasic.ShowDialog() == true)
            {
                credentialAsyncResult.IsCanceled  = false;
                credentialAsyncResult.Credentials = windowBasic.DistinguishedNameCredential;
            }
            else
            {
                credentialAsyncResult.IsCanceled = true;
            }

            // If a valid callback was provided in the IAsyncResult structure then send a signal that we're through.
            if (credentialAsyncResult.AsyncCallback != null)
            {
                credentialAsyncResult.AsyncCallback(credentialAsyncResult);
            }
        }
        /// <summary>
        /// Starts the user interface that prompts a user for a certificate.
        /// </summary>
        /// <param name="channel">The client channel.</param>
        /// <param name="callback">The callback object.</param>
        /// <param name="state">Any state data.</param>
        /// <returns>The System.IAsyncResult to use to call back when processing has completed.</returns>
        public IAsyncResult BeginDisplayInitializationUI(IClientChannel channel, AsyncCallback callback, Object state)
        {
            // Any worker thread can request a channel, so the user prompt needs to be invoked on a Single Threaded Apartment (STA) thread.  This will create that
            // thread and communicate to it the current state of the channel.
            CredentialAsyncResult credentialAsyncResult = new CredentialAsyncResult()
            {
                AsyncCallback  = callback,
                AsyncState     = state,
                Credentials    = this.Credentials,
                IClientChannel = channel
            };

            // Only single threaded apartments are allowed to call the user interface to prompt the user.  Background threads must wait until an apartment that can
            // prompt the user has validated the credentials.
            if (Thread.CurrentThread.GetApartmentState() == ApartmentState.STA)
            {
                // Only prompt the user once for their credentials.  After a set of credentials are chosen, they'll be used by all subsequent calls to use this
                // channel until the 'IsPrompted' flag is cleared.
                if (InteractiveChannelInitializer.IsPrompted)
                {
                    // This will prompt the user for their credentials.  It effectively creates a modal dialog box and prevents the other threads from executing,
                    // which effectively obviates the whole IAsyncResult strategy here, but this was the only way to come up with a consistent way to call the
                    // channels from the console or a Windows environment.  Note that the frame will only return control to this thread when we set the 'Continue'
                    // property to 'false'.
                    DispatcherFrame dispatcherFrame = new DispatcherFrame();
                    dispatcherFrame.Dispatcher.BeginInvoke(DispatcherPriority.Normal, this.Prompt, credentialAsyncResult);
                    dispatcherFrame.Dispatcher.BeginInvoke(DispatcherPriority.Normal, new Action(() => dispatcherFrame.Continue = false));
                    Dispatcher.PushFrame(dispatcherFrame);
                }
                else
                {
                    // If we have already prompted the user then return the credentials immediately.
                    credentialAsyncResult.Credentials = this.Credentials;
                    credentialAsyncResult.SetCompletedSynchronously();
                }
            }
            else
            {
                // The non-UI threads must wait here until a UI thread has validated the credentials.
                InteractiveChannelInitializer.ValidCredentials.WaitOne();

                // Setting the credentials in this way will make the channel act synchronously (that is, without prompting the user).  This is important after the
                // first time the channel has been initialized as we don't want to keep bother the user for credentials.
                credentialAsyncResult.Credentials = this.Credentials;
                credentialAsyncResult.SetCompletedSynchronously();
            }

            // This will be used by the caller to manage the asynchronous task that prompts the user and then invoke the EndDisplayInitializationUI with the results
            // of the dialog.
            return(credentialAsyncResult);
        }