~~NOTOC~~
====== Scenario 10.2: Configurare client TLS ======
**Categoria:** [[.:start|TLS/mTLS]] \\
**Complessità:** Media \\
**Prerequisiti:** Trust Store, certificato client opzionale \\
**Tempo stimato:** 15-20 minuti
----
===== Descrizione =====
Questo scenario descrive la **configurazione di un client TLS** per connessioni sicure a server con certificati Post-Quantum.
**Compiti del client:**
* Validare certificato server
* Configurare Trust Store
* Versione TLS e Cipher Suites
* Opzionale: presentare certificato client
----
===== Workflow =====
flowchart LR
TRUST[Caricare Trust Store] --> CONNECT[Stabilire connessione]
CONNECT --> VERIFY[Verificare cert server]
VERIFY --> CHAIN[Validare chain]
CHAIN --> REV[Verificare revoca]
REV --> OK{Tutto OK?}
OK -->|Si| DATA[Trasferire dati]
OK -->|No| ABORT[Interrompere]
style DATA fill:#e8f5e9
style ABORT fill:#ffebee
----
===== Esempio codice: HttpClient con Trust Store personalizzato =====
using WvdS.Security.Cryptography.X509Certificates.Extensions.PQ;
using System.Net.Security;
using System.Security.Cryptography.X509Certificates;
using var ctx = PqCryptoContext.Initialize();
// Caricare Trust Store personalizzato
var trustedCerts = new X509Certificate2Collection();
trustedCerts.Add(ctx.LoadCertificate("company-root-ca.crt.pem"));
trustedCerts.Add(ctx.LoadCertificate("company-intermediate-ca.crt.pem"));
// Configurare HttpClientHandler
var handler = new HttpClientHandler
{
// Validazione personalizzata certificato server
ServerCertificateCustomValidationCallback = (message, cert, chain, errors) =>
{
// Verificare prima gli errori standard
if (errors == SslPolicyErrors.None)
{
return true;
}
// Per UntrustedRoot: usare Trust Store personalizzato
if (errors == SslPolicyErrors.RemoteCertificateChainErrors)
{
var customChain = new X509Chain();
customChain.ChainPolicy.CustomTrustStore.AddRange(trustedCerts);
customChain.ChainPolicy.TrustMode = X509ChainTrustMode.CustomRootTrust;
customChain.ChainPolicy.RevocationMode = X509RevocationMode.Online;
customChain.ChainPolicy.RevocationFlag = X509RevocationFlag.EntireChain;
bool isValid = customChain.Build(cert);
if (!isValid)
{
foreach (var status in customChain.ChainStatus)
{
Console.WriteLine($"Errore chain: {status.StatusInformation}");
}
}
return isValid;
}
Console.WriteLine($"Errore SSL: {errors}");
return false;
},
// Versione TLS
SslProtocols = SslProtocols.Tls13,
// Verifica revoca
CheckCertificateRevocationList = true
};
using var httpClient = new HttpClient(handler)
{
Timeout = TimeSpan.FromSeconds(30)
};
// Eseguire richiesta
var response = await httpClient.GetAsync("https://api.example.com/data");
Console.WriteLine($"Stato: {response.StatusCode}");
----
===== Esempio codice: Client TLS con SocketsHttpHandler =====
// SocketsHttpHandler per maggiore controllo
var socketsHandler = new SocketsHttpHandler
{
// Connection Pooling
PooledConnectionLifetime = TimeSpan.FromMinutes(15),
PooledConnectionIdleTimeout = TimeSpan.FromMinutes(2),
MaxConnectionsPerServer = 10,
// Configurazione TLS
SslOptions = new SslClientAuthenticationOptions
{
// Forzare TLS 1.3
EnabledSslProtocols = SslProtocols.Tls13,
// Nome server per SNI
TargetHost = "api.example.com",
// Callback validazione certificato
RemoteCertificateValidationCallback = (sender, certificate, chain, errors) =>
{
if (errors == SslPolicyErrors.None) return true;
// Logging
Console.WriteLine($"Certificato: {certificate?.Subject}");
Console.WriteLine($"Errori: {errors}");
return false; // Validazione rigorosa
},
// Protocolli applicazione (ALPN)
ApplicationProtocols = new List
{
SslApplicationProtocol.Http2,
SslApplicationProtocol.Http11
}
}
};
using var client = new HttpClient(socketsHandler);
----
===== Certificate Pinning =====
public class CertificatePinningHandler : HttpClientHandler
{
private readonly HashSet _pinnedCertificates;
public CertificatePinningHandler(params string[] pinnedThumbprints)
{
_pinnedCertificates = new HashSet(
pinnedThumbprints,
StringComparer.OrdinalIgnoreCase
);
ServerCertificateCustomValidationCallback = ValidateServerCertificate;
}
private bool ValidateServerCertificate(
HttpRequestMessage message,
X509Certificate2? cert,
X509Chain? chain,
SslPolicyErrors errors)
{
if (cert == null) return false;
// 1. Validazione standard
if (errors != SslPolicyErrors.None)
{
Console.WriteLine($"Errore SSL Policy: {errors}");
return false;
}
// 2. Verificare pin (Leaf o Intermediate)
bool pinValid = false;
// Verificare certificato leaf
if (_pinnedCertificates.Contains(cert.Thumbprint))
{
pinValid = true;
}
// Cercare nella chain
if (!pinValid && chain != null)
{
foreach (var element in chain.ChainElements)
{
if (_pinnedCertificates.Contains(element.Certificate.Thumbprint))
{
pinValid = true;
break;
}
}
}
if (!pinValid)
{
Console.WriteLine($"Pin certificato non corrispondente: {cert.Thumbprint}");
}
return pinValid;
}
}
// Utilizzo
var pinnedHandler = new CertificatePinningHandler(
"A1B2C3D4E5F6...", // Thumbprint Leaf
"B2C3D4E5F6G7..." // Thumbprint Intermediate (pin di backup)
);
using var client = new HttpClient(pinnedHandler);
----
===== Gestione Trust Store =====
public class TrustStoreManager
{
private readonly X509Certificate2Collection _trustedRoots = new();
private readonly X509Certificate2Collection _trustedIntermediates = new();
public void AddTrustedRoot(string certPath)
{
var cert = new X509Certificate2(certPath);
// Verificare se è un certificato CA
var basicConstraints = cert.Extensions["2.5.29.19"] as X509BasicConstraintsExtension;
if (basicConstraints == null || !basicConstraints.CertificateAuthority)
{
throw new ArgumentException("Non è un certificato CA");
}
_trustedRoots.Add(cert);
Console.WriteLine($"Trusted Root aggiunto: {cert.Subject}");
}
public void AddTrustedIntermediate(string certPath)
{
var cert = new X509Certificate2(certPath);
_trustedIntermediates.Add(cert);
Console.WriteLine($"Trusted Intermediate aggiunto: {cert.Subject}");
}
public X509Chain CreateChain()
{
var chain = new X509Chain();
chain.ChainPolicy.CustomTrustStore.AddRange(_trustedRoots);
chain.ChainPolicy.TrustMode = X509ChainTrustMode.CustomRootTrust;
chain.ChainPolicy.ExtraStore.AddRange(_trustedIntermediates);
chain.ChainPolicy.RevocationMode = X509RevocationMode.Online;
chain.ChainPolicy.RevocationFlag = X509RevocationFlag.EntireChain;
chain.ChainPolicy.UrlRetrievalTimeout = TimeSpan.FromSeconds(15);
return chain;
}
public bool ValidateCertificate(X509Certificate2 cert)
{
using var chain = CreateChain();
return chain.Build(cert);
}
}
----
===== Logica di retry per errori TLS =====
public class ResilientTlsClient
{
private readonly HttpClient _client;
private readonly int _maxRetries;
public ResilientTlsClient(HttpClient client, int maxRetries = 3)
{
_client = client;
_maxRetries = maxRetries;
}
public async Task GetWithRetry(string url)
{
Exception? lastException = null;
for (int attempt = 1; attempt <= _maxRetries; attempt++)
{
try
{
return await _client.GetAsync(url);
}
catch (HttpRequestException ex) when (IsTlsError(ex))
{
lastException = ex;
Console.WriteLine($"Errore TLS (tentativo {attempt}/{_maxRetries}): {ex.Message}");
if (attempt < _maxRetries)
{
await Task.Delay(TimeSpan.FromSeconds(Math.Pow(2, attempt)));
}
}
}
throw new HttpRequestException($"Fallito dopo {_maxRetries} tentativi", lastException);
}
private bool IsTlsError(HttpRequestException ex)
{
return ex.InnerException is AuthenticationException ||
ex.Message.Contains("SSL") ||
ex.Message.Contains("TLS");
}
}
----
===== Requisiti client specifici per settore =====
^ Settore ^ Trust Store ^ Pinning ^ Timeout ^
| **Finanza** | PKI propria | Obbligatorio | 30s |
| **Sanità** | gematik TSL | Raccomandato | 60s |
| **IoT** | Integrato | Obbligatorio | 120s |
| **Enterprise** | Distribuito AD/GPO | Opzionale | 30s |
----
===== Scenari correlati =====
^ Relazione ^ Scenario ^ Descrizione ^
| **Prerequisito** | [[.:server_setup|10.1 Server TLS]] | Configurare server |
| **Estensione** | [[.:mtls_deployment|10.3 Deployment mTLS]] | Aggiungere cert client |
| **Correlato** | [[it:int:pqcrypt:szenarien:validierung:chain_validation|5.2 Validazione Chain]] | Verifica certificati |
----
<< [[.:server_setup|← 10.1 Server TLS]] | [[.:start|↑ Panoramica TLS]] | [[.:mtls_deployment|10.3 Deployment mTLS →]] >>
{{tag>scenario tls client httpclient trust-store pinning}}
----
//Wolfgang van der Stille @ EMSR DATA d.o.o. - Post-Quantum Cryptography Professional//