Аутентификация сертификата API ASP.NET Core 8 не работаетC#

Место общения программистов C#
Ответить
Anonymous
 Аутентификация сертификата API ASP.NET Core 8 не работает

Сообщение Anonymous »

У меня есть библиотека, которую я изначально создал в .NET 6. Недавно я выполнил обновление на месте до .NET 8. Теперь код библиотеки никогда не выполняется. Вот код класса AuthenticationService.cs (реализует интерфейс IAuthenticationService.cs, имеющий заглушку для метода IsValidClientCertificate, используемого при внедрении зависимостей):

Код: Выделить всё

using System.Configuration;
using Corp.Lib.CertificateAuthentication.Services.Interfaces;
using System.Security.Cryptography.X509Certificates;
using Corp.Lib.CertificateAuthentication.Configuration;
using Microsoft.Extensions.Options;
using Corp.Lib.Logging;

namespace Corp.Lib.CertificateAuthentication.Services
{
public class AuthenticationService : IAuthenticationService
{
private readonly ClientCertificateSettings _ClientCertificateSettings;

public AuthenticationService(IOptions options)
{
_ClientCertificateSettings = options.Value;
}

public bool IsValidClientCertificate(X509Certificate2 clientCertificate)
{
if (clientCertificate == null!)
{
var error = new ConfigurationErrorsException("Missing certificate or certificate not sent by client.");

Logger.Log.Error(error, "Certificate validation failed. Missing certificate or certificate not sent by client.");

throw error;
}

if (_ClientCertificateSettings == null! || _ClientCertificateSettings.AllowedCertificates == null! || !_ClientCertificateSettings.AllowedCertificates.ToList().Any())
{
var error = new ConfigurationErrorsException("Certificate configuration missing. Check AppSettings.");

Logger.Log.Error(error, "Certificate validation failed. Certificate configuration missing. Check AppSettings.");

throw error;
}

if (_ClientCertificateSettings.AllowedCertificates.Any(cert => string.IsNullOrEmpty(cert.Subject)))
{
var error = new ConfigurationErrorsException("Certificate configuration missing Subject. Check AppSettings.");

Logger.Log.Error(error, "Certificate validation failed. Certificate configuration missing Subject. Check AppSettings.");

throw error;
}

if (_ClientCertificateSettings.AllowedCertificates.Any(cert => string.IsNullOrEmpty(cert.Issuer)))
{
var error = new ConfigurationErrorsException("Certificate configuration missing Subject. Check AppSettings.");

Logger.Log.Error(error, "Certificate validation failed. Certificate configuration missing Issuer. Check AppSettings.");

throw error;
}

// 1. Check time validity of certificate.
if (DateTime.Compare(DateTime.UtcNow, clientCertificate.NotBefore) < 0 || DateTime.Compare(DateTime.UtcNow, clientCertificate.NotAfter) > 0)
{
Logger.Log.Warning($"Certificate with thumbprint {clientCertificate.Thumbprint} is expired.");

return false;
}

// 2. Check subject name of certificate.
var foundSubject = false;

var certSubjectData = clientCertificate.Subject.Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries);

if (certSubjectData.Any(certSubject => _ClientCertificateSettings.AllowedCertificates.Any(cert => cert.Subject.Equals(certSubject.Trim(), StringComparison.InvariantCultureIgnoreCase))))
{
foundSubject = true;
}

if (!foundSubject)
{
Logger.Log.Warning($"Certificate with thumbprint {clientCertificate.Thumbprint} does not have a matching Subject.");

return false;
}

// 3.  Check issuer name of certificate.
var certIssuerData = clientCertificate.Issuer.Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries);

var foundIssuer = certIssuerData.Any(issuerData => _ClientCertificateSettings.AllowedCertificates.Any(cert => cert.Issuer.Equals(issuerData.Trim(), StringComparison.InvariantCultureIgnoreCase)));

if (!foundIssuer)
{
Logger.Log.Warning($"Certificate with thumbprint {clientCertificate.Thumbprint} does not have a matching Issuer.");

return false;
}

// Check if the Certificate exists in the personal cert store.
if (_ClientCertificateSettings.CheckThumbprintInCertStore)
{
var store = new X509Store(StoreName.My, StoreLocation.LocalMachine);

try
{
store.Open(OpenFlags.OpenExistingOnly | OpenFlags.ReadOnly);

var certs = store.Certificates.Find(X509FindType.FindByThumbprint, clientCertificate.Thumbprint, true);

if (certs.Count == 0)
{
Logger.Log.Warning("Invalid client certificate. The thumbprint does not match with a certificate in the certificate store. {@clientCertificate}", clientCertificate);

return false;
}
}
catch (Exception ex)
{
Logger.Log.Error(ex, "An exception occurred searching for the client certificate in the certificate store.  {@clientCertificate}", clientCertificate);

throw;
}
finally
{
store.Close();

store.Dispose();
}
}

return true;
}
}
}

