Udostępnij za pośrednictwem


Udostępnianie certyfikatów między aplikacjami systemu Windows

Aplikacje systemu Windows, które wymagają bezpiecznego uwierzytelniania poza kombinacją identyfikatora użytkownika i hasła, mogą używać certyfikatów do uwierzytelniania. Uwierzytelnianie certyfikatu zapewnia wysoki poziom zaufania podczas uwierzytelniania użytkownika. W niektórych przypadkach grupa usług będzie chciała uwierzytelnić użytkownika dla wielu aplikacji. W tym artykule pokazano, jak można uwierzytelniać wiele aplikacji systemu Windows przy użyciu tego samego certyfikatu oraz jak zapewnić użytkownikom metodę importowania certyfikatu udostępnionego w celu uzyskania dostępu do zabezpieczonych usług sieci Web.

Aplikacje mogą uwierzytelniać się w usłudze internetowej przy użyciu certyfikatu, a wiele aplikacji może używać jednego certyfikatu z magazynu certyfikatów w celu uwierzytelnienia tego samego użytkownika. Jeśli certyfikat nie istnieje w sklepie, możesz dodać kod do aplikacji, aby zaimportować certyfikat z pliku PFX. Aplikacja kliencka w tym przewodniku Szybki start jest aplikacją WinUI, a usługa internetowa jest internetowym interfejsem API ASP.NET Core.

Wskazówka

Microsoft Copilot to doskonałe źródło informacji, jeśli masz pytania dotyczące rozpoczynania pisania aplikacji systemu Windows lub ASP.NET Core internetowych interfejsów API. Copilot może pomóc w pisaniu kodu, znajdowaniu przykładów i dowiedzeniu się więcej o najlepszych rozwiązaniach dotyczących tworzenia bezpiecznych aplikacji.

Wymagania wstępne

Tworzenie i publikowanie zabezpieczonej usługi internetowej

  1. Otwórz program Microsoft Visual Studio i wybierz pozycję Utwórz nowy projekt na ekranie startowym.

  2. W oknie dialogowym Tworzenie nowego projektu wybierz pozycję API z listy rozwijanej Wybierz typ projektu , aby przefiltrować dostępne szablony projektów.

  3. Wybierz szablon ASP.NET Core Web API i następnie wybierz Dalej.

  4. Nadaj aplikacji nazwę "FirstContosoBank" i wybierz przycisk Dalej.

  5. Wybierz pozycję .NET 8.0 lub nowszą jako strukturę, ustaw typ uwierzytelniania na Brak, upewnij się, że jest zaznaczona opcja Konfiguruj dla protokołu HTTPS , usuń zaznaczenie pola wyboru Włącz obsługę OpenAPI, zaznacz opcję Nie używaj instrukcji najwyższego poziomu i Użyj kontrolerów, a następnie wybierz pozycję Utwórz.

    Zrzut ekranu przedstawiający tworzenie szczegółów nowego projektu przez program Visual Studio dla projektu internetowego interfejsu API ASP.NET Core

  6. Kliknij prawym przyciskiem myszy plik WeatherForecastController.cs w folderze Controllers (Kontrolery ) i wybierz polecenie Change name (Zmień nazwę). Zmień nazwę na BankController.cs i pozwól programowi Visual Studio zmienić nazwę klasy i wszystkich odwołań do klasy.

  7. W pliku launchSettings.json zmień wartość "launchUrl" z "weatherforecast" na "bank" dla wszystkich trzech konfiguracji, które używają tej wartości.

  8. W pliku BankController.cs dodaj następującą metodę "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. Otwórz Menedżera pakietów NuGet i wyszukaj i zainstaluj najnowszą stabilną wersję pakietu Microsoft.AspNetCore.Authentication.Certificate . Ten pakiet zawiera oprogramowanie pośredniczące do uwierzytelniania certyfikatów w ASP.NET Core.

  10. Dodaj nową klasę do projektu o nazwie SecureCertificateValidationService. Dodaj następujący kod do klasy, aby skonfigurować oprogramowanie pośredniczące uwierzytelniania certyfikatu.

    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. Otwórz Program.cs i zastąp kod w metodzie Main następującym kodem:

    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();
     }
    

    Powyższy kod konfiguruje serwer Kestrel tak, aby wymagał certyfikatu klienta i dodaje oprogramowanie pośredniczące uwierzytelniania certyfikatu do aplikacji. Oprogramowanie pośredniczące weryfikuje certyfikat klienta przy użyciu SecureCertificateValidationService klasy. Zdarzenie OnCertificateValidated jest wywoływane po zweryfikowaniu certyfikatu. Jeśli certyfikat jest prawidłowy, zdarzenie wywołuje metodę Success . Jeśli certyfikat jest nieprawidłowy, zdarzenie wywołuje Fail metodę z komunikatem o błędzie, który zostanie zwrócony do klienta.

  12. Rozpocznij debugowanie projektu, aby uruchomić usługę internetową. Możesz otrzymać komunikaty o zaufaniu i zainstalowaniu certyfikatu SSL. Kliknij przycisk Tak dla każdego z tych komunikatów, aby zaufać certyfikatowi i kontynuować debugowanie projektu.

    Zrzut ekranu przedstawiający okno dialogowe z pytaniem użytkownika, czy chce zaufać certyfikatowi

    Zrzut ekranu przedstawiający okno dialogowe systemu Windows z pytaniem użytkownika, czy chce zainstalować certyfikat

  13. Serwis internetowy będzie dostępny pod adresem https://localhost:7072/bank. Usługę internetową można przetestować, otwierając przeglądarkę internetową i wpisując adres internetowy. Zobaczysz wygenerowane dane prognozy pogody sformatowane jako JSON. Utrzymuj działanie usługi internetowej podczas tworzenia aplikacji klienckiej.

