ln.ai.assistant/ln.ai.assistant/CertificateAuthority.cs

138 lines
5.4 KiB
C#

using System.Security.Cryptography;
using System.Security.Cryptography.X509Certificates;
using ln.type;
namespace ln.ai.assistant;
public class CertificateAuthority
{
public string StoragePath { get; }
public string Name { get; }
private X509Certificate2 _privateCertificate;
public CertificateAuthority(string storagePath, string caName)
{
StoragePath = storagePath;
Name = caName;
if (!Directory.Exists(StoragePath))
Directory.CreateDirectory(StoragePath);
Initialize();
}
public void Initialize()
{
string caCertFileName = Path.Combine(StoragePath, ".ca.pem");
string caKeyFileName = Path.Combine(StoragePath, ".ca.key");
if (File.Exists(caCertFileName) && File.Exists(caKeyFileName))
{
_privateCertificate = X509Certificate2.CreateFromPemFile(caCertFileName, caKeyFileName);
}
else
{
using (RSA rsa = RSA.Create(4096))
{
var request = new CertificateRequest($"CN={Name}", rsa, HashAlgorithmName.SHA256, RSASignaturePadding.Pkcs1);
request.CertificateExtensions.Add(new X509KeyUsageExtension(X509KeyUsageFlags.CrlSign | X509KeyUsageFlags.KeyCertSign, false));
request.CertificateExtensions.Add(new X509BasicConstraintsExtension(true, false, 0, false));
_privateCertificate = request.CreateSelfSigned(DateTimeOffset.Now, DateTimeOffset.Now + TimeSpan.FromDays(3650));
WritePemFile(caCertFileName, _privateCertificate);
WritePemFile(caKeyFileName, rsa);
}
}
}
public bool GetOrCreateCertificate(string distinguishedName, out X509Certificate2? certificate)
{
return TryLoadCertificate(distinguishedName, out certificate) ||
CreateSignedCertificate(distinguishedName, out certificate);
}
private bool CreateSignedCertificate(string distinguishedName, out X509Certificate2? certificate)
{
string certFileName = Path.Combine(StoragePath, $"{distinguishedName}.cert.pem");
string keyFileName = Path.Combine(StoragePath, $"{distinguishedName}.key.pem");
using (RSA rsa = RSA.Create(4096))
{
var request = new CertificateRequest($"CN={distinguishedName}", rsa, HashAlgorithmName.SHA256, RSASignaturePadding.Pkcs1);
request.CertificateExtensions.Add(new X509KeyUsageExtension(X509KeyUsageFlags.DataEncipherment | X509KeyUsageFlags.KeyEncipherment |
X509KeyUsageFlags.DigitalSignature, false));
request.CertificateExtensions.Add(
new X509EnhancedKeyUsageExtension(new OidCollection { new Oid("1.3.6.1.5.5.7.3.1") }, false));
var san = new SubjectAlternativeNameBuilder();
san.AddDnsName(distinguishedName);
request.CertificateExtensions.Add(san.Build());
certificate = request.Create(_privateCertificate, DateTimeOffset.Now,
DateTimeOffset.Now + TimeSpan.FromDays(365), BitConverter.GetBytes(DateTime.Now.ToUnixTimeSeconds()));
WritePemFile(certFileName, certificate);
WritePemFile(keyFileName, rsa);
return true;
}
certificate = null;
return false;
}
private bool TryLoadCertificate(string distinguishedName, out X509Certificate2? certificate)
{
string certFileName = Path.Combine(StoragePath, $"{distinguishedName}.cert.pem");
string keyFileName = Path.Combine(StoragePath, $"{distinguishedName}.key.pem");
if (File.Exists(certFileName) && File.Exists(keyFileName))
{
// certificate = new X509Certificate2(X509Certificate2.CreateFromPemFile(certFileName, keyFileName).Export(X509ContentType.Pfx, "alpha"), "alpha");
certificate = X509Certificate2.CreateFromPemFile(certFileName, keyFileName);
return true;
}
certificate = null;
return false;
}
private void WritePemFile(string pemFileName, X509Certificate2 certificate2, RSA rsa)
{
using (var pemFileStream =
new StreamWriter(new FileStream(pemFileName, FileMode.CreateNew)))
{
WritePemFile("CERTIFICATE", certificate2.RawData, pemFileStream);
WritePemFile("RSA PRIVATE KEY", rsa.ExportRSAPrivateKey(), pemFileStream);
}
}
private void WritePemFile(string pemFileName, X509Certificate2 certificate2)
{
using (var pemFileStream =
new StreamWriter(new FileStream(pemFileName, FileMode.CreateNew)))
{
WritePemFile("CERTIFICATE", certificate2.RawData, pemFileStream);
}
}
private void WritePemFile(string pemFileName, RSA rsa)
{
using (var pemFileStream =
new StreamWriter(new FileStream(pemFileName, FileMode.CreateNew)))
{
WritePemFile("RSA PRIVATE KEY", rsa.ExportRSAPrivateKey(), pemFileStream);
}
}
private static void WritePemFile(string type, byte[] data, TextWriter outputFile)
{
outputFile.WriteLine($"-----BEGIN {type}-----");
outputFile.WriteLine(Convert.ToBase64String(data, Base64FormattingOptions.InsertLineBreaks));
outputFile.WriteLine($"-----END {type}-----");
}
}