Freigeben über


Freigeben von Zertifikaten zwischen Windows-Apps

Windows-Apps, die eine sichere Authentifizierung über eine Benutzer-ID- und Kennwortkombination hinaus erfordern, können Zertifikate für die Authentifizierung verwenden. Die Zertifikatauthentifizierung bietet eine hohe Vertrauenswürdigkeit bei der Benutzerauthentifizierung. Es kann vorkommen, dass eine Gruppe von Diensten einen Benutzer für mehrere Apps authentifizieren möchte. In diesem Artikel wird gezeigt, wie Sie mehrere Windows-Apps mit demselben Zertifikat authentifizieren und wie Sie benutzern eine Methode zum Importieren eines Zertifikats bereitstellen können, das für access für gesicherte Webdienste bereitgestellt wurde.

Apps können sich mit einem Zertifikat bei einem Webdienst authentifizieren, und mehrere Apps können ein einzelnes Zertifikat aus dem Zertifikatspeicher verwenden, um denselben Benutzer zu authentifizieren. Wenn kein Zertifikat im Speicher vorhanden ist, können Sie Ihrer App Code hinzufügen, um ein Zertifikat aus einer PFX-Datei zu importieren. Die Client-App in dieser Schnellstartanleitung ist eine WinUI 3-App, und der Webdienst ist eine ASP.NET Core Web-API.

Tipp

Microsoft Copilot ist eine großartige Ressource, wenn Sie Fragen zum Einstieg in das Schreiben von Windows-Apps oder ASP.NET Core-Web-APIs haben. Copilot kann Ihnen dabei helfen, Code zu schreiben, Beispiele zu finden und mehr über bewährte Methoden zum Erstellen sicherer Apps zu erfahren.

Voraussetzungen