Aby uzyskać więcej informacji na temat pracy z interfejsami API sieci Web opartymi na kontrolerze ASP.NET Core, zobacz Tworzenie internetowego interfejsu API za pomocą ASP.NET Core.

Tworzenie aplikacji WinUI korzystającej z uwierzytelniania certyfikatu

Teraz, gdy masz co najmniej jedną zabezpieczoną usługę internetową, aplikacje mogą używać certyfikatów do uwierzytelniania w tych usługach internetowych. Po wysłaniu żądania do uwierzytelnionej usługi sieci Web przy użyciu obiektu HttpClient z interfejsów API WinRT początkowe żądanie nie będzie zawierać certyfikatu klienta. Uwierzytelniona usługa internetowa odpowie żądaniem uwierzytelnienia klienta. W takim przypadku klient systemu Windows automatycznie odpytuje magazyn certyfikatów pod kątem dostępnych certyfikatów klienta. Użytkownik może wybrać spośród tych certyfikatów, aby uwierzytelnić się w usłudze internetowej. Niektóre certyfikaty są chronione hasłem, dlatego należy podać użytkownikowi sposób wprowadzania hasła dla certyfikatu.

Uwaga / Notatka

Nie ma jeszcze interfejsów API zestawu SDK aplikacji systemu Windows do zarządzania certyfikatami. Do zarządzania certyfikatami w aplikacji należy używać interfejsów API WinRT. Będziemy również używać interfejsów API magazynu WinRT do importowania certyfikatu z pliku PFX. Wiele interfejsów API WinRT może być używanych przez dowolną aplikację systemu Windows z tożsamością pakietu, w tym aplikacje WinUI.

Kod klienta HTTP, który zaimplementujemy, używa . Klient HttpClient sieci NET. Klient HttpClient zawarty w interfejsach API WinRT nie obsługuje certyfikatów klienta.

Jeśli nie ma dostępnych certyfikatów klienta, użytkownik będzie musiał dodać certyfikat do magazynu certyfikatów. Możesz dołączyć kod w aplikacji, który umożliwia użytkownikowi wybranie pliku PFX zawierającego certyfikat klienta, a następnie zaimportowanie tego certyfikatu do magazynu certyfikatów klienta.

Wskazówka

Możesz użyć poleceń cmdlet programu PowerShell New-SelfSignedCertificate i Export-PfxCertificate , aby utworzyć certyfikat z podpisem własnym i wyeksportować go do pliku PFX do użycia z tym przewodnikiem Szybki start. Aby uzyskać więcej informacji, zobacz New-SelfSignedCertificate i Export-PfxCertificate.

Należy pamiętać, że podczas generowania certyfikatu należy zapisać odcisk palca certyfikatu, aby użyć go w usłudze internetowej na potrzeby walidacji.

  1. Otwórz program Visual Studio i utwórz nowy projekt WinUI na stronie startowej. Nazwij nowy projekt "FirstContosoBankApp". Kliknij przycisk Utwórz , aby utworzyć nowy projekt.

  2. W pliku MainWindow.xaml dodaj następujący kod XAML do elementu Grid , zastępując istniejący element StackPanel i jego zawartość. Ten kod XAML zawiera przycisk umożliwiający przeglądanie pliku PFX do zaimportowania, pole tekstowe umożliwiające wprowadzenie hasła do pliku PFX chronionego hasłem, przycisk importowania wybranego pliku PFX, przycisk do logowania się do zabezpieczonej usługi internetowej oraz blok tekstowy w celu wyświetlenia stanu bieżącej akcji.

    <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. Zapisz zmiany w oknie głównym .

  4. Otwórz plik MainWindow.xaml.cs i dodaj następujące using instrukcje.

    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. W pliku MainWindow.xaml.cs dodaj następujące zmienne do klasy MainWindow . Określają one adres punktu końcowego zabezpieczonej usługi logowania usługi internetowej "FirstContosoBank" oraz zmienną globalną, która przechowuje certyfikat PFX do zaimportowania do magazynu certyfikatów. Zaktualizuj <server-name> port do localhost:7072 lub dowolny port określony w konfiguracji "https" w pliku launchSettings.json projektu interfejsu API.

    private Uri requestUri = new Uri("https://<server-name>/bank/login");
    private string pfxCert = null;
    
  6. W pliku MainWindow.xaml.cs dodaj następujący program obsługi kliknięć dla przycisku logowania i metody uzyskiwania dostępu do zabezpieczonej usługi internetowej.

    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. Następnie dodaj następujące programy obsługi kliknięć dla przycisku, aby przejść do pliku PFX, i przycisku, aby zaimportować wybrany plik PFX do magazynu certyfikatów.

    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. Otwórz plik Package.appxmanifest i dodaj następujące możliwości do karty Możliwości .

    • Uwierzytelnianie przedsiębiorstwa
    • Certyfikaty użytkownika współdzielonego
  9. Uruchom aplikację i zaloguj się do zabezpieczonej usługi internetowej, a także zaimportuj plik PFX do lokalnego magazynu certyfikatów.

    Zrzut ekranu aplikacji WinUI z przyciskami do przeglądania pliku PFX, importowania certyfikatu i logowania się do zabezpieczonej usługi internetowej

Te kroki umożliwiają utworzenie wielu aplikacji korzystających z tego samego certyfikatu użytkownika w celu uzyskania dostępu do tych samych lub różnych zabezpieczonych usług sieci Web.

Windows Hello

Zabezpieczenia i tożsamość

Tworzenie internetowego interfejsu API przy użyciu platformy ASP.NET Core