Partilhar via


Compartilhar certificados entre aplicativos do Windows

Os aplicativos do Windows que exigem autenticação segura além de uma combinação de ID de usuário e senha podem usar certificados para autenticação. A autenticação de certificado oferece um elevado nível de confiança ao autenticar um usuário. Em alguns casos, um grupo de serviços desejará autenticar um usuário para vários aplicativos. Este artigo mostra como você pode autenticar vários aplicativos do Windows usando o mesmo certificado e como você pode fornecer um método para os usuários importarem um certificado que foi fornecido para acesso a serviços Web protegidos.

Os aplicativos podem se autenticar em um serviço Web usando um certificado e vários aplicativos podem usar um único certificado do repositório de certificados para autenticar o mesmo usuário. Se um certificado não existir no repositório, você poderá adicionar código ao seu aplicativo para importar um certificado de um arquivo PFX. O aplicativo cliente neste início rápido é um aplicativo WinUI e o serviço Web é uma API Web ASP.NET Core.

Dica

O Microsoft Copilot é um ótimo recurso se você tiver dúvidas sobre como começar a escrever aplicativos do Windows ou APIs Web do ASP.NET Core. O Copilot pode ajudá-lo a escrever código, encontrar exemplos e aprender mais sobre as práticas recomendadas para criar aplicativos seguros.

Pré-requisitos

  • Visual Studio com as cargas de trabalho de desenvolvimento ASP.NET e Web e desenvolvimento de aplicativos Windows instaladas.
  • O SDK (Software Development Kit) mais recente para usar as APIs do WinRT (Tempo de Execução do Windows) em seu aplicativo WinUI.
  • PowerShell para trabalhar com certificados autoassinados.

