using System; using System.IO; using System.Security.Cryptography; using System.Security.Cryptography.X509Certificates; using Microsoft.Extensions.Configuration; namespace SNote.Server.Security; public class CertificateManager { private readonly string _certPath; private readonly string _certPassword = "snote-password"; private X509Certificate2? _certificate; public CertificateManager(IConfiguration configuration) { var path = configuration["SharedCertificatePath"]; if (string.IsNullOrEmpty(path)) { path = Path.Combine(AppContext.BaseDirectory, "snote-shared.pfx"); } _certPath = path; } public X509Certificate2 GetCertificate() { if (_certificate != null) return _certificate; if (File.Exists(_certPath)) { try { var bytes = File.ReadAllBytes(_certPath); // EphemeralKeySet is standard and cross-platform for loading certificates from memory _certificate = new X509Certificate2(bytes, _certPassword, X509KeyStorageFlags.Exportable | X509KeyStorageFlags.EphemeralKeySet); return _certificate; } catch (Exception ex) { Console.WriteLine($"Error loading certificate at {_certPath}: {ex.Message}. Re-generating."); } } // Generate new self-signed certificate _certificate = GenerateCertificate(); return _certificate; } private X509Certificate2 GenerateCertificate() { Console.WriteLine($"Generating a new shared network certificate at {_certPath}..."); using var rsa = RSA.Create(2048); var req = new CertificateRequest("cn=SNoteSharedNetwork", rsa, HashAlgorithmName.SHA256, RSASignaturePadding.Pkcs1); req.CertificateExtensions.Add(new X509KeyUsageExtension(X509KeyUsageFlags.DigitalSignature | X509KeyUsageFlags.KeyEncipherment, true)); var selfSigned = req.CreateSelfSigned(DateTimeOffset.UtcNow.AddDays(-1), DateTimeOffset.UtcNow.AddYears(20)); var pfxBytes = selfSigned.Export(X509ContentType.Pfx, _certPassword); var dir = Path.GetDirectoryName(_certPath); if (!string.IsNullOrEmpty(dir) && !Directory.Exists(dir)) { Directory.CreateDirectory(dir); } File.WriteAllBytes(_certPath, pfxBytes); // Reload it to ensure it contains exportable keys and correct storage flags return new X509Certificate2(pfxBytes, _certPassword, X509KeyStorageFlags.Exportable | X509KeyStorageFlags.EphemeralKeySet); } }