Поделиться через


Совместное использование сертификатов между приложениями Windows

Приложения Windows, требующие безопасной проверки подлинности за пределами сочетания идентификаторов пользователя и пароля, могут использовать сертификаты для проверки подлинности. Проверка подлинности сертификата обеспечивает высокий уровень доверия при проверке подлинности пользователя. В некоторых случаях группа служб требует проверки подлинности пользователя для нескольких приложений. В этой статье показано, как выполнить проверку подлинности нескольких приложений Windows с помощью одного сертификата и как можно предоставить способ импорта сертификата, предоставленного для access защищенных веб-служб.

Приложения могут проходить проверку подлинности в веб-службе с помощью сертификата, а несколько приложений могут использовать один сертификат из хранилища сертификатов для проверки подлинности одного пользователя. Если сертификат не существует в магазине, вы можете добавить код в приложение для импорта сертификата из PFX-файла. Клиентское приложение в этом кратком руководстве — это приложение WinUI 3, а веб-служба — это веб-API ASP.NET Core.

Совет

Microsoft Copilot — отличный ресурс, если у вас есть вопросы о том, как начать писать приложения для Windows или веб-API на ASP.NET Core. Copilot поможет вам написать код, найти примеры и узнать больше о рекомендациях по созданию безопасных приложений.

Предварительные требования

Создание и публикация защищенной веб-службы

  1. Откройте Microsoft Visual Studio и выберите Создать новый проект на стартовом экране.

  2. В диалоговом окне "Создание нового проекта" выберите API в выпадающем списке Выберите тип проекта, чтобы отфильтровать доступные шаблоны проектов.

  3. Выберите шаблон ASP.NET Core веб-API и выберите Next.

  4. Присвойте приложению имя FirstContosoBank и нажмите кнопку "Далее".

  5. Выберите .NET 8.0 или более поздней версии в качестве Framework, задайте тип Authentication значение None, убедитесь, что установлен флажок Настроить для HTTPS, снимите флажок Enable OpenAPI support, поставьте отметку "Не используйте операторы верхнего уровня" и "Использовать контроллеры", и выберите Create.

    Снимок экрана с деталями создания нового проекта Visual Studio для веб-API ASP.NET Core

  6. Щелкните правой кнопкой мыши файл WeatherForecastController.cs в папке "Контроллеры" и выберите "Переименовать". Измените имя на BankController.cs и позвольте Visual Studio переименовать класс и все ссылки на класс.

  7. В файле launchSettings.json измените значение launchUrl с "weatherforecast" на "bank" для всех трех конфигураций, которые используют значение.

  8. В файле BankController.cs добавьте следующий метод 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. Откройте Package Manager NuGet и найдите последнюю стабильную версию пакета Microsoft.AspNetCore.Authentication.Certificate. Этот пакет предоставляет ПО промежуточного слоя для проверки подлинности сертификатов в ASP.NET Core.

  10. Добавьте новый класс в project с именем SecureCertificateValidationService. Добавьте следующий код в класс, чтобы настроить ПО промежуточного слоя проверки подлинности сертификата.

    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. Откройте Program.cs и замените код в методе Main следующим кодом:

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

    Приведенный выше код настраивает сервер Kestrel для требования сертификата клиента и добавляет ПО промежуточного слоя проверки подлинности сертификата в приложение. ПО промежуточного слоя проверяет сертификат клиента с помощью SecureCertificateValidationService класса. Событие OnCertificateValidated вызывается при проверке сертификата. Если сертификат действителен, событие вызывает Success метод. Если сертификат недопустим, событие вызывает Fail метод с сообщением об ошибке, которое будет возвращено клиенту.

  12. Начните отладку проекта, чтобы запустить веб-службу. Вы можете получать сообщения о доверии и установке SSL-сертификата. Щелкните Yes для каждого из этих сообщений, чтобы доверять сертификату и продолжить отладку project.

    Снимок экрана диалогового окна с запросом пользователя, если он хочет доверять сертификату

    Снимок экрана диалогового окна Windows с просьбой пользователя установить сертификат

  13. Веб-служба будет доступна по адресу https://localhost:7072/bank. Вы можете протестировать веб-службу, открыв веб-браузер и введя веб-адрес. Вы увидите созданные данные прогноза погоды в формате JSON. Сохраняйте работу веб-службы при создании клиентского приложения.