Criar e publicar um serviço Web seguro

  1. Abra o Microsoft Visual Studio e selecione Criar um novo projeto na tela inicial.

  2. Na caixa de diálogo Criar um novo projeto, selecione API na lista suspensa Selecionar um tipo de projeto para filtrar os modelos de projeto disponíveis.

  3. Selecione o modelo API Web do ASP.NET Core e Avançar.

  4. Nomeie o aplicativo como "FirstContosoBank" e selecione Avançar.

  5. Escolha o .NET 8.0 ou posterior como a Estrutura, defina o Tipo de autenticação como Nenhum, verifique se Configurar para HTTPS está marcado, desmarque Habilitar suporte a OpenAPI, marque Não usar instruções de nível superior e Usar controladores e selecione Criar.

    Uma captura de tela do Visual Studio criar novos detalhes do projeto para o projeto de API Web do ASP.NET Core

  6. Clique com o botão direito do mouse no arquivo WeatherForecastController.cs na pasta Controladores e selecione Renomear. Altere o nome para BankController.cs e deixe o Visual Studio renomear a classe e todas as referências à classe.

  7. No arquivo launchSettings.json, altere o valor de "launchUrl" de "weatherforecast" para "bank" para todas as três configurações que usam o valor.

  8. No arquivo BankController.cs, adicione o seguinte método "Login".

    [HttpGet]
    [Route("login")]
    public string Login()
    {
        // Return any value you like here.
        // The client is just looking for a 200 OK response.
        return "true";
    }
    
  9. Abra o Gerenciador de Pacotes NuGet e pesquise e instale a versão estável mais recente do pacote Microsoft.AspNetCore.Authentication.Certificate . Este pacote fornece middleware para autenticação de certificado no ASP.NET Core.

  10. Adicione uma nova classe ao projeto chamada SecureCertificateValidationService. Adicione o código a seguir à classe para configurar o middleware de autenticação de certificado.

    using System.Security.Cryptography.X509Certificates;
    
    public class SecureCertificateValidationService
    {
        public bool ValidateCertificate(X509Certificate2 clientCertificate)
        {
            // Values are hard-coded for this example.
            // You should load your valid thumbprints from a secure location.
            string[] allowedThumbprints = { "YOUR_CERTIFICATE_THUMBPRINT_1", "YOUR_CERTIFICATE_THUMBPRINT_2" };
            if (allowedThumbprints.Contains(clientCertificate.Thumbprint))
            {
                return true;
            }
        }
    }
    
  11. Abra Program.cs e substitua o código no método Main pelo seguinte código:

    public static void Main(string[] args)
    {
        var builder = WebApplication.CreateBuilder(args);
    
        // Add our certificate validation service to the DI container.
        builder.Services.AddTransient<SecureCertificateValidationService>();
    
        builder.Services.Configure<KestrelServerOptions>(options =>
        {
            // Configure Kestrel to require a client certificate.
            options.ConfigureHttpsDefaults(options =>
            {
                options.ClientCertificateMode = ClientCertificateMode.RequireCertificate;
                options.AllowAnyClientCertificate();
            });
        });
    
        builder.Services.AddControllers();
    
        // Add certificate authentication middleware.
        builder.Services.AddAuthentication(
        CertificateAuthenticationDefaults.AuthenticationScheme)
           .AddCertificate(options =>
        {
            options.AllowedCertificateTypes = CertificateTypes.SelfSigned;
            options.Events = new CertificateAuthenticationEvents
            {
                // Validate the certificate with the validation service.
                OnCertificateValidated = context =>
                {
                    var validationService = context.HttpContext.RequestServices.GetService<SecureCertificateValidationService>();
    
                    if (validationService.ValidateCertificate(context.ClientCertificate))
                    {
                        context.Success();
                    }
                    else
                    {
                        context.Fail("Invalid certificate");
                    }
    
                    return Task.CompletedTask;
                },
                OnAuthenticationFailed = context =>
                {
                    context.Fail("Invalid certificate");
                    return Task.CompletedTask;
                }
            };
         });
    
         var app = builder.Build();
    
         // Add authentication/authorization middleware.
         app.UseHttpsRedirection();
         app.UseAuthentication();
         app.UseAuthorization();
    
         app.MapControllers();
         app.Run();
     }
    

    O código acima configura o servidor Kestrel para exigir um certificado de cliente e adiciona o middleware de autenticação de certificado ao aplicativo. O middleware valida o certificado do cliente usando a SecureCertificateValidationService classe. O OnCertificateValidated evento é chamado quando um certificado é validado. Se o certificado for válido, o evento chamará o Success método. Se o certificado for inválido, o evento chamará o Fail método com uma mensagem de erro, que será retornada ao cliente.

  12. Comece a depurar o projeto para iniciar o serviço Web. Você pode receber mensagens sobre como confiar e instalar um certificado SSL. Clique em Sim para cada uma dessas mensagens para confiar no certificado e continuar a depuração do projeto.

    Uma captura de tela de uma caixa de diálogo perguntando ao usuário se ele deseja confiar em um certificado

    Uma captura de tela de uma caixa de diálogo do Windows perguntando ao usuário se ele deseja instalar um certificado

  13. O serviço web estará disponível em https://localhost:7072/bank. Você pode testar o serviço Web abrindo um navegador da Web e inserindo o endereço Web. Você verá os dados de previsão do tempo gerados formatados como JSON. Mantenha o serviço Web em execução enquanto cria o aplicativo cliente.

Para obter mais informações sobre como trabalhar com APIs Web baseadas no controlador ASP.NET Core, consulte Criar uma API Web com ASP.NET Core.

Criar um aplicativo WinUI que usa autenticação de certificado

Agora que você tem um ou mais serviços Web protegidos, seus aplicativos podem usar certificados para se autenticar nesses serviços Web. Quando você faz uma solicitação para um serviço Web autenticado usando o objeto HttpClient das APIs do WinRT, a solicitação inicial não conterá um certificado de cliente. O serviço Web autenticado responderá com uma solicitação de autenticação do cliente. Quando isso ocorrer, o cliente Windows consultará automaticamente o repositório de certificados para obter os certificados de cliente disponíveis. Seu usuário pode selecionar entre esses certificados para autenticar no serviço Web. Alguns certificados são protegidos por senha, portanto, você precisará fornecer ao usuário uma maneira de inserir a senha de um certificado.

Observação

Ainda não há APIs SDK do Aplicativo Windows para gerenciar certificados. Você deve usar as APIs do WinRT para gerenciar certificados em seu aplicativo. Também usaremos APIs de armazenamento do WinRT para importar um certificado de um arquivo PFX. Muitas APIs do WinRT podem ser usadas por qualquer aplicativo do Windows com o identificador do pacote, incluindo aplicativos WinUI.

O código do cliente HTTP que implementaremos usa . NET. O HttpClient incluído nas APIs do WinRT não dá suporte a certificados de cliente.

