~~NOTOC~~
====== Scenario 9.1: Autenticazione client mTLS ======
**Categoria:** [[.:start|Autenticazione]] \\
**Complessita:** ⭐⭐⭐⭐ (Alta) \\
**Prerequisiti:** Certificati client e server \\
**Tempo stimato:** 20-30 minuti
----
===== Descrizione =====
Questo scenario descrive l'**autenticazione Mutual TLS (mTLS)**, in cui sia il server che il client si autenticano reciprocamente con certificati.
**Vantaggi rispetto all'autenticazione con password:**
* **Piu forte** - Basata su crittografia
* **Automatizzabile** - Nessuna interazione utente
* **Revocabile** - Possibilita di revoca centralizzata
* **Zero-Trust** - Ogni richiesta e autenticata
----
===== Workflow =====
sequenceDiagram
participant Client
participant Server
Client->>Server: ClientHello
Server->>Client: ServerHello + ServerCert
Server->>Client: CertificateRequest
Client->>Server: ClientCert + CertificateVerify
Server->>Server: Validare certificato client
Server->>Client: Finished
Client->>Server: Finished
Note over Client,Server: Connessione mTLS stabilita
----
===== Esempio di codice: Server mTLS (ASP.NET Core) =====
using Microsoft.AspNetCore.Authentication.Certificate;
using Microsoft.AspNetCore.Server.Kestrel.Https;
var builder = WebApplication.CreateBuilder(args);
// Configurare Kestrel per mTLS
builder.WebHost.ConfigureKestrel(options =>
{
options.ConfigureHttpsDefaults(https =>
{
// Caricare certificato server
https.ServerCertificate = new X509Certificate2(
"server.pfx", "ServerPassword!");
// Richiedere certificato client
https.ClientCertificateMode = ClientCertificateMode.RequireCertificate;
// Validare certificato client
https.ClientCertificateValidation = (cert, chain, errors) =>
{
// Validazione personalizzata
if (errors != SslPolicyErrors.None)
{
Console.WriteLine($"SSL Policy Errors: {errors}");
return false;
}
// Verificare propria 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);
};
});
});
// Aggiungere Certificate Authentication
builder.Services.AddAuthentication(CertificateAuthenticationDefaults.AuthenticationScheme)
.AddCertificate(options =>
{
options.AllowedCertificateTypes = CertificateTypes.Chained;
options.RevocationMode = X509RevocationMode.Online;
options.Events = new CertificateAuthenticationEvents
{
OnCertificateValidated = context =>
{
// Estrarre claims dal certificato
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($"Auth failed: {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 = "Authenticated", subject });
});
app.Run();
----
===== Esempio di codice: Client mTLS (HttpClient) =====
using WvdS.Security.Cryptography.X509Certificates.Extensions.PQ;
using var ctx = PqCryptoContext.Initialize();
// Caricare certificato e chiave client
var clientCert = ctx.LoadCertificateWithPrivateKey(
"client.crt.pem",
"client.key.pem",
"ClientPassword!"
);
// HttpClientHandler con certificato client
var handler = new HttpClientHandler
{
ClientCertificateOptions = ClientCertificateOption.Manual,
ServerCertificateCustomValidationCallback = (message, cert, chain, errors) =>
{
// Validare certificato server
if (errors != SslPolicyErrors.None)
{
Console.WriteLine($"Server certificate error: {errors}");
// In produzione: return false;
}
return true;
}
};
// Aggiungere certificato client
handler.ClientCertificates.Add(clientCert);
using var httpClient = new HttpClient(handler);
// Inviare richiesta
var response = await httpClient.GetAsync("https://api.example.com/api/protected");
if (response.IsSuccessStatusCode)
{
var content = await response.Content.ReadAsStringAsync();
Console.WriteLine($"Response: {content}");
}
else
{
Console.WriteLine($"Error: {response.StatusCode}");
}
----
===== Autorizzazione basata su certificato =====
public class CertificateAuthorizationHandler : AuthorizationHandler
{
protected override Task HandleRequirementAsync(
AuthorizationHandlerContext context,
CertificateRequirement requirement)
{
// Ottenere certificato dal Principal
var thumbprint = context.User.FindFirst("certificate_thumbprint")?.Value;
var subject = context.User.FindFirst(ClaimTypes.NameIdentifier)?.Value;
if (string.IsNullOrEmpty(thumbprint))
{
return Task.CompletedTask;
}
// Verificare se il certificato e autorizzato
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();
}
// Registrazione
builder.Services.AddAuthorization(options =>
{
options.AddPolicy("ServiceAccounts", policy =>
policy.Requirements.Add(new CertificateRequirement
{
AllowedSubjectPatterns = new List
{
@"^CN=.*\.svc\.cluster\.local$", // Kubernetes services
@"^CN=service-[a-z]+$" // Named services
}
}));
});
builder.Services.AddSingleton();
----
===== Nginx come Reverse Proxy mTLS =====
server {
listen 443 ssl;
server_name api.example.com;
# Certificato server
ssl_certificate /etc/nginx/ssl/server-chain.pem;
ssl_certificate_key /etc/nginx/ssl/server.key.pem;
# Autenticazione certificato client
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 / {
# Passare info certificato client al backend
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;
}
}
----
===== Requisiti mTLS specifici per settore =====
^ Settore ^ Durata certificato ^ Revocation ^ Particolarita ^
| **Kubernetes** | 1 anno | CRL | SPIFFE/SPIRE |
| **Settore finanziario** | 90 giorni | OCSP | Conforme PCI-DSS |
| **Sanita** | 1 anno | CRL + OCSP | Conforme DiGAV |
| **IoT/SCADA** | 5 anni | CRL | Capacita offline |
----
===== Scenari correlati =====
^ Relazione ^ Scenario ^ Descrizione ^
| **Prerequisito** | [[it:int:pqcrypt:szenarien:zertifikate:client_cert|3.2 Certificato client]] | Creare cert client |
| **Correlato** | [[.:smartcard_login|9.2 Smartcard Login]] | Token hardware |
| **Correlato** | [[it:int:pqcrypt:szenarien:tls:mtls_deployment|10.3 Deployment mTLS]] | Configurazione TLS |
----
<< [[.:start|← Panoramica autenticazione]] | [[it:int:pqcrypt:szenarien:start|↑ Scenari]] | [[.:smartcard_login|9.2 Smartcard Login →]] >>
{{tag>scenario autenticazione mtls certificato-client zero-trust}}
----
//Wolfgang van der Stille @ EMSR DATA d.o.o. - Post-Quantum Cryptography Professional//