~~NOTOC~~
====== Szenario 10.2: TLS-Client konfigurieren ======
**Kategorie:** [[.:start|TLS/mTLS]] \\
**Komplexität:** ⭐⭐⭐ (Mittel) \\
**Voraussetzungen:** Trust Store, optional Client-Zertifikat \\
**Geschätzte Zeit:** 15-20 Minuten
----
===== Beschreibung =====
Dieses Szenario beschreibt die **Konfiguration eines TLS-Clients** für sichere Verbindungen zu Servern mit Post-Quantum-Zertifikaten.
**Aufgaben des Clients:**
* Server-Zertifikat validieren
* Trust Store konfigurieren
* TLS-Version und Cipher Suites
* Optional: Client-Zertifikat präsentieren
----
===== Workflow =====
flowchart LR
TRUST[Trust Store laden] --> CONNECT[Verbindung aufbauen]
CONNECT --> VERIFY[Server-Cert prüfen]
VERIFY --> CHAIN[Chain validieren]
CHAIN --> REV[Revocation prüfen]
REV --> OK{Alles OK?}
OK -->|Ja| DATA[Daten übertragen]
OK -->|Nein| ABORT[Abbrechen]
style DATA fill:#e8f5e9
style ABORT fill:#ffebee
----
===== Code-Beispiel: HttpClient mit Custom Trust Store =====
using WvdS.Security.Cryptography.X509Certificates.Extensions.PQ;
using System.Net.Security;
using System.Security.Cryptography.X509Certificates;
using var ctx = PqCryptoContext.Initialize();
// Custom Trust Store laden
var trustedCerts = new X509Certificate2Collection();
trustedCerts.Add(ctx.LoadCertificate("company-root-ca.crt.pem"));
trustedCerts.Add(ctx.LoadCertificate("company-intermediate-ca.crt.pem"));
// HttpClientHandler konfigurieren
var handler = new HttpClientHandler
{
// Custom Server-Zertifikats-Validierung
ServerCertificateCustomValidationCallback = (message, cert, chain, errors) =>
{
// Standard-Fehler zuerst prüfen
if (errors == SslPolicyErrors.None)
{
return true;
}
// Bei UntrustedRoot: Custom Trust Store verwenden
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($"Chain error: {status.StatusInformation}");
}
}
return isValid;
}
Console.WriteLine($"SSL Error: {errors}");
return false;
},
// TLS-Version
SslProtocols = SslProtocols.Tls13,
// Revocation-Prüfung
CheckCertificateRevocationList = true
};
using var httpClient = new HttpClient(handler)
{
Timeout = TimeSpan.FromSeconds(30)
};
// Request ausführen
var response = await httpClient.GetAsync("https://api.example.com/data");
Console.WriteLine($"Status: {response.StatusCode}");
----
===== Code-Beispiel: TLS-Client mit SocketsHttpHandler =====
// SocketsHttpHandler für mehr Kontrolle
var socketsHandler = new SocketsHttpHandler
{
// Connection Pooling
PooledConnectionLifetime = TimeSpan.FromMinutes(15),
PooledConnectionIdleTimeout = TimeSpan.FromMinutes(2),
MaxConnectionsPerServer = 10,
// TLS Konfiguration
SslOptions = new SslClientAuthenticationOptions
{
// TLS 1.3 erzwingen
EnabledSslProtocols = SslProtocols.Tls13,
// Server-Name für SNI
TargetHost = "api.example.com",
// Certificate Validation Callback
RemoteCertificateValidationCallback = (sender, certificate, chain, errors) =>
{
if (errors == SslPolicyErrors.None) return true;
// Logging
Console.WriteLine($"Certificate: {certificate?.Subject}");
Console.WriteLine($"Errors: {errors}");
return false; // Strenge Validierung
},
// Application Protocols (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. Standard-Validierung
if (errors != SslPolicyErrors.None)
{
Console.WriteLine($"SSL Policy Error: {errors}");
return false;
}
// 2. Pin prüfen (Leaf oder Intermediate)
bool pinValid = false;
// Leaf-Zertifikat prüfen
if (_pinnedCertificates.Contains(cert.Thumbprint))
{
pinValid = true;
}
// Chain durchsuchen
if (!pinValid && chain != null)
{
foreach (var element in chain.ChainElements)
{
if (_pinnedCertificates.Contains(element.Certificate.Thumbprint))
{
pinValid = true;
break;
}
}
}
if (!pinValid)
{
Console.WriteLine($"Certificate pin mismatch: {cert.Thumbprint}");
}
return pinValid;
}
}
// Verwendung
var pinnedHandler = new CertificatePinningHandler(
"A1B2C3D4E5F6...", // Leaf Thumbprint
"B2C3D4E5F6G7..." // Intermediate Thumbprint (Backup-Pin)
);
using var client = new HttpClient(pinnedHandler);
----
===== Trust Store Management =====
public class TrustStoreManager
{
private readonly X509Certificate2Collection _trustedRoots = new();
private readonly X509Certificate2Collection _trustedIntermediates = new();
public void AddTrustedRoot(string certPath)
{
var cert = new X509Certificate2(certPath);
// Prüfen ob es ein CA-Zertifikat ist
var basicConstraints = cert.Extensions["2.5.29.19"] as X509BasicConstraintsExtension;
if (basicConstraints == null || !basicConstraints.CertificateAuthority)
{
throw new ArgumentException("Kein CA-Zertifikat");
}
_trustedRoots.Add(cert);
Console.WriteLine($"Trusted Root hinzugefügt: {cert.Subject}");
}
public void AddTrustedIntermediate(string certPath)
{
var cert = new X509Certificate2(certPath);
_trustedIntermediates.Add(cert);
Console.WriteLine($"Trusted Intermediate hinzugefügt: {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);
}
}
----
===== Retry-Logik bei TLS-Fehlern =====
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($"TLS error (attempt {attempt}/{_maxRetries}): {ex.Message}");
if (attempt < _maxRetries)
{
await Task.Delay(TimeSpan.FromSeconds(Math.Pow(2, attempt)));
}
}
}
throw new HttpRequestException($"Failed after {_maxRetries} attempts", lastException);
}
private bool IsTlsError(HttpRequestException ex)
{
return ex.InnerException is AuthenticationException ||
ex.Message.Contains("SSL") ||
ex.Message.Contains("TLS");
}
}
----
===== Branchenspezifische Client-Anforderungen =====
^ Branche ^ Trust Store ^ Pinning ^ Timeout ^
| **Finanz** | Eigene PKI | Pflicht | 30s |
| **Healthcare** | gematik TSL | Empfohlen | 60s |
| **IoT** | Eingebettet | Pflicht | 120s |
| **Enterprise** | AD/GPO verteilt | Optional | 30s |
----
===== Verwandte Szenarien =====
^ Beziehung ^ Szenario ^ Beschreibung ^
| **Voraussetzung** | [[.:server_setup|10.1 TLS-Server]] | Server konfigurieren |
| **Erweiterung** | [[.:mtls_deployment|10.3 mTLS Deployment]] | Client-Cert hinzufügen |
| **Verwandt** | [[de:int:pqcrypt:szenarien:validierung:chain_validation|5.2 Chain Validation]] | Cert-Prüfung |
----
<< [[.:server_setup|← 10.1 TLS-Server]] | [[.:start|↑ TLS-Übersicht]] | [[.:mtls_deployment|10.3 mTLS Deployment →]] >>
{{tag>szenario tls client httpclient trust-store pinning}}
----
//Wolfgang van der Stille @ EMSR DATA d.o.o. - Post-Quantum Cryptography Professional//