/// <summary> /// Initializes the static elements of a 'WebTransactionProtocol' connection. /// </summary> static HttpBatchProcessor() { // We need to reference the user preferences in this static constructor. UserPreferences userPreferences = new UserPreferences(); // Use a default timout for the Http Request if one was specified in the application configuration file. string timeoutText = ConfigurationManager.AppSettings["httpTimeout"]; HttpBatchProcessor.Timeout = timeoutText == null ? defaultTimeout : Convert.ToInt32(timeoutText); // Initially, the decision about prompting for a URL is decided by whether there is a recently saved URL to try. // After the first WebException, we'll prompt them until a good one is entered. HttpBatchProcessor.Url = (string)UserPreferences.LocalSettings[webServiceMru0Key]; HttpBatchProcessor.IsUrlPrompted = HttpBatchProcessor.Url == string.Empty; // Initially the decision to prompt the user for credentials is made by whether a complete set of information for // establishing a session can be collected from the local settings. If there isn't enough information, the user will // be prompted. After the first time, a 401 error will indicate that the user should be prompted for their credentials // again. HttpBatchProcessor.IsCredentialPrompted = false; // Select the method for authenticating the user when they connect to the web transaction server. The default is to // use certificates. This can be overridden by configuration settings. HttpBatchProcessor.AuthenticationMode = AuthenticationMode.Certificate; string authenticationModeText = ConfigurationManager.AppSettings["authenticationMode"]; if (authenticationModeText != null) { HttpBatchProcessor.AuthenticationMode = (AuthenticationMode)Enum.Parse(typeof(AuthenticationMode), authenticationModeText); } // Set up the details of authentication based on the selected authentication mode. switch (HttpBatchProcessor.AuthenticationMode) { case AuthenticationMode.Certificate: // This is a collection of cached certificates used for SSL security. HttpBatchProcessor.clientCertificates = new X509CertificateCollection(); // If a certificate name was stored in the user preferences, then attempt to match it up against the personal // certificate store. If a previosly used certificate was found, then put it in the cache where it can be used the // next time a connection is established. string certificateName = (string)UserPreferences.LocalSettings[certificateNameKey]; if (certificateName != null) { foreach (X509Certificate x509Certificate in Crypt.GetCertificateStore(myStoreName)) { if (x509Certificate.Subject == certificateName) { HttpBatchProcessor.clientCertificates.Add(x509Certificate); } } } // If the user preferences has a valid certificate, then the prompting can be bypassed. Otherwise, the first time // a request is sent, the user will be asked for a URL and a certificate. HttpBatchProcessor.IsCredentialPrompted = HttpBatchProcessor.clientCertificates.Count == 0; break; case AuthenticationMode.WindowsIntegrated: // Integrated Mode uses the current user's security context, there is nothing for which to prompt the user. HttpBatchProcessor.IsCredentialPrompted = false; break; case AuthenticationMode.Basic: // Basic authorization over SSL can be used for the web request. See if the user has some saved credentials. HttpBatchProcessor.networkCredential = new NetworkCredential(); HttpBatchProcessor.networkCredential.UserName = (string)UserPreferences.LocalSettings[usernameKey]; HttpBatchProcessor.networkCredential.Domain = (string)UserPreferences.LocalSettings[domainKey]; // The user must always be prompted for the password in Basic Mode due to an inability to secure the password // on the local machine. HttpBatchProcessor.IsCredentialPrompted = true; break; } // Since the UserPreferences were used by a static non-component, the resources need to be cleaned up explicitly. This // should balance the books on the UserPreferences component. userPreferences.Dispose(); }
/// <summary> /// Constructs an HttpWebRequest specifically destined for the transaction server. /// </summary> private static HttpWebRequest CreateCertificateRequest() { // This section of code is thread safe. Since the user can modify the URL or the credentials of other threads that // want to use this class, the class itself is locked while the user is prompted (if prompting is necessary). lock (typeof(HttpBatchProcessor)) { // Get the URL. GetUrl(); // If the WebTransactionProtocol has successfully connected once, then try to use the URL and Certificate from the // previous connection for this request. if (HttpBatchProcessor.IsCredentialPrompted) { // A Certificate is also required for SSL communication. A cache of certificates is kept by this class to speed up // the task of connecting to the server. Since the URL has changed at this point, none of the previous // certificates that were selected by the user should be used. HttpBatchProcessor.clientCertificates.Clear(); // The design goal of the certificate selection sequence is to limit the number of decisions that the user has to // make. The personal certficate store is used to select a certificate for the session. If the name on the // certificate matches the name stored in the user preferences, then that certificate is used. X509CertificateCollection x509CertificateCollection = Crypt.GetCertificateStore(myStoreName); // If there is only one certificate in the personal store, then use it without any prompting. Otherwise, ask the // user to select a certificate. if (x509CertificateCollection.Count == 1) { HttpBatchProcessor.clientCertificates.Add(x509CertificateCollection[0]); } else { // Only prompt for a certificate if there are more than one to choose from. if (x509CertificateCollection.Count != 0) { // Prompt the user for a certificate. Note that if they don't select a certificate an exception is throw // that will terminate the application if not caught. Generally, the application should be allowed to exit // if a secure connection with the server can't be established. FormSelectCertificate formSelectCertificate = new FormSelectCertificate(); formSelectCertificate.ShowDialog(); if (formSelectCertificate.DialogResult != DialogResult.OK) { throw new UserAbortException("User cancelled the operation"); } // Add this certificate to the client's collection. HttpBatchProcessor.clientCertificates.Add(formSelectCertificate.SelectedCertificate); // There's no need to prompt again until some error forces the user back here. HttpBatchProcessor.IsCredentialPrompted = false; // The form must be destroyed explicitly or the components will hang around until the garbage is collected. formSelectCertificate.Dispose(); } } } } // Create and initialize an HttpWebRequest to handle the conversation between the client and server. All Batch // operations are handled using the 'POST' verb. The timeout is specified above though the configuration file or a // programtic setting. Also note that the Stream Buffering is disabled as it appears to have a negative impact on // throughput. Finally, the chosen certificate is tacked onto the outgoing request for authentication. HttpWebRequest httpWebRequest = (HttpWebRequest)HttpWebRequest.Create(HttpBatchProcessor.Url); httpWebRequest.Method = "POST"; httpWebRequest.Timeout = HttpBatchProcessor.Timeout; httpWebRequest.AllowWriteStreamBuffering = true; httpWebRequest.ClientCertificates.AddRange(HttpBatchProcessor.clientCertificates); // This request can be used to send a 'Batch' object to the Web Transaction server. return(httpWebRequest); }