在应用之间共享证书

需要超出用户 ID 和密码组合的安全身份验证的通用 Windows 平台(UWP)应用可以使用证书进行身份验证。 对用户进行身份验证时,证书身份验证提供高级别的信任。 在某些情况下,一组服务需要对多个应用的用户进行身份验证。 本文介绍如何使用相同的证书对多个应用进行身份验证,以及如何为用户提供方便的代码来导入提供的用于访问安全 Web 服务的证书。

应用可以使用证书向 Web 服务进行身份验证,多个应用可以使用证书存储中的单个证书对同一用户进行身份验证。 如果存储中不存在证书,则可以将代码添加到应用,以便从 PFX 文件导入证书。

启用Microsoft Internet Information Services(IIS)和客户端证书映射

本文使用 Microsoft Internet Information Services (IIS) 作为示例。 默认情况下,IIS 未启用。 可以使用控制面板启用 IIS。

  1. 打开控制面板并选择“ 程序”。
  2. 选择 打开或关闭的 Windows 功能。
  3. 展开 Internet Information Services,然后展开 万维网服务。 展开 应用程序开发功能 并选择 ASP.NET 3.5ASP.NET 4.5。 进行这些选择将自动启用 Internet Information Services
  4. 单击 确定 以应用更改。

创建和发布受保护的 Web 服务

  1. 以管理员身份运行 Microsoft Visual Studio,并从起始页中选择 “新建项目 ”。 需要管理员访问权限才能将 Web 服务发布到 IIS 服务器。 在“新建项目”对话框中,将框架更改为 .NET Framework 3.5。 选择 Visual C# ->Web ->Visual Studio ->ASP.NET Web 服务应用程序。 将应用程序命名为“FirstContosoBank”。 单击 确定 以创建项目。

  2. Service1.asmx.cs 文件中,将默认 HelloWorld Web 方法替换为以下“Login”方法。

            [WebMethod]
            public string Login()
            {
                // Verify certificate with CA
                var cert = new System.Security.Cryptography.X509Certificates.X509Certificate2(
                    this.Context.Request.ClientCertificate.Certificate);
                bool test = cert.Verify();
                return test.ToString();
            }
    
  3. 保存 Service1.asmx.cs 文件。

  4. 解决方案资源管理器中,右键单击“FirstContosoBank”应用,然后选择 “发布”

  5. 在“发布 Web”对话框中,创建新的配置文件并将其命名为“ContosoProfile”。 单击“下一步”。

  6. 在下一页上,输入 IIS 服务器的服务器名称,并指定网站名称“默认网站/FirstContosoBank”。 单击 发布 您的 Web 服务。