Вот код статического класса расширения ServiceCollectionExtension.cs:

Код: Выделить всё

using System.Net;
using System.Security.Claims;
using System.Security.Cryptography.X509Certificates;
using Corp.Lib.CertificateAuthentication.Configuration;
using Corp.Lib.CertificateAuthentication.Services;
using Corp.Lib.CertificateAuthentication.Services.Interfaces;
using Microsoft.AspNetCore.Authentication.Certificate;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Server.Kestrel.Https;
using Microsoft.Extensions.DependencyInjection;

namespace Corp.Lib.CertificateAuthentication.Extensions
{
public static class ServiceCollectionExtensions
{
public static void AddCertificateAuthenticationService(this WebApplicationBuilder builder)
{
builder.Services.Configure(builder.Configuration.GetSection("ClientCertificateSettings"));

// Require a certificate from any client sending a request.
builder.WebHost.ConfigureKestrel(kestrel =>
{
kestrel.ConfigureHttpsDefaults(defaults =>
{
defaults.ClientCertificateMode = ClientCertificateMode.RequireCertificate;
});
});

builder.Services.AddCertificateForwarding(options =>
{
options.CertificateHeader = "ssl-client-cert";

options.HeaderConverter = (headerValue) =>
{
X509Certificate2? clientCertificate = null;

if (!string.IsNullOrWhiteSpace(headerValue))
{
clientCertificate = X509Certificate2.CreateFromPem(WebUtility.UrlDecode(headerValue));
}

return clientCertificate!;
};
});

builder.Services.AddSingleton();

builder.Services.AddAuthentication(CertificateAuthenticationDefaults.AuthenticationScheme).AddCertificate(options =>
{
options.AllowedCertificateTypes = CertificateTypes.Chained;

options.Events = new CertificateAuthenticationEvents
{
OnCertificateValidated = context =>
{
var validationService = context.HttpContext.RequestServices.GetRequiredService();

if (validationService.IsValidClientCertificate(context.ClientCertificate))
{
var claims = new[]
{
new Claim(
ClaimTypes.NameIdentifier,
context.ClientCertificate.GetNameInfo(X509NameType.SimpleName, false),
ClaimValueTypes.String,
context.Options.ClaimsIssuer),
new Claim(
ClaimTypes.Name,
context.ClientCertificate.GetNameInfo(X509NameType.SimpleName, false),
ClaimValueTypes.String,
context.Options.ClaimsIssuer)
};

context.Principal = new ClaimsPrincipal(new ClaimsIdentity(claims,  context.Scheme.Name));

context.Success();
}
else
{
context.Fail("Invalid certificate.");
}

return Task.CompletedTask;
}
};
});

builder.Services.AddAuthorization();
}

public static void UseCertificateAuthenticationService(this WebApplication app)
{
app.UseCertificateForwarding();

app.UseAuthentication();

app.UseAuthorization();
}
}
}
Наконец, вот файл Program.cs для проекта WebApi, ссылающийся на библиотеку проверки подлинности сертификатов (ведение журнала требуется в моем проекте, но не включено сюда — закомментируйте ссылки):< /p>

Код: Выделить всё

using Corp.Lib.CertificateAuthentication.Extensions;
using Corp.Lib.Logging.Extensions;

var builder = WebApplication.CreateBuilder(args);

// Add services to the container.
builder.AddLogging(false);
builder.AddCertificateAuthenticationService();

builder.Services.AddControllers();

// Learn more about configuring Swagger/OpenAPI at https://aka.ms/aspnetcore/swashbuckle
builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen();

var app = builder.Build();

app.UseLogging(false);
app.UseCertificateAuthenticationService();

app.UseSwagger();
app.UseSwaggerUI();

app.UseHttpsRedirection();

app.MapControllers();

app.Run();
Реализация, которую я включил на предыдущем шаге, в основном дословно повторяла предписанную реализацию Microsoft Learn, найденную здесь (https://learn.microsoft.com/en-us/aspnet/core/security/ аутентификация/certauth?view=aspnetcore-8.0). Я даже не могу заставить браузер запрашивать сертификат. Когда это происходит в сценарии UAT с IIS в качестве веб-сервера, он запрашивает сертификат, но игнорирует метод AuthenticationService.cs IsValidClientCertificate(), как будто он даже не выполняется. Я просто хочу, чтобы проверка подлинности сертификата выполнялась с моей определенной логикой в ​​дополнение к тому, что делает Kestrel, IIS Express или IIS.

Подробнее здесь: https://stackoverflow.com/questions/790 ... ot-working
Ответить

Быстрый ответ

Изменение регистра текста: 
Смайлики
:) :( :oops: :roll: :wink: :muza: :clever: :sorry: :angel: :read: *x)
Ещё смайлики…
   
К этому ответу прикреплено по крайней мере одно вложение.

Если вы не хотите добавлять вложения, оставьте поля пустыми.

Максимально разрешённый размер вложения: 15 МБ.

Вернуться в «C#»