72 lines
2.6 KiB
C#
72 lines
2.6 KiB
C#
|
|
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);
|
||
|
|
}
|
||
|
|
}
|