Дополнительные сведения о работе с контроллерными веб-API в ASP.NET Core см. в разделе Создание веб-API с ASP.NET Core.

Создание приложения WinUI, использующего проверку подлинности сертификата

Теперь, когда у вас есть одна или несколько защищенных веб-служб, приложения могут использовать сертификаты для проверки подлинности в этих веб-службах. При выполнении запроса к проверенной веб-службе с помощью объекта HttpClient из API WinRT первоначальный запрос не будет содержать сертификат клиента. Аутентифицированная веб-служба отвечает на запрос аутентификации клиента. При этом клиент Windows автоматически запрашивает сертификаты в хранилище для доступных клиентских сертификатов. Пользователь может выбрать из этих сертификатов для аутентификации в веб-службе. Некоторые сертификаты защищены паролем, поэтому пользователю нужно предоставить способ ввода пароля для сертификата.

Примечание.

Пока нет Windows App SDK API для управления сертификатами. Для управления сертификатами в приложении необходимо использовать API WinRT. Мы также будем использовать API WinRT storage для импорта сертификата из PFX-файла. Многие API WinRT могут использоваться любым приложением Windows с идентичностью пакета, включая приложения WinUI.

Код HTTP-клиента, который мы реализуем, использует HttpClient из .NET. HttpClient, включенный в API WinRT, не поддерживает сертификаты клиентов.

Если нет доступных сертификатов клиента, пользователю потребуется добавить сертификат в хранилище сертификатов. В приложение можно включить код, позволяющий пользователю выбрать PFX-файл, содержащий сертификат клиента, а затем импортировать этот сертификат в хранилище сертификатов клиента.

Совет

Командлеты PowerShell New-SelfSignedCertificate и Export-PfxCertificate можно использовать для создания самоподписанного сертификата и его экспорта в файл формата PFX для использования с этим кратким руководством. Дополнительные сведения см. в разделе New-SelfSignedCertificate и Export-PfxCertificate.

Обратите внимание, что при создании сертификата необходимо сохранить отпечаток сертификата, который будет использоваться в веб-службе для проверки.

  1. Откройте Visual Studio и создайте новый проект WinUI на начальной странице. Назовите новый проект "FirstContosoBankApp". Щелкните Create, чтобы создать новый project.

  2. В файле MainWindow.xaml добавьте следующий код XAML в элемент Grid, заменив существующий элемент StackPanel и его содержимое. Этот XAML-файл содержит кнопку для выбора PFX-файла для импорта, текстовое поле для ввода пароля для PFX-файла, защищенного паролем, кнопку для импорта выбранного PFX-файла, кнопку для входа в защищенный веб-сервис и текстовый блок для отображения состояния текущего действия.

    <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. Сохраните изменения в MainWindow.

  4. Откройте файл MainWindow.xaml.cs и добавьте следующие 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. В файле MainWindow.xaml.cs добавьте следующие переменные в класс MainWindow . Они указывают адрес для конечной точки защищенной службы входа в веб-службу FirstContosoBank и глобальную переменную, содержащую сертификат PFX для импорта в хранилище сертификатов. Обновите <server-name> на localhost:7072 или порт, указанный в настройках "https" в файле launchSettings.json проекта API.

    private Uri requestUri = new Uri("https://<server-name>/bank/login");
    private string pfxCert = null;
    
  6. В файле MainWindow.xaml.cs добавьте следующий обработчик нажатия кнопки входа и метод для обращения к защищенной веб-службе.

    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. Затем добавьте следующие обработчики нажатия кнопки, чтобы найти 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)
    {
        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. Откройте файл Package.appxmanifest и добавьте следующие возможности на вкладку "Возможности".

    • Корпоративная аутентификация
    • ОбщиеПользовательские сертификаты
  9. Запустите приложение и войдите в безопасную веб-службу, а также импортируйте PFX-файл в локальное хранилище сертификатов.

    Снимок экрана приложения WinUI с кнопками для поиска PFX-файла, импорта сертификата и входа в безопасную веб-службу

Эти действия можно использовать для создания нескольких приложений, использующих один и тот же сертификат пользователя для access одинаковых или разных защищенных веб-служб.

Windows Hello

Безопасность и идентификация

Создание веб-API с помощью ASP.NET Core