Совместное использование сертификатов между приложениями Windows
Приложения Windows, требующие безопасной проверки подлинности за пределами сочетания идентификаторов пользователя и пароля, могут использовать сертификаты для проверки подлинности. Проверка подлинности сертификата обеспечивает высокий уровень доверия при проверке подлинности пользователя. В некоторых случаях группа служб требует проверки подлинности пользователя для нескольких приложений. В этой статье показано, как выполнить проверку подлинности нескольких приложений Windows с помощью одного сертификата и как можно предоставить способ импорта сертификата, предоставленного для доступа к защищенным веб-службам.
Приложения могут проходить проверку подлинности в веб-службе с помощью сертификата, а несколько приложений могут использовать один сертификат из хранилища сертификатов для проверки подлинности одного пользователя. Если сертификат не существует в магазине, вы можете добавить код в приложение для импорта сертификата из PFX-файла. Клиентское приложение в этом кратком руководстве — это приложение WinUI, а веб-служба — это веб-API ASP.NET Core.
Совет
Microsoft Copilot — отличный ресурс, если у вас есть вопросы о начале написания приложений Windows или веб-API ASP.NET Core. Copilot поможет вам написать код, найти примеры и узнать больше о рекомендациях по созданию безопасных приложений.
Необходимые компоненты
- Visual Studio с установленными ASP.NET и веб-разработками и рабочими нагрузками разработки приложений Windows.
- Последний пакет средств разработки программного обеспечения Windows (SDK) для использования API среда выполнения Windows (WinRT) в приложении WinUI.
- PowerShell для работы с самозаверяющий сертификатами.
Создание и публикация защищенной веб-службы
Откройте Microsoft Visual Studio и выберите "Создать проект " на начальном экране.
В диалоговом окне "Создание проекта" выберите API в раскрывающемся списке "Выбор типа проекта", чтобы отфильтровать доступные шаблоны проектов.
Выберите шаблон Веб-API ASP.NET Core и нажмите кнопку Далее.
Присвойте приложению имя FirstContosoBank и нажмите кнопку "Далее".
Выберите .NET 8.0 или более поздней версии в качестве платформы, задайте для типа проверки подлинности значение None, убедитесь, что установлен флажок "Настройка HTTPS", снимите флажок Включить поддержку OpenAPI, проверьте, не используйте операторы верхнего уровня и используйте контроллеры верхнего уровня и нажмите кнопку "Создать".
Щелкните правой кнопкой мыши файл WeatherForecastController.cs в папке "Контроллеры" и выберите "Переименовать". Измените имя на BankController.cs и позвольте Visual Studio переименовать класс и все ссылки на класс.
В файле launchSettings.json измените значение launchUrl с "weatherforecast" на "bank" для всех трех конфигураций, которые используют значение.
В файле 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"; }
Откройте диспетчер пакетов NuGet и найдите последнюю стабильную версию пакета Microsoft.AspNetCore.Authentication.Certificate. Этот пакет предоставляет ПО промежуточного слоя для проверки подлинности сертификата в ASP.NET Core.
Добавьте новый класс в проект с именем 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; } } }
Откройте 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
метод с сообщением об ошибке, которое будет возвращено клиенту.Начните отладку проекта для запуска веб-службы. Вы можете получать сообщения о доверии и установке SSL-сертификата. Нажмите кнопку "Да" для каждого из этих сообщений, чтобы доверять сертификату и продолжить отладку проекта.
Веб-служба будет доступна по адресу
https://localhost:7072/bank
. Вы можете протестировать веб-службу, открыв веб-браузер и введя веб-адрес. Вы увидите созданные данные прогноза погоды в формате JSON. Сохраняйте работу веб-службы при создании клиентского приложения.
Дополнительные сведения о работе с веб-API на основе контроллера ASP.NET Core см. в статье "Создание веб-API с помощью ASP.NET Core".
Создание приложения WinUI, использующего проверку подлинности сертификата
Теперь, когда у вас есть одна или несколько защищенных веб-служб, приложения могут использовать сертификаты для проверки подлинности в этих веб-службах. При выполнении запроса к проверенной веб-службе с помощью объекта HttpClient из API WinRT первоначальный запрос не будет содержать сертификат клиента. Веб-служба, прошедшие проверку подлинности, будет отвечать на запрос проверки подлинности клиента. При этом клиент Windows автоматически запрашивает хранилище сертификатов для доступных сертификатов. Пользователь может выбрать из этих сертификатов проверку подлинности в веб-службе. Некоторые сертификаты защищены паролем, поэтому пользователю потребуется предоставить пользователю способ ввода пароля для сертификата.
Примечание.
Api пакета SDK для приложений Windows для управления сертификатами пока отсутствуют. Для управления сертификатами в приложении необходимо использовать API WinRT. Мы также будем использовать API-интерфейсы хранилища WinRT для импорта сертификата из PFX-файла. Многие API WinRT могут использоваться любым приложением Windows с удостоверением пакета, включая приложения WinUI.
Код клиента HTTP, который мы реализуем, используется. HttpClient в NET. HttpClient, включенный в API WinRT, не поддерживает сертификаты клиентов.
Если нет доступных сертификатов клиента, пользователю потребуется добавить сертификат в хранилище сертификатов. В приложение можно включить код, позволяющий пользователю выбрать PFX-файл, содержащий сертификат клиента, а затем импортировать этот сертификат в хранилище сертификатов клиента.
Совет
Командлеты PowerShell New-SelfSignedCertificate и Export-PfxCertificate можно использовать для создания самозаверяющего сертификата и экспорта в PFX-файл для использования с этим кратким руководством. Дополнительные сведения см. в разделе New-SelfSignedCertificate и Export-PfxCertificate.
Обратите внимание, что при создании сертификата необходимо сохранить отпечаток сертификата, который будет использоваться в веб-службе для проверки.
Откройте Visual Studio и создайте проект WinUI на начальной странице. Назовите новый проект FirstContosoBankApp. Щелкните Создать, чтобы создать новый проект.
В файле 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"/>
Сохраните изменения MainWindow .
Откройте файл 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;
В файле 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;
В файле 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(); }
Затем добавьте следующие обработчики нажатия кнопки, чтобы найти 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(); }
Откройте файл Package.appxmanifest и добавьте следующие возможности на вкладку "Возможности".
- EnterpriseAuthentication
- SharedUserCertificates
Запустите приложение и войдите в безопасную веб-службу, а также импортируйте PFX-файл в локальное хранилище сертификатов.
Эти действия можно использовать для создания нескольких приложений, использующих один и тот же сертификат пользователя для доступа к одинаковым или разным защищенным веб-службам.
Связанный контент
Windows developer