~~NOTOC~~
====== Scenarij 10.2: Konfiguracija TLS odjemalca ======
**Kategorija:** [[.:start|TLS/mTLS]] \\
**Kompleksnost:** ⭐⭐⭐ (Srednja) \\
**Predpogoji:** Shramba zaupanja, opcijski odjemalčev certifikat \\
**Predviden čas:** 15-20 minut
----
===== Opis =====
Ta scenarij opisuje **konfiguracijo TLS odjemalca** za varne povezave s strežniki s postkvantnimi certifikati.
**Naloge odjemalca:**
* Validacija strežniškega certifikata
* Konfiguracija shrambe zaupanja
* TLS različica in šifrirni nabori
* Opcijsko: Predstavitev odjemalčevega certifikata
----
===== Potek dela =====
flowchart LR
TRUST[Nalaganje shrambe zaupanja] --> CONNECT[Vzpostavitev povezave]
CONNECT --> VERIFY[Preverjanje strežniškega certifikata]
VERIFY --> CHAIN[Validacija verige]
CHAIN --> REV[Preverjanje preklica]
REV --> OK{Vse v redu?}
OK -->|Da| DATA[Prenos podatkov]
OK -->|Ne| ABORT[Prekinitev]
style DATA fill:#e8f5e9
style ABORT fill:#ffebee
----
===== Primer kode: HttpClient s prilagojeno shrambo zaupanja =====
using WvdS.Security.Cryptography.X509Certificates.Extensions.PQ;
using System.Net.Security;
using System.Security.Cryptography.X509Certificates;
using var ctx = PqCryptoContext.Initialize();
// Nalaganje prilagojene shrambe zaupanja
var trustedCerts = new X509Certificate2Collection();
trustedCerts.Add(ctx.LoadCertificate("company-root-ca.crt.pem"));
trustedCerts.Add(ctx.LoadCertificate("company-intermediate-ca.crt.pem"));
// Konfiguracija HttpClientHandler
var handler = new HttpClientHandler
{
// Prilagojena validacija strežniškega certifikata
ServerCertificateCustomValidationCallback = (message, cert, chain, errors) =>
{
// Najprej preverjanje standardnih napak
if (errors == SslPolicyErrors.None)
{
return true;
}
// Pri UntrustedRoot: Uporaba prilagojene shrambe zaupanja
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($"Napaka verige: {status.StatusInformation}");
}
}
return isValid;
}
Console.WriteLine($"SSL napaka: {errors}");
return false;
},
// TLS različica
SslProtocols = SslProtocols.Tls13,
// Preverjanje preklica
CheckCertificateRevocationList = true
};
using var httpClient = new HttpClient(handler)
{
Timeout = TimeSpan.FromSeconds(30)
};
// Izvajanje zahteve
var response = await httpClient.GetAsync("https://api.example.com/data");
Console.WriteLine($"Status: {response.StatusCode}");
----
===== Primer kode: TLS odjemalec s SocketsHttpHandler =====
// SocketsHttpHandler za več nadzora
var socketsHandler = new SocketsHttpHandler
{
// Združevanje povezav
PooledConnectionLifetime = TimeSpan.FromMinutes(15),
PooledConnectionIdleTimeout = TimeSpan.FromMinutes(2),
MaxConnectionsPerServer = 10,
// TLS konfiguracija
SslOptions = new SslClientAuthenticationOptions
{
// Zahtevanje TLS 1.3
EnabledSslProtocols = SslProtocols.Tls13,
// Ime strežnika za SNI
TargetHost = "api.example.com",
// Povratni klic za validacijo certifikata
RemoteCertificateValidationCallback = (sender, certificate, chain, errors) =>
{
if (errors == SslPolicyErrors.None) return true;
// Beleženje
Console.WriteLine($"Certifikat: {certificate?.Subject}");
Console.WriteLine($"Napake: {errors}");
return false; // Stroga validacija
},
// Aplikacijski protokoli (ALPN)
ApplicationProtocols = new List
{
SslApplicationProtocol.Http2,
SslApplicationProtocol.Http11
}
}
};
using var client = new HttpClient(socketsHandler);
----
===== Pripenjanje certifikata =====
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. Standardna validacija
if (errors != SslPolicyErrors.None)
{
Console.WriteLine($"SSL napaka politike: {errors}");
return false;
}
// 2. Preverjanje pripetja (list ali vmesni)
bool pinValid = false;
// Preverjanje list certifikata
if (_pinnedCertificates.Contains(cert.Thumbprint))
{
pinValid = true;
}
// Iskanje po verigi
if (!pinValid && chain != null)
{
foreach (var element in chain.ChainElements)
{
if (_pinnedCertificates.Contains(element.Certificate.Thumbprint))
{
pinValid = true;
break;
}
}
}
if (!pinValid)
{
Console.WriteLine($"Neujemanje pripetja certifikata: {cert.Thumbprint}");
}
return pinValid;
}
}
// Uporaba
var pinnedHandler = new CertificatePinningHandler(
"A1B2C3D4E5F6...", // Prstni odtis list certifikata
"B2C3D4E5F6G7..." // Prstni odtis vmesnega certifikata (rezervno pripetje)
);
using var client = new HttpClient(pinnedHandler);
----
===== Upravljanje shrambe zaupanja =====
public class TrustStoreManager
{
private readonly X509Certificate2Collection _trustedRoots = new();
private readonly X509Certificate2Collection _trustedIntermediates = new();
public void AddTrustedRoot(string certPath)
{
var cert = new X509Certificate2(certPath);
// Preverjanje ali je CA certifikat
var basicConstraints = cert.Extensions["2.5.29.19"] as X509BasicConstraintsExtension;
if (basicConstraints == null || !basicConstraints.CertificateAuthority)
{
throw new ArgumentException("Ni CA certifikat");
}
_trustedRoots.Add(cert);
Console.WriteLine($"Dodan zaupanja vreden korenski certifikat: {cert.Subject}");
}
public void AddTrustedIntermediate(string certPath)
{
var cert = new X509Certificate2(certPath);
_trustedIntermediates.Add(cert);
Console.WriteLine($"Dodan zaupanja vreden vmesni certifikat: {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);
}
}
----
===== Logika ponovnih poskusov pri TLS napakah =====
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 napaka (poskus {attempt}/{_maxRetries}): {ex.Message}");
if (attempt < _maxRetries)
{
await Task.Delay(TimeSpan.FromSeconds(Math.Pow(2, attempt)));
}
}
}
throw new HttpRequestException($"Neuspešno po {_maxRetries} poskusih", lastException);
}
private bool IsTlsError(HttpRequestException ex)
{
return ex.InnerException is AuthenticationException ||
ex.Message.Contains("SSL") ||
ex.Message.Contains("TLS");
}
}
----
===== Panožne zahteve za odjemalce =====
^ Panoga ^ Shramba zaupanja ^ Pripenjanje ^ Časovna omejitev ^
| **Finance** | Lastna PKI | Obvezno | 30 sekund |
| **Zdravstvo** | gematik TSL | Priporočeno | 60 sekund |
| **IoT** | Vgrajeno | Obvezno | 120 sekund |
| **Podjetja** | AD/GPO porazdeljeno | Opcijsko | 30 sekund |
----
===== Povezani scenariji =====
^ Povezava ^ Scenarij ^ Opis ^
| **Predpogoj** | [[.:server_setup|10.1 TLS strežnik]] | Konfiguracija strežnika |
| **Razširitev** | [[.:mtls_deployment|10.3 Uvajanje mTLS]] | Dodajanje odjemalčevega certifikata |
| **Povezano** | [[sl:int:pqcrypt:szenarien:validierung:chain_validation|5.2 Validacija verige]] | Preverjanje certifikata |
----
<< [[.:server_setup|← 10.1 TLS strežnik]] | [[.:start|↑ Pregled TLS]] | [[.:mtls_deployment|10.3 Uvajanje mTLS →]] >>
{{tag>scenarij tls odjemalec httpclient shramba-zaupanja pripenjanje}}
----
//Wolfgang van der Stille @ EMSR DATA d.o.o. - Post-Quantum Cryptography Professional//