将 Web 服务配置为使用客户端证书身份验证

  1. 运行 Internet Information Services (IIS) 管理器
  2. 扩展您的 IIS 服务器站点。 在 “默认网站”下,选择新的“FirstContosoBank”Web 服务。 在“操作”部分中,选择“高级设置...”
  3. 应用程序池 设置为 .NET v2.0 ,然后单击“ 确定”。
  4. Internet Information Services (IIS) 管理器中,选择 IIS 服务器,然后双击 “服务器证书”。 在 “作 ”部分中,选择“ 创建 Self-Signed 证书...”。输入“ContosoBank”作为证书的友好名称,然后单击“ 确定”。 这将创建一个新的证书,供 IIS 服务器使用,格式为“<服务器名称>.<域名>”。
  5. Internet Information Services (IIS) 管理器中,选择默认网站。 在 “作 ”部分中,选择“ 绑定 ”,然后单击“ 添加...”。选择“https”作为类型,将端口设置为“443”,然后输入 IIS 服务器的完整主机名(“<server-name>”。<domain-name>”。 将 SSL 证书设置为“ContosoBank”。 单击 “确定” 。 在 网站绑定 窗口中单击 关闭
  6. Internet Information Services (IIS) 管理器中,选择“FirstContosoBank”Web 服务。 双击 SSL 设置。 检查 是否需要 SSL。 在 客户端证书下,选择 需要。 在 操作 部分中,单击 “应用”
  7. 可以通过打开 Web 浏览器并输入以下 Web 地址来验证 Web 服务是否已正确配置:“https://< server-name>。<domain-name>/FirstContosoBank/Service1.asmx”。 例如,"https://myserver.example.com/FirstContosoBank/Service1.asmx"。 如果 Web 服务配置正确,系统会提示你选择客户端证书以访问 Web 服务。

可以重复前面的步骤来创建多个可以使用同一客户端证书访问的 Web 服务。

创建使用证书身份验证的 UWP 应用

拥有一个或多个受保护的 Web 服务后,应用可以使用证书对这些 Web 服务进行身份验证。 使用 HttpClient 对象向经过身份验证的 Web 服务发出请求时,初始请求将不包含客户端证书。 经过身份验证的 Web 服务将使用客户端身份验证请求进行响应。 发生这种情况时,Windows 客户端将自动查询证书存储中可用的客户端证书。 用户可以从这些证书中进行选择,以向 Web 服务进行身份验证。 某些证书受密码保护,因此需要为用户提供输入证书密码的方法。

如果没有可用的客户端证书,则用户需要将证书添加到证书存储。 可以在应用中包括代码,使用户能够选择包含客户端证书的 PFX 文件,然后将该证书导入到客户端证书存储中。

提示 您可以使用 makecert.exe 创建一个用于此快速启动的 PFX 文件。 关于如何使用 makecert.exe的信息,请参阅 MakeCert。

 

  1. 打开 Visual Studio 并从起始页创建新项目。 将新项目命名为“FirstContosoBankApp”。 单击确定以创建新项目。

  2. 在 MainPage.xaml 文件中,将以下 XAML 添加到默认 Grid 元素。 此 XAML 包括用于浏览要导入的 PFX 文件的按钮、用于输入受密码保护的 PFX 文件的密码的文本框、用于导入所选 PFX 文件的按钮、用于登录到受保护 Web 服务的按钮,以及用于显示当前作状态的文本块。

    <Button x:Name="Import" Content="Import Certificate (PFX file)" HorizontalAlignment="Left" Margin="352,305,0,0" VerticalAlignment="Top" Height="77" Width="260" Click="Import_Click" FontSize="16"/>
    <Button x:Name="Login" Content="Login" HorizontalAlignment="Left" Margin="611,305,0,0" VerticalAlignment="Top" Height="75" Width="240" Click="Login_Click" FontSize="16"/>
    <TextBlock x:Name="Result" HorizontalAlignment="Left" Margin="355,398,0,0" TextWrapping="Wrap" VerticalAlignment="Top" Height="153" Width="560"/>
    <PasswordBox x:Name="PfxPassword" HorizontalAlignment="Left" Margin="483,271,0,0" VerticalAlignment="Top" Width="229"/>
    <TextBlock HorizontalAlignment="Left" Margin="355,271,0,0" TextWrapping="Wrap" Text="PFX password" VerticalAlignment="Top" FontSize="18" Height="32" Width="123"/>
    <Button x:Name="Browse" Content="Browse for PFX file" HorizontalAlignment="Left" Margin="352,189,0,0" VerticalAlignment="Top" Click="Browse_Click" Width="499" Height="68" FontSize="16"/>
    <TextBlock HorizontalAlignment="Left" Margin="717,271,0,0" TextWrapping="Wrap" Text="(Optional)" VerticalAlignment="Top" Height="32" Width="83" FontSize="16"/>
    
  3. 保存 MainPage.xaml 文件。

  4. 在“MainPage.xaml.cs”文件中,添加以下 using 语句。

    using Windows.Web.Http;
    using System.Text;
    using Windows.Security.Cryptography.Certificates;
    using Windows.Storage.Pickers;
    using Windows.Storage;
    using Windows.Storage.Streams;
    
  5. 在 MainPage.xaml.cs 文件中,将以下变量添加到 MainPage 类中。 它们指定“FirstContosoBank”Web 服务的受保护“Login”方法的地址,以及保存要导入到证书存储中的 PFX 证书的全局变量。 将 <服务器名称> 更新为 Microsoft Internet Information Server (IIS) 服务器的完全限定服务器名称。

    private Uri requestUri = new Uri("https://<server-name>/FirstContosoBank/Service1.asmx?op=Login");
    private string pfxCert = null;
    
  6. 在MainPage.xaml.cs文件中,为登录按钮和访问安全 Web 服务的方法添加以下单击处理程序。

    private void Login_Click(object sender, RoutedEventArgs e)
    {
        MakeHttpsCall();
    }
    
    private async void MakeHttpsCall()
    {
    
        StringBuilder result = new StringBuilder("Login ");
        HttpResponseMessage response;
        try
        {
            Windows.Web.Http.HttpClient httpClient = new Windows.Web.Http.HttpClient();
            response = await httpClient.GetAsync(requestUri);
            if (response.StatusCode == HttpStatusCode.Ok)
            {
                result.Append("successful");
            }
            else
            {
                result = result.Append("failed with ");
                result = result.Append(response.StatusCode);
            }
        }
        catch (Exception ex)
        {
            result = result.Append("failed with ");
            result = result.Append(ex.Message);
        }
    
        Result.Text = result.ToString();
    }
    
  7. 在 MainPage.xaml.cs 文件中,添加以下单击处理程序:一个用于浏览 PFX 文件的按钮,以及一个用于将所选 PFX 文件导入证书存储的按钮。

    private async void Import_Click(object sender, RoutedEventArgs e)
    {
        try
        {
            Result.Text = "Importing selected certificate into user certificate store....";
            await CertificateEnrollmentManager.UserCertificateEnrollmentManager.ImportPfxDataAsync(
                pfxCert,
                PfxPassword.Password,
                ExportOption.Exportable,
                KeyProtectionLevel.NoConsent,
                InstallOptions.DeleteExpired,
                "Import Pfx");
    
            Result.Text = "Certificate import succeeded";
        }
        catch (Exception ex)
        {
            Result.Text = "Certificate import failed with " + ex.Message;
        }
    }
    
    private async void Browse_Click(object sender, RoutedEventArgs e)
    {
    
        StringBuilder result = new StringBuilder("Pfx file selection ");
        FileOpenPicker pfxFilePicker = new FileOpenPicker();
        pfxFilePicker.FileTypeFilter.Add(".pfx");
        pfxFilePicker.CommitButtonText = "Open";
        try
        {
            StorageFile pfxFile = await pfxFilePicker.PickSingleFileAsync();
            if (pfxFile != null)
            {
                IBuffer buffer = await FileIO.ReadBufferAsync(pfxFile);
                using (DataReader dataReader = DataReader.FromBuffer(buffer))
                {
                    byte[] bytes = new byte[buffer.Length];
                    dataReader.ReadBytes(bytes);
                    pfxCert = System.Convert.ToBase64String(bytes);
                    PfxPassword.Password = string.Empty;
                    result.Append("succeeded");
                }
            }
            else
            {
                result.Append("failed");
            }
        }
        catch (Exception ex)
        {
            result.Append("failed with ");
            result.Append(ex.Message); ;
        }
    
        Result.Text = result.ToString();
    }
    
  8. 运行应用并登录到受保护的 Web 服务,并将 PFX 文件导入本地证书存储。

可以使用这些步骤创建多个应用,这些应用使用相同的用户证书来访问相同或不同的安全 Web 服务。