Erstellen und Veröffentlichen eines gesicherten Webdiensts

  1. Öffnen Sie Microsoft Visual Studio und wählen Sie Neues Projekt erstellen auf dem Startbildschirm aus.

  2. Wählen Sie im Dialogfeld "Neues Projekt erstellen" API im Projekttyp auswählen Dropdown-Liste aus, um die verfügbaren Projektvorlagen zu filtern.

  3. Wählen Sie die Vorlage ASP.NET Core Web-API aus, und wählen Sie Next aus.

  4. Benennen Sie die Anwendung "FirstContosoBank", und wählen Sie "Weiter" aus.

  5. Wählen Sie .NET 8.0 oder höher als Framework aus, legen Sie den Authentication-Typ auf None fest, stellen Sie sicher, dass Configure für HTTPS aktiviert ist, deaktivieren Sie Enable OpenAPI-Unterstützung, überprüfen Sie Verwenden Sie keine Anweisungen auf oberster Ebene und Use controllers, und wählen Sie Create aus.

    Ein Screenshot von Visual Studio zum Erstellen eines neuen Projekts mit Details für das ASP.NET Core-Web-API-Projekt.

  6. Klicken Sie mit der rechten Maustaste auf die WeatherForecastController.cs Datei im Ordner "Controller" , und wählen Sie "Umbenennen" aus. Ändern Sie den Namen in BankController.cs und lassen Sie Visual Studio die Klasse und alle Verweise auf die Klasse umbenennen.

  7. Ändern Sie in der DateilaunchSettings.json den Wert von "launchUrl" von "weatherforecast" auf "bank" für alle drei Konfigurationen, die diesen Wert verwenden.

  8. Fügen Sie in der datei BankController.cs die folgende "Login"-Methode hinzu.

    [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. Öffnen Sie die NuGet-Package Manager, und suchen Sie nach der neuesten stabilen Version des Pakets Microsoft.AspNetCore.Authentication.Certificate. Dieses Paket stellt Middleware für die Zertifikatauthentifizierung in ASP.NET Core bereit.

  10. Fügen Sie dem Projekt eine neue Klasse namens SecureCertificateValidationService hinzu. Fügen Sie der Klasse den folgenden Code hinzu, um die Zertifikatauthentifizierungs-Middleware zu konfigurieren.

    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. Öffnen Sie Program.cs , und ersetzen Sie den Code in der Main-Methode durch den folgenden Code:

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

    Der obige Code konfiguriert den Kestrel-Server so, dass ein Clientzertifikat erforderlich ist, und fügt der App die Middleware für die Zertifikatauthentifizierung hinzu. Die Middleware überprüft das Clientzertifikat mithilfe der SecureCertificateValidationService Klasse. Das OnCertificateValidated Ereignis wird aufgerufen, wenn ein Zertifikat überprüft wird. Wenn das Zertifikat gültig ist, ruft das Ereignis die Success Methode auf. Wenn das Zertifikat ungültig ist, ruft das Ereignis die Fail Methode mit einer Fehlermeldung auf, die an den Client zurückgegeben wird.

  12. Starten Sie das Debuggen des project, um den Webdienst zu starten. Möglicherweise erhalten Sie Nachrichten zum Vertrauen und Installieren eines SSL-Zertifikats. Klicken Sie auf Yes für jede dieser Nachrichten, um dem Zertifikat zu vertrauen und das Debuggen des project fortzusetzen.

    Screenshot eines Dialogfelds, in dem der Benutzer gefragt wird, ob er einem Zertifikat vertrauen möchte

    Screenshot eines Windows-Dialogfelds, in dem der Benutzer gefragt wird, ob er ein Zertifikat installieren möchte

  13. Der Webdienst wird unter https://localhost:7072/bank verfügbar sein. Sie können den Webdienst testen, indem Sie einen Webbrowser öffnen und die Webadresse eingeben. Die generierten Wettervorhersagedaten werden als JSON formatiert angezeigt. Lassen Sie den Webdienst weiterlaufen, während Sie die Client-App erstellen.

Weitere Informationen zum Arbeiten mit ASP.NET Core controllerbasierten Web-APIs finden Sie unter Create a web API with ASP.NET Core.

Erstellen einer WinUI-App, die die Zertifikatauthentifizierung verwendet

Nachdem Sie nun über einen oder mehrere gesicherte Webdienste verfügen, können Ihre Apps Zertifikate verwenden, um sich bei diesen Webdiensten zu authentifizieren. Wenn Sie eine Anforderung an einen authentifizierten Webdienst mit dem HttpClient-Objekt aus den WinRT-APIs senden, enthält die ursprüngliche Anforderung kein Clientzertifikat. Der authentifizierte Webdienst antwortet mit einer Anforderung für die Clientauthentifizierung. In diesem Fall fragt der Windows-Client automatisch den Zertifikatspeicher nach verfügbaren Clientzertifikaten ab. Ihr Benutzer kann aus diesen Zertifikaten auswählen, um sich beim Webdienst zu authentifizieren. Einige Zertifikate sind kennwortgeschützter, daher müssen Sie dem Benutzer eine Möglichkeit geben, das Kennwort für ein Zertifikat einzugeben.

Hinweis

Es gibt noch keine Windows App SDK APIs zum Verwalten von Zertifikaten. Sie müssen die WinRT-APIs verwenden, um Zertifikate in Ihrer App zu verwalten. Außerdem verwenden wir WinRT-storage-APIs, um ein Zertifikat aus einer PFX-Datei zu importieren. Viele WinRT-APIs können von jedem Windows app mit Paketidentität verwendet werden, einschließlich WinUI-Apps.

Der von uns implementierte HTTP-Clientcode verwendet .NET HttpClient. Der in den WinRT-APIs enthaltene HttpClient unterstützt keine Clientzertifikate.

Wenn keine Clientzertifikate verfügbar sind, muss der Benutzer dem Zertifikatspeicher ein Zertifikat hinzufügen. Sie können Code in Ihre App einfügen, mit dem ein Benutzer eine PFX-Datei auswählen kann, die ein Clientzertifikat enthält, und dann dieses Zertifikat in den Clientzertifikatspeicher importieren kann.

Tipp

Sie können die PowerShell-Cmdlets New-SelfSignedCertificate und Export-PfxCertificate verwenden, um ein selbstsigniertes Zertifikat zu erstellen und in eine PFX-Datei zu exportieren, die mit dieser Schnellstartanleitung verwendet werden kann. Weitere Informationen finden Sie unter New-SelfSignedCertificate und Export-PfxCertificate.

Beachten Sie, dass Sie beim Generieren des Zertifikats den Fingerabdruck des Zertifikats zur Überprüfung im Webdienst speichern sollten.

  1. Öffnen Sie Visual Studio und erstellen Sie von der Startseite aus ein neues WinUI-Projekt. Nennen Sie den neuen project "FirstContosoBankApp". Klicken Sie auf Create, um das neue Projekt zu erstellen.

  2. Fügen Sie in der Datei "MainWindow.xaml " einem Grid-Element den folgenden XAML-Code hinzu, wobei das vorhandene StackPanel-Element und deren Inhalt ersetzt werden. Dieser XAML-Code enthält eine Schaltfläche zum Suchen nach einer zu importierenden PFX-Datei, ein Textfeld zum Eingeben eines Kennworts für eine kennwortgeschützte PFX-Datei, eine Schaltfläche zum Importieren einer ausgewählten PFX-Datei, eine Schaltfläche zum Anmelden beim gesicherten Webdienst und einen Textblock zum Anzeigen des Status der aktuellen Aktion.

    <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. Speichern Sie die MainWindow-Änderungen .

  4. Öffnen Sie die datei MainWindow.xaml.cs , und fügen Sie die folgenden using Anweisungen hinzu.

    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. Fügen Sie in der datei MainWindow.xaml.cs die folgenden Variablen zur MainWindow-Klasse hinzu. Sie geben die Adresse für den gesicherten Anmeldedienstendpunkt Ihres "FirstContosoBank"-Webdiensts und eine globale Variable an, die ein PFX-Zertifikat enthält, das in den Zertifikatspeicher importiert werden soll. Aktualisieren Sie die <server-name> auf localhost:7072 oder den Port, der in der "https"-Konfiguration in der Datei launchSettings.json Ihres API-Projekts angegeben ist.

    private Uri requestUri = new Uri("https://<server-name>/bank/login");
    private string pfxCert = null;
    
  6. Fügen Sie in der Datei MainWindow.xaml.cs den folgenden Klickhandler für die Anmeldeschaltfläche und -methode hinzu, um den gesicherten Webdienst zu access.

    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. Fügen Sie als Nächstes die folgenden Klickhandler für die Schaltfläche hinzu, um nach einer PFX-Datei und der Schaltfläche zum Importieren einer ausgewählten PFX-Datei in den Zertifikatspeicher zu suchen.

    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. Öffnen Sie die Datei "Package.appxmanifest ", und fügen Sie der Registerkarte "Funktionen " die folgenden Funktionen hinzu.

    • Unternehmensauthentifizierung
    • SharedUserCertificates
  9. Führen Sie Ihre App aus, und melden Sie sich bei Ihrem gesicherten Webdienst an, und importieren Sie eine PFX-Datei in den lokalen Zertifikatspeicher.

    Screenshot der WinUI-App mit Schaltflächen zum Suchen nach einer PFX-Datei, Importieren eines Zertifikats und Anmelden bei einem gesicherten Webdienst

Mit diesen Schritten können Sie mehrere Apps erstellen, die dasselbe Benutzerzertifikat verwenden, um dieselben oder verschiedene gesicherte Webdienste zu access.

Windows Hello

Sicherheit und Identität

Erstellen einer Web-API mit ASP.NET Core