Scenarij 9.1: mTLS overitev odjemalca

Kategorija: Overitev
Kompleksnost: 4/5 (Visoka)
Predpogoji: Certifikati odjemalca in strežnika
Ocenjeni čas: 20-30 minut


Opis

Ta scenarij opisuje Mutual TLS (mTLS) overitev, pri kateri se tako strežnik kot odjemalec medsebojno overita s certifikati.

Prednosti pred overitvijo z geslom:

  • Močnejša - Kriptografsko osnovana
  • Avtomatizirana - Brez uporabniške interakcije
  • Preklicljiva - Centralna možnost preklica
  • Zero-Trust - Vsaka zahteva je overjena

Potek dela

sequenceDiagram participant Client as Odjemalec participant Server as Strežnik Client->>Server: ClientHello Server->>Client: ServerHello + ServerCert Server->>Client: CertificateRequest Client->>Server: ClientCert + CertificateVerify Server->>Server: Validacija certifikata odjemalca Server->>Client: Finished Client->>Server: Finished Note over Client,Server: mTLS-povezava vzpostavljena


Primer kode: mTLS strežnik (ASP.NET Core)

using Microsoft.AspNetCore.Authentication.Certificate;
using Microsoft.AspNetCore.Server.Kestrel.Https;
 
var builder = WebApplication.CreateBuilder(args);
 
// Konfiguracija Kestrel za mTLS
builder.WebHost.ConfigureKestrel(options =>
{
    options.ConfigureHttpsDefaults(https =>
    {
        // Nalaganje certifikata strežnika
        https.ServerCertificate = new X509Certificate2(
            "server.pfx", "ServerPassword!");
 
        // Zahtevanje certifikata odjemalca
        https.ClientCertificateMode = ClientCertificateMode.RequireCertificate;
 
        // Validacija certifikata odjemalca
        https.ClientCertificateValidation = (cert, chain, errors) =>
        {
            // Prilagojena validacija
            if (errors != SslPolicyErrors.None)
            {
                Console.WriteLine($"SSL Policy Errors: {errors}");
                return false;
            }
 
            // Preverjanje lastne CA
            var trustedRoots = new X509Certificate2Collection();
            trustedRoots.Add(new X509Certificate2("root-ca.crt"));
 
            var customChain = new X509Chain();
            customChain.ChainPolicy.CustomTrustStore.AddRange(trustedRoots);
            customChain.ChainPolicy.TrustMode = X509ChainTrustMode.CustomRootTrust;
            customChain.ChainPolicy.RevocationMode = X509RevocationMode.Online;
 
            return customChain.Build(cert);
        };
    });
});
 
// Dodajanje overitve s certifikatom
builder.Services.AddAuthentication(CertificateAuthenticationDefaults.AuthenticationScheme)
    .AddCertificate(options =>
    {
        options.AllowedCertificateTypes = CertificateTypes.Chained;
        options.RevocationMode = X509RevocationMode.Online;
 
        options.Events = new CertificateAuthenticationEvents
        {
            OnCertificateValidated = context =>
            {
                // Ekstrakcija claims iz certifikata
                var claims = new[]
                {
                    new Claim(ClaimTypes.NameIdentifier, context.ClientCertificate.Subject),
                    new Claim(ClaimTypes.Name, context.ClientCertificate.GetNameInfo(X509NameType.SimpleName, false)),
                    new Claim("certificate_thumbprint", context.ClientCertificate.Thumbprint)
                };
 
                context.Principal = new ClaimsPrincipal(
                    new ClaimsIdentity(claims, context.Scheme.Name));
 
                context.Success();
                return Task.CompletedTask;
            },
 
            OnAuthenticationFailed = context =>
            {
                Console.WriteLine($"Overitev spodletela: {context.Exception.Message}");
                return Task.CompletedTask;
            }
        };
    });
 
builder.Services.AddAuthorization();
 
var app = builder.Build();
 
app.UseAuthentication();
app.UseAuthorization();
 
app.MapGet("/api/protected", [Authorize] (HttpContext ctx) =>
{
    var subject = ctx.User.FindFirst(ClaimTypes.NameIdentifier)?.Value;
    return Results.Ok(new { message = "Overjeno", subject });
});
 
app.Run();

Primer kode: mTLS odjemalec (HttpClient)

using WvdS.Security.Cryptography.X509Certificates.Extensions.PQ;
 
using var ctx = PqCryptoContext.Initialize();
 
// Nalaganje certifikata in ključa odjemalca
var clientCert = ctx.LoadCertificateWithPrivateKey(
    "client.crt.pem",
    "client.key.pem",
    "ClientPassword!"
);
 