Se não houver certificados de cliente disponíveis, o usuário precisará adicionar um certificado ao repositório de certificados. Você pode incluir código em seu aplicativo que permite que um usuário selecione um arquivo PFX que contenha um certificado de cliente e, em seguida, importe esse certificado para o repositório de certificados do cliente.

Dica

Você pode usar os cmdlets do PowerShell New-SelfSignedCertificate e Export-PfxCertificate para criar um certificado autoassinado e exportá-lo para um arquivo PFX para usar com este início rápido. Para obter informações, consulte New-SelfSignedCertificate e Export-PfxCertificate.

Observe que, ao gerar o certificado, você deve salvar a impressão digital do certificado para usar no serviço Web para validação.

  1. Abra o Visual Studio e crie um novo projeto WinUI na página inicial. Nomeie o novo projeto como "FirstContosoBankApp". Clique em Criar para criar o projeto.

  2. No arquivo MainWindow.xaml, adicione o XAML a seguir a um elemento Grid, substituindo o elemento StackPanel existente e seu conteúdo. Esse XAML inclui um botão para procurar um arquivo PFX a ser importado, uma caixa de texto para inserir uma senha para um arquivo PFX protegido por senha, um botão para importar um arquivo PFX selecionado, um botão para fazer logon no serviço Web protegido e um bloco de texto para exibir o status da ação atual.

    <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. Salve as alterações de MainWindow .

  4. Abra o arquivo MainWindow.xaml.cs e adicione as instruções a seguir using .

    using System;
    using System.Security.Cryptography.X509Certificates;
    using System.Diagnostics;
    using System.Net.Http;
    using System.Net;
    using System.Text;
    using Microsoft.UI.Xaml;
    using Windows.Security.Cryptography.Certificates;
    using Windows.Storage.Pickers;
    using Windows.Storage;
    using Windows.Storage.Streams;
    
  5. No arquivo MainWindow.xaml.cs, adicione as variáveis a seguir à classe MainWindow . Eles especificam o endereço para o ponto de extremidade do serviço de logon seguro do serviço Web "FirstContosoBank" e uma variável global que contém um certificado PFX a ser importado para o repositório de certificados. Atualize a <server-name> porta para localhost:7072 ou qualquer porta especificada na configuração "https" no arquivo launchSettings.json do projeto de API.

    private Uri requestUri = new Uri("https://<server-name>/bank/login");
    private string pfxCert = null;
    
  6. No arquivo MainWindow.xaml.cs, adicione o seguinte manipulador de cliques para o botão de login e o método para acessar o serviço Web protegido.

    private void Login_Click(object sender, RoutedEventArgs e)
    {
        MakeHttpsCall();
    }
    
    private async void MakeHttpsCall()
    {
        var result = new StringBuilder("Login ");
    
        // Load the certificate
        var certificate = new X509Certificate2(Convert.FromBase64String(pfxCert),
                                               PfxPassword.Password);
    
        // Create HttpClientHandler and add the certificate
        var handler = new HttpClientHandler();
        handler.ClientCertificates.Add(certificate);
        handler.ClientCertificateOptions = ClientCertificateOption.Automatic;
    
        // Create HttpClient with the handler
        var client = new HttpClient(handler);
    
        try
        {
            // Make a request
            var response = await client.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. Em seguida, adicione os seguintes manipuladores de cliques para o botão para procurar um arquivo PFX e o botão para importar um arquivo PFX selecionado para o repositório de certificados.

    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)
    {
        var result = new StringBuilder("Pfx file selection ");
        var pfxFilePicker = new FileOpenPicker();
        IntPtr hwnd = WinRT.Interop.WindowNative.GetWindowHandle(this);
        WinRT.Interop.InitializeWithWindow.Initialize(pfxFilePicker, hwnd);
        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. Abra o arquivo Package.appxmanifest e adicione os seguintes recursos à guia Recursos .

    • Autenticação Empresarial
    • Certificados de usuário compartilhados
  9. Execute seu aplicativo e faça logon no serviço Web protegido, bem como importe um arquivo PFX para o repositório de certificados local.

    Uma captura de tela do aplicativo WinUI com botões para procurar um arquivo PFX, importar um certificado e fazer logon em um serviço Web seguro

Você pode usar essas etapas para criar vários aplicativos que usam o mesmo certificado de usuário para acessar os mesmos serviços Web protegidos ou diferentes.

Windows Hello

Segurança e identidade

Criar uma API Web com o ASP.NET Core