using System.Net.Security; using System.Security.Authentication; using System.Security.Cryptography; using System.Security.Cryptography.X509Certificates; namespace NATS.Server.Tls; public static class TlsHelper { public static X509Certificate2 LoadCertificate(string certPath, string? keyPath) { if (keyPath != null) return X509Certificate2.CreateFromPemFile(certPath, keyPath); return X509CertificateLoader.LoadCertificateFromFile(certPath); } public static X509Certificate2Collection LoadCaCertificates(string caPath) { var collection = new X509Certificate2Collection(); collection.ImportFromPemFile(caPath); return collection; } public static SslServerAuthenticationOptions BuildServerAuthOptions(NatsOptions opts) { var cert = LoadCertificate(opts.TlsCert!, opts.TlsKey); var authOpts = new SslServerAuthenticationOptions { ServerCertificate = cert, EnabledSslProtocols = opts.TlsMinVersion, ClientCertificateRequired = opts.TlsVerify, }; if (opts.TlsVerify && opts.TlsCaCert != null) { var caCerts = LoadCaCertificates(opts.TlsCaCert); authOpts.RemoteCertificateValidationCallback = (_, cert, chain, errors) => { if (cert == null) return false; using var chain2 = new X509Chain(); chain2.ChainPolicy.TrustMode = X509ChainTrustMode.CustomRootTrust; foreach (var ca in caCerts) chain2.ChainPolicy.CustomTrustStore.Add(ca); chain2.ChainPolicy.RevocationMode = X509RevocationMode.NoCheck; var cert2 = cert as X509Certificate2 ?? X509CertificateLoader.LoadCertificate(cert.GetRawCertData()); return chain2.Build(cert2); }; } return authOpts; } public static string GetCertificateHash(X509Certificate2 cert) { var spki = cert.PublicKey.ExportSubjectPublicKeyInfo(); var hash = SHA256.HashData(spki); return Convert.ToHexStringLower(hash); } public static bool MatchesPinnedCert(X509Certificate2 cert, HashSet pinned) { var hash = GetCertificateHash(cert); return pinned.Contains(hash); } }