Scenarij 9.1: mTLS klijentska autentifikacija

Kategorija: Autentifikacija
Složenost: ⭐⭐⭐⭐ (Visoka)
Preduvjeti: Klijentski i serverski certifikati
Procijenjeno vrijeme: 20-30 minuta


Opis

Ovaj scenarij opisuje Mutual TLS (mTLS) autentifikaciju, gdje se i server i klijent međusobno autentificiraju certifikatima.

Prednosti u odnosu na autentifikaciju lozinkom:

  • Jača - Kriptografski utemeljena
  • Automatizirana - Bez interakcije korisnika
  • Opoziva - Centralna mogućnost opoziva
  • Zero-Trust - Svaki zahtjev je autentificiran

Tijek rada

sequenceDiagram participant Client as Klijent participant Server Client->>Server: ClientHello Server->>Client: ServerHello + ServerCert Server->>Client: CertificateRequest Client->>Server: ClientCert + CertificateVerify Server->>Server: Validacija klijentskog certifikata Server->>Client: Finished Client->>Server: Finished Note over Client,Server: mTLS veza uspostavljena


Primjer koda: mTLS Server (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 =>
    {
        // Učitavanje serverskog certifikata
        https.ServerCertificate = new X509Certificate2(
            "server.pfx", "ServerPassword!");
 
        // Zahtijevanje klijentskog certifikata
        https.ClientCertificateMode = ClientCertificateMode.RequireCertificate;
 
        // Validacija klijentskog certifikata
        https.ClientCertificateValidation = (cert, chain, errors) =>
        {
            // Prilagođena validacija
            if (errors != SslPolicyErrors.None)
            {
                Console.WriteLine($"SSL Policy greške: {errors}");
                return false;
            }
 
            // Provjera vlastite 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);
        };
    });
});
 
// Dodavanje Certificate Authentication
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($"Autentifikacija neuspješna: {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 = "Autentificirano", subject });
});
 
app.Run();

Primjer koda: mTLS Klijent (HttpClient)

using WvdS.Security.Cryptography.X509Certificates.Extensions.PQ;
 
using var ctx = PqCryptoContext.Initialize();
 
// Učitavanje klijentskog certifikata i ključa
var clientCert = ctx.LoadCertificateWithPrivateKey(
    "client.crt.pem",
    "client.key.pem",
    "ClientPassword!"
);
 
// HttpClientHandler s klijentskim certifikatom
var handler = new HttpClientHandler
{
    ClientCertificateOptions = ClientCertificateOption.Manual,
    ServerCertificateCustomValidationCallback = (message, cert, chain, errors) =>
    {
        // Validacija serverskog certifikata
        if (errors != SslPolicyErrors.None)
        {
            Console.WriteLine($"Greška serverskog certifikata: {errors}");
            // U produkciji: return false;
        }
        return true;
    }
};
 
// Dodavanje klijentskog certifikata
handler.ClientCertificates.Add(clientCert);
 
using var httpClient = new HttpClient(handler);
 
// Slanje zahtjeva
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($"Greška: {response.StatusCode}");
}

Autorizacija temeljena na certifikatu

public class CertificateAuthorizationHandler : AuthorizationHandler<CertificateRequirement>
{
    protected override Task HandleRequirementAsync(
        AuthorizationHandlerContext context,
        CertificateRequirement requirement)
    {
        // Dohvaćanje 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;
        }
 
        // Provjera je li certifikat autoriziran
        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 servisi
                @"^CN=service-[a-z]+$"            // Imenovani servisi
            }
        }));
});
 
builder.Services.AddSingleton<IAuthorizationHandler, CertificateAuthorizationHandler>();

Nginx kao mTLS Reverse Proxy

server {
    listen 443 ssl;
    server_name api.example.com;
 
    # Serverski certifikat
    ssl_certificate     /etc/nginx/ssl/server-chain.pem;
    ssl_certificate_key /etc/nginx/ssl/server.key.pem;
 
    # Autentifikacija klijentskim certifikatom
    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 / {
        # Proslijeđivanje informacija o klijentskom certifikatu backendu
        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;
    }
}

Zahtjevi mTLS specifični za industriju

Industrija Trajanje certifikata Opoziv Posebnost
Kubernetes 1 godina CRL SPIFFE/SPIRE
Financijski sektor 90 dana OCSP PCI-DSS usklađenost
Zdravstvo 1 godina CRL + OCSP DiGAV usklađenost
IoT/SCADA 5 godina CRL Offline mogućnost

Povezani scenariji

Povezanost Scenarij Opis
Preduvjet 3.2 Klijentski certifikat Kreiranje klijentskog certifikata
Povezano 9.2 Smartcard prijava Hardverski token
Povezano 10.3 mTLS Deployment TLS konfiguracija

« ← Pregled autentifikacije | ↑ Scenariji | 9.2 Smartcard prijava → »


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

Zuletzt geändert: 30.01.2026. u 00:16