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 den Zugriff auf 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-App, und der Webdienst ist eine ASP.NET Core-Web-API.

Tipp

Microsoft Copilot ist eine großartige Ressource, wenn Sie Fragen zum 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 auf der Startseite "Neues Projekt erstellen" aus.

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

  3. Wählen Sie die ASP.NET Core-Web-API-Vorlage aus, und klicken Sie auf Weiter.

  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 Authentifizierungstyp auf "Keine" fest, stellen Sie sicher, dass "Für HTTPS konfigurieren" aktiviert ist, deaktivieren Sie die Option "OpenAPI-Unterstützung aktivieren", überprüfen Sie "Anweisungen der obersten Ebene nicht verwenden" und "Controller verwenden", und wählen Sie "Erstellen" aus.

    Screenshot des Visual Studio-Projekts zum Erstellen neuer Projektdetails 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 datei launchSettings.json den Wert von "launchUrl" von "weatherforecast" in "bank" für alle drei Konfigurationen, die den 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-Paket-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 mit dem Namen 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 Projekts, um den Webdienst zu starten. Möglicherweise erhalten Sie Nachrichten zum Vertrauen und Installieren eines SSL-Zertifikats. Klicken Sie für jede dieser Nachrichten auf "Ja ", um dem Zertifikat zu vertrauen, und fahren Sie mit dem Debuggen des Projekts fort.

    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. Sie können den Webdienst testen, indem Sie einen Webbrowser öffnen und die Webadresse eingeben. Die generierten Wettervorhersagedaten werden als JSON formatiert angezeigt. Führen Sie die Ausführung des Webdiensts beim Erstellen der Client-App durch.

Weitere Informationen zum Arbeiten mit ASP.NET Core controllerbasierten Web-APIs finden Sie unter Erstellen einer Web-API mit 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-Speicher-APIs, um ein Zertifikat aus einer PFX-Datei zu importieren. Viele WinRT-APIs können von jeder Windows-App mit Paketidentität verwendet werden, einschließlich WinUI-Apps.

Der HTTP-Clientcode, den wir implementieren, verwendet . HttpClient von NET. 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 auf der Startseite ein neues WinUI-Projekt. Nennen Sie das neue Projekt "FirstContosoBankApp". Klicken Sie auf Erstellen, 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 der MainWindow-Klasse die folgenden Variablen 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 den <server-name> Port oder localhost:7072 den port, der in der "https"-Konfiguration in der launchSettings.json Datei 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 auf den gesicherten Webdienst zuzugreifen.

    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.

    • EnterpriseAuthentication
    • 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 auf dieselben oder unterschiedliche gesicherte Webdienste zuzugreifen.

Windows Hello

Sicherheit und Identität

Erstellen einer Web-API mit ASP.NET Core