In my last post, I described a rather tedious and error prone method for configuring One To One client certificate mappings in IIS 7.5, so with a little bit of help I created a small WPF application that should ensure the smooth configuration of one to one client certificate mappings.
There is no validation as this is simply a developer tool and I'm too lazy to add it, you can have a look at this post for an example of textbox validation in WPF.
Xaml
<Window x:Class="IISCERTTOOL.MainWindow" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" Title="MainWindow" Height="302" Width="477"> <Grid> <Button Content="Add One to One Certificate Mapping" Height="23" HorizontalAlignment="Left" Margin="242,212,0,0" Name="Add" VerticalAlignment="Top" Width="191" Click="Add_Click" /> <Label Content="UserName" Height="28" HorizontalAlignment="Left" Margin="46,50,0,0" Name="label1" VerticalAlignment="Top" Width="81" /> <TextBox Height="23" HorizontalAlignment="Left" Margin="163,50,0,0" Name="UserName" VerticalAlignment="Top" Width="241" /> <Label Content="Password" Height="28" HorizontalAlignment="Left" Margin="46,84,0,0" Name="label2" VerticalAlignment="Top" Width="81" /> <PasswordBox Height="23" HorizontalAlignment="Left" Margin="163,89,0,0" Name="Password" VerticalAlignment="Top" Width="241" /> <Label Content="Certificate" Height="28" HorizontalAlignment="Left" Margin="46,118,0,0" Name="label3" VerticalAlignment="Top" Width="81" /> <TextBox Height="23" HorizontalAlignment="Left" Margin="163,123,0,0" Name="Certificate" VerticalAlignment="Top" Width="241" /> <Label Content="WebSite" Height="28" HorizontalAlignment="Left" Margin="46,152,0,0" Name="label4" VerticalAlignment="Top" Width="81" /> <ComboBox Height="23" HorizontalAlignment="Left" Margin="163,152,0,0" Name="WebSite" VerticalAlignment="Top" Width="241" /> <Button Content="..." Height="20" HorizontalAlignment="Left" Margin="416,123,0,0" Name="SelectCertificate" VerticalAlignment="Top" Width="17" FontStyle="Normal" Click="SelectCertificate_Click" /> </Grid> </Window>
Code behind.
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Windows; using System.Windows.Controls; using System.Windows.Data; using System.Windows.Documents; using System.Windows.Input; using System.Windows.Media; using System.Windows.Media.Imaging; using System.Windows.Navigation; using System.Windows.Shapes; using Microsoft.Web.Administration; using System.IO; using System.Diagnostics; namespace IISCERTTOOL { /// <summary> /// Interaction logic for MainWindow.xaml /// </summary> public partial class MainWindow : Window { public MainWindow() { InitializeComponent(); LoadWebSites(); } private void SelectCertificate_Click(object sender, RoutedEventArgs e) { try { Microsoft.Win32.OpenFileDialog dialog = new Microsoft.Win32.OpenFileDialog(); dialog.AddExtension = true; dialog.CheckFileExists = true; dialog.Filter = "cer files (*.cer)|*.cer"; dialog.Multiselect = false; if ((bool)dialog.ShowDialog()) { Certificate.Text = dialog.FileName; } } catch (Exception ex) { DisplayException(ex); } } private void Add_Click(object sender, RoutedEventArgs e) { try { string publicKey = ReadKey(Certificate.Text.Trim()); if (!string.IsNullOrEmpty(publicKey) && ConfigureOneToOneCertMapping(UserName.Text.Trim(), Password.Password.Trim(), publicKey, WebSite.SelectedValue.ToString())) { MessageBox.Show("One to One Mapping successfully added"); } else { MessageBox.Show("One to One Mapping was not added"); } } catch (Exception ex) { DisplayException(ex); } } /// <summary> /// Read certificate file into a string /// </summary> private string ReadKey(string path) { StringBuilder publicKey = new StringBuilder(); try { string[] file = File.ReadAllLines(path).Skip(1).ToArray(); for (int i = 0; i < file.Length - 1; i++) { publicKey.Append(file[i]); } } catch (Exception ex) { DisplayException(ex); } return publicKey.ToString(); } /// <summary> /// Grab all Websites on the server and populate the dropdown combobox. /// </summary> private void LoadWebSites() { try { List<string> sites = new List<string>(); using (ServerManager serverManager = new ServerManager()) { foreach (Site s in serverManager.Sites) { sites.Add(s.Name); } } WebSite.ItemsSource = sites; } catch (Exception ex) { DisplayException(ex); } } /// <summary> /// Configure OneToOne Certificate Mapping for client authenticated SSL/TLS /// </summary> private bool ConfigureOneToOneCertMapping(string UserName, string Password, string PublicKey, string WebSiteName) { bool result = false; using (ServerManager serverManager = new ServerManager()) { try { Configuration config = serverManager.GetApplicationHostConfiguration(); ConfigurationSection iisClientCertificateMappingAuthenticationSection = config.GetSection("system.webServer/security/authentication/iisClientCertificateMappingAuthentication", WebSiteName); iisClientCertificateMappingAuthenticationSection["enabled"] = true; iisClientCertificateMappingAuthenticationSection["oneToOneCertificateMappingsEnabled"] = true; ConfigurationElementCollection oneToOneMappingsCollection = iisClientCertificateMappingAuthenticationSection.GetCollection("oneToOneMappings"); ConfigurationElement addElement = oneToOneMappingsCollection.CreateElement("add"); addElement["enabled"] = true; addElement["userName"] = UserName; addElement["password"] = Password; addElement["certificate"] = PublicKey; oneToOneMappingsCollection.Add(addElement); ConfigurationSection accessSection = config.GetSection("system.webServer/security/access", WebSiteName); accessSection["sslFlags"] = @"Ssl, SslNegotiateCert"; serverManager.CommitChanges(); result = true; } catch (Exception ex) { DisplayException(ex); } return result; } } /// <summary> /// Gets details of exception and displays them in a textbox. /// </summary> private static void DisplayException(Exception ex) { StringBuilder sb = new StringBuilder(); StackTrace trace = new StackTrace(); sb.AppendLine("Exception Occurred."); sb.AppendLine(string.Format("{0}.{1}", trace.GetFrame(1).GetMethod().ReflectedType.Name, trace.GetFrame(1).GetMethod().Name)); sb.AppendLine(string.Format("Source: {0}.", ex.Source)); sb.AppendLine(string.Format("Type: {0}.", ex.GetType())); sb.AppendLine(string.Format("Message: {0}.", ex.Message)); if (ex.InnerException != null) { sb.AppendLine(string.Format("Inner Exception: {0}.", ex.InnerException.Message)); } MessageBox.Show(sb.ToString()); } } }