~~NOTOC~~
====== Scenarij 9.1: mTLS klijentska autentifikacija ======
**Kategorija:** [[.:start|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
{
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 AllowedThumbprints { get; set; } = new();
public List AllowedSubjectPatterns { get; set; } = new();
}
// Registracija
builder.Services.AddAuthorization(options =>
{
options.AddPolicy("ServiceAccounts", policy =>
policy.Requirements.Add(new CertificateRequirement
{
AllowedSubjectPatterns = new List
{
@"^CN=.*\.svc\.cluster\.local$", // Kubernetes servisi
@"^CN=service-[a-z]+$" // Imenovani servisi
}
}));
});
builder.Services.AddSingleton();
----
===== 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** | [[hr:int:pqcrypt:szenarien:zertifikate:client_cert|3.2 Klijentski certifikat]] | Kreiranje klijentskog certifikata |
| **Povezano** | [[.:smartcard_login|9.2 Smartcard prijava]] | Hardverski token |
| **Povezano** | [[hr:int:pqcrypt:szenarien:tls:mtls_deployment|10.3 mTLS Deployment]] | TLS konfiguracija |
----
<< [[.:start|← Pregled autentifikacije]] | [[hr:int:pqcrypt:szenarien:start|↑ Scenariji]] | [[.:smartcard_login|9.2 Smartcard prijava →]] >>
{{tag>scenarij autentifikacija mtls klijentski-certifikat zero-trust}}
----
//Wolfgang van der Stille @ EMSR DATA d.o.o. - Post-Quantum Cryptography Professional//