.Net 6 API - Certificate Authentication - Not working 403 response received

1k Views Asked by At

I am working on an Azure Function that needs to send a certificate to an API that will authenticate the request and return the data accordingly.

I am successfully getting the certificate from Azure Key Vault as a X509Certificate2. This is making a call (will be making several) to my API I am getting a 403 response.

I have followed this configuration https://learn.microsoft.com/en-us/aspnet/core/security/authentication/certauth?view=aspnetcore-6.0 and my code currently looks like this

-- Program.cs

WebApplicationBuilder builder = WebApplication.CreateBuilder(args);

builder.Services.Configure<KestrelServerOptions>(options =>
{
    options.ConfigureHttpsDefaults(options =>
        options.ClientCertificateMode = ClientCertificateMode.AllowCertificate);
});

builder.Services.AddAuthentication(
        CertificateAuthenticationDefaults.AuthenticationScheme)
    .AddCertificate(options =>
    {
        options.Events = new CertificateAuthenticationEvents
        {
            OnCertificateValidated = context =>
            {
                var validationService = context.HttpContext.RequestServices
                    .GetRequiredService<ICertificateValidationService>();

                if (validationService.ValidateCertificate(context.ClientCertificate).Result)
                {
                    var claims = new[]
                    {
                        new Claim(
                            ClaimTypes.NameIdentifier,
                            context.ClientCertificate.Subject,
                            ClaimValueTypes.String, context.Options.ClaimsIssuer),
                        new Claim(
                            ClaimTypes.Name,
                            context.ClientCertificate.Subject,
                            ClaimValueTypes.String, context.Options.ClaimsIssuer)
                    };

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

                return Task.CompletedTask;
            },
            OnAuthenticationFailed = failedContext =>
            {
                failedContext.Response.StatusCode = (int)HttpStatusCode.Unauthorized;
                return Task.CompletedTask;
            }
        };
    });

-- Validation Service

public class CertificateValidationService : ICertificateValidationService
{
    private readonly IKeyVaultCertificateService _keyVaultService;
    private readonly KeyVaultConfiguration _keyVauyltConfiguration;

    public CertificateValidationService(IKeyVaultCertificateService keyVaultService, IOptions<KeyVaultConfiguration> keyVauyltConfiguration)
    {
        _keyVaultService = keyVaultService;
        _keyVauyltConfiguration = keyVauyltConfiguration.Value;
    }

    public async Task<bool> ValidateCertificate(X509Certificate2 clientCertificate)
    {

        X509Certificate2 expectedCertificate = await _keyVaultService.GetX509CertificateAsync(_keyVauyltConfiguration.CertificateName);

        return clientCertificate.Thumbprint == expectedCertificate.Thumbprint;
    }
}

-- Controller

[Authorize(AuthenticationSchemes = CertificateAuthenticationDefaults.AuthenticationScheme)]
public IActionResult GetCount()
{
    /// removed
}

When the request is made I am not hitting the method ValidateCertificate and I am just getting a 403 response.

I am having the same when I make a request through Postman and send the certificate in the request there too.

I would be grateful if someone could help me as I am stuck without success right now

--- Edit 1

_client = new HttpClient();

if (_erpConfiguration.UserCertificateAuthentication)
{
    X509Certificate2 certificate = _keyVaultCertificateService
        .GetX509CertificateAsync(_keyVaultConfiguration.CertificateName).Result;
    var handler = new HttpClientHandler();
    handler.ClientCertificates.Add(certificate);
    _client = new HttpClient(handler);
}

var countHttpResponse = await _client.GetAsync(fullUri);

--- End Edit 1

--- Edit 2

I am getting the certificate from KeyVault like this

public async Task<KeyVaultCertificateWithPolicy> GetCertificateWithPolicyAsync(string certificateName)
{
    Response<KeyVaultCertificateWithPolicy>? certificate = await _certificateClient.GetCertificateAsync(certificateName);
    return certificate;
}


public async Task<X509Certificate2> GetX509CertificateAsync(string certificateName)
{
    KeyVaultCertificateWithPolicy certificate = await GetCertificateWithPolicyAsync(certificateName);
    var certContent = certificate.Cer;
    return new X509Certificate2(certContent);
}

--- End Edit 2

--- Edit 3

public async Task<X509Certificate2> GetX509CertificateAsync(string certificateName)
{
    KeyVaultCertificateWithPolicy certificate = await GetCertificateWithPolicyAsync(certificateName);

    // Return a certificate with only the public key if the private key is not exportable.
    if (certificate.Policy?.Exportable != true)
    {
        return new X509Certificate2(certificate.Cer);
    }

    // Parse the secret ID and version to retrieve the private key.
    string[] segments = certificate.SecretId.AbsolutePath.Split('/', StringSplitOptions.RemoveEmptyEntries);
    if (segments.Length != 3)
    {
        throw new InvalidOperationException($"Number of segments is incorrect: {segments.Length}, URI: {certificate.SecretId}");
    }

    string secretName = segments[1];
    string secretVersion = segments[2];

    KeyVaultSecret secret = await _secretClient.GetSecretAsync(secretName, secretVersion, CancellationToken.None);

    // For PEM, you'll need to extract the base64-encoded message body.
    if (!"application/x-pkcs12".Equals(secret.Properties.ContentType,
            StringComparison.InvariantCultureIgnoreCase))
    {
        throw new VerificationException("Unable to validate certificate");
    }

    byte[] pfx = Convert.FromBase64String(secret.Value);
    return new X509Certificate2(pfx);

}

--- End Edit 3

0

There are 0 best solutions below