// HttpClientHandler s certifikatom odjemalca
var handler = new HttpClientHandler
{
    ClientCertificateOptions = ClientCertificateOption.Manual,
    ServerCertificateCustomValidationCallback = (message, cert, chain, errors) =>
    {
        // Validacija certifikata strežnika
        if (errors != SslPolicyErrors.None)
        {
            Console.WriteLine($"Napaka certifikata strežnika: {errors}");
            // V produkciji: return false;
        }
        return true;
    }
};
 
// Dodajanje certifikata odjemalca
handler.ClientCertificates.Add(clientCert);
 
using var httpClient = new HttpClient(handler);
 
// Pošiljanje zahteve
var response = await httpClient.GetAsync("https://api.example.com/api/protected");
 
if (response.IsSuccessStatusCode)
{
    var content = await response.Content.ReadAsStringAsync();
    Console.WriteLine($"Odgovor: {content}");
}
else
{
    Console.WriteLine($"Napaka: {response.StatusCode}");
}

Avtorizacija na osnovi certifikatov

public class CertificateAuthorizationHandler : AuthorizationHandler<CertificateRequirement>
{
    protected override Task HandleRequirementAsync(
        AuthorizationHandlerContext context,
        CertificateRequirement requirement)
    {
        // Pridobitev certifikata iz Principal
        var thumbprint = context.User.FindFirst("certificate_thumbprint")?.Value;
        var subject = context.User.FindFirst(ClaimTypes.NameIdentifier)?.Value;
 
        if (string.IsNullOrEmpty(thumbprint))
        {
            return Task.CompletedTask;
        }
 
        // Preverjanje ali je certifikat avtoriziran
        if (requirement.AllowedThumbprints.Contains(thumbprint) ||
            requirement.AllowedSubjectPatterns.Any(p => Regex.IsMatch(subject, p)))
        {
            context.Succeed(requirement);
        }
 
        return Task.CompletedTask;
    }
}
 
public class CertificateRequirement : IAuthorizationRequirement
{
    public HashSet<string> AllowedThumbprints { get; set; } = new();
    public List<string> AllowedSubjectPatterns { get; set; } = new();
}
 
// Registracija
builder.Services.AddAuthorization(options =>
{
    options.AddPolicy("ServiceAccounts", policy =>
        policy.Requirements.Add(new CertificateRequirement
        {
            AllowedSubjectPatterns = new List<string>
            {
                @"^CN=.*\.svc\.cluster\.local$",  // Kubernetes storitve
                @"^CN=service-[a-z]+$"            // Poimenovane storitve
            }
        }));
});
 
builder.Services.AddSingleton<IAuthorizationHandler, CertificateAuthorizationHandler>();

Nginx kot mTLS Reverse Proxy

server {
    listen 443 ssl;
    server_name api.example.com;
 
    # Certifikat strežnika
    ssl_certificate     /etc/nginx/ssl/server-chain.pem;
    ssl_certificate_key /etc/nginx/ssl/server.key.pem;
 
    # Overitev s certifikatom odjemalca
    ssl_client_certificate /etc/nginx/ssl/ca-chain.pem;
    ssl_verify_client on;
    ssl_verify_depth 2;
 
    # TLS 1.3
    ssl_protocols TLSv1.3;
    ssl_prefer_server_ciphers on;
 
    # OCSP Stapling
    ssl_stapling on;
    ssl_stapling_verify on;
 
    location / {
        # Posredovanje info o certifikatu odjemalca zaledju
        proxy_set_header X-Client-Cert $ssl_client_cert;
        proxy_set_header X-Client-Verify $ssl_client_verify;
        proxy_set_header X-Client-S-DN $ssl_client_s_dn;
 
        proxy_pass http://backend:8080;
    }
}

Panožno-specifične mTLS zahteve

Panoga Veljavnost certifikata Preklic Posebnost
Kubernetes 1 leto CRL SPIFFE/SPIRE
Finančni sektor 90 dni OCSP PCI-DSS skladen
Zdravstvo 1 leto CRL + OCSP DiGAV-skladen
IoT/SCADA 5 let CRL Offline-zmožnost

Povezani scenariji

Povezava Scenarij Opis
Predpogoj 3.2 Certifikat odjemalca Ustvarjanje Client-Cert
Povezano 9.2 Prijava s pametno kartico Strojni žeton
Povezano 10.3 mTLS Deployment TLS-konfiguracija

« Pregled overitve | Scenariji | 9.2 Prijava s pametno kartico »


Wolfgang van der Stille @ EMSR DATA d.o.o. - Post-Quantum Cryptography Professional

Zuletzt geändert: dne 30.01.2026 ob 01:34