mirror of
https://github.com/jie65535/GrasscutterCommandGenerator.git
synced 2025-06-08 23:19:14 +08:00
202 lines
7.5 KiB
C#
202 lines
7.5 KiB
C#
#nullable enable
|
|
using System.Collections.Generic;
|
|
using System.IO;
|
|
using System.Linq;
|
|
using System;
|
|
using System.Security.Cryptography;
|
|
using System.Security.Cryptography.X509Certificates;
|
|
|
|
namespace Eavesdrop;
|
|
|
|
public sealed class Certifier : IDisposable
|
|
{
|
|
private readonly X509Store _rootStore, _myStore;
|
|
private readonly IDictionary<string, X509Certificate2> _certificateCache;
|
|
|
|
public string Issuer { get; }
|
|
public string CertificateAuthorityName { get; }
|
|
|
|
public DateTime NotAfter { get; set; }
|
|
public DateTime NotBefore { get; set; }
|
|
public int KeyLength { get; set; } = 1024;
|
|
public bool IsCachingSignedCertificates { get; set; }
|
|
|
|
public X509Certificate2? Authority { get; private set; }
|
|
|
|
public Certifier()
|
|
: this("Eavesdrop")
|
|
{ }
|
|
public Certifier(string issuer)
|
|
: this(issuer, $"{issuer} Root Certificate Authority", StoreLocation.CurrentUser)
|
|
{ }
|
|
public Certifier(string issuer, string certificateAuthorityName)
|
|
: this(issuer, certificateAuthorityName, StoreLocation.CurrentUser)
|
|
{ }
|
|
public Certifier(string issuer, string certificateAuthorityName, StoreLocation location)
|
|
{
|
|
_myStore = new X509Store(StoreName.My, location);
|
|
_rootStore = new X509Store(StoreName.Root, location);
|
|
_certificateCache = new Dictionary<string, X509Certificate2>();
|
|
|
|
NotBefore = DateTime.Now;
|
|
NotAfter = NotBefore.AddMonths(1);
|
|
|
|
Issuer = issuer;
|
|
CertificateAuthorityName = certificateAuthorityName;
|
|
}
|
|
|
|
public bool CreateTrustedRootCertificate()
|
|
{
|
|
return (Authority = InstallCertificate(_rootStore, CertificateAuthorityName)) != null;
|
|
}
|
|
public bool DestroyTrustedRootCertificate()
|
|
{
|
|
return DestroyCertificates(_rootStore);
|
|
}
|
|
public bool ExportTrustedRootCertificate(string path)
|
|
{
|
|
X509Certificate2? rootCertificate = InstallCertificate(_rootStore, CertificateAuthorityName);
|
|
|
|
path = Path.GetFullPath(path);
|
|
if (rootCertificate != null)
|
|
{
|
|
byte[] data = rootCertificate.Export(X509ContentType.Cert);
|
|
File.WriteAllBytes(path, data);
|
|
}
|
|
return File.Exists(path);
|
|
}
|
|
|
|
public X509Certificate2? GenerateCertificate(string certificateName)
|
|
{
|
|
return InstallCertificate(_myStore, certificateName);
|
|
}
|
|
public X509Certificate2 CreateCertificate(string subjectName, string alternateName)
|
|
{
|
|
using var rsa = RSA.Create(KeyLength);
|
|
var certificateRequest = new CertificateRequest(subjectName, rsa, HashAlgorithmName.SHA256, RSASignaturePadding.Pkcs1);
|
|
if (Authority == null)
|
|
{
|
|
certificateRequest.CertificateExtensions.Add(new X509BasicConstraintsExtension(true, false, 0, true));
|
|
certificateRequest.CertificateExtensions.Add(new X509SubjectKeyIdentifierExtension(certificateRequest.PublicKey, false));
|
|
|
|
using X509Certificate2 certificate = certificateRequest.CreateSelfSigned(NotBefore.ToUniversalTime(), NotAfter.ToUniversalTime());
|
|
|
|
certificate.FriendlyName = alternateName;
|
|
return new X509Certificate2(certificate.Export(X509ContentType.Pfx, string.Empty), string.Empty, X509KeyStorageFlags.Exportable | X509KeyStorageFlags.PersistKeySet);
|
|
}
|
|
else
|
|
{
|
|
var sanBuilder = new SubjectAlternativeNameBuilder();
|
|
sanBuilder.AddDnsName(alternateName);
|
|
|
|
certificateRequest.CertificateExtensions.Add(sanBuilder.Build());
|
|
certificateRequest.CertificateExtensions.Add(new X509BasicConstraintsExtension(false, false, 0, false));
|
|
certificateRequest.CertificateExtensions.Add(new X509SubjectKeyIdentifierExtension(certificateRequest.PublicKey, false));
|
|
|
|
using X509Certificate2 certificate = certificateRequest.Create(Authority, Authority.NotBefore, Authority.NotAfter, Guid.NewGuid().ToByteArray());
|
|
using X509Certificate2 certificateWithPrivateKey = certificate.CopyWithPrivateKey(rsa);
|
|
|
|
certificateWithPrivateKey.FriendlyName = alternateName;
|
|
return new X509Certificate2(certificateWithPrivateKey.Export(X509ContentType.Pfx, string.Empty), string.Empty, X509KeyStorageFlags.Exportable | X509KeyStorageFlags.PersistKeySet);
|
|
}
|
|
}
|
|
private X509Certificate2? InstallCertificate(X509Store store, string certificateName)
|
|
{
|
|
if (_certificateCache.TryGetValue(certificateName, out X509Certificate2? certificate))
|
|
{
|
|
if (DateTime.Now >= certificate.NotAfter)
|
|
{
|
|
_certificateCache.Remove(certificateName);
|
|
}
|
|
else return certificate;
|
|
}
|
|
lock (store)
|
|
{
|
|
try
|
|
{
|
|
store.Open(OpenFlags.ReadWrite);
|
|
string subjectName = $"CN={certificateName}, O={Issuer}";
|
|
|
|
certificate = FindCertificates(store, subjectName)?[0];
|
|
if (certificate != null && DateTime.Now >= certificate.NotAfter)
|
|
{
|
|
if (Authority == null)
|
|
{
|
|
DestroyCertificates();
|
|
store.Open(OpenFlags.ReadWrite);
|
|
}
|
|
else
|
|
{
|
|
store.Remove(certificate);
|
|
}
|
|
certificate = null;
|
|
}
|
|
|
|
if (certificate == null)
|
|
{
|
|
certificate = CreateCertificate(subjectName, certificateName);
|
|
if (certificate != null)
|
|
{
|
|
if (store == _rootStore || IsCachingSignedCertificates)
|
|
{
|
|
store.Add(certificate);
|
|
}
|
|
}
|
|
}
|
|
|
|
return certificate;
|
|
}
|
|
catch { return certificate = null; }
|
|
finally
|
|
{
|
|
store.Close();
|
|
if (certificate != null && !_certificateCache.ContainsKey(certificateName))
|
|
{
|
|
_certificateCache.Add(certificateName, certificate);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
public bool DestroyCertificates(X509Store store)
|
|
{
|
|
lock (store)
|
|
{
|
|
try
|
|
{
|
|
store.Open(OpenFlags.ReadWrite);
|
|
X509Certificate2Collection certificates = store.Certificates.Find(X509FindType.FindByIssuerName, Issuer, false);
|
|
|
|
store.RemoveRange(certificates);
|
|
IEnumerable<string> subjectNames = certificates.Cast<X509Certificate2>().Select(c => c.GetNameInfo(X509NameType.SimpleName, false));
|
|
|
|
foreach (string subjectName in subjectNames)
|
|
{
|
|
if (!_certificateCache.ContainsKey(subjectName)) continue;
|
|
_certificateCache.Remove(subjectName);
|
|
}
|
|
return true;
|
|
}
|
|
catch { return false; }
|
|
finally { store.Close(); }
|
|
}
|
|
}
|
|
public bool DestroyCertificates() => DestroyCertificates(_myStore) && DestroyCertificates(_rootStore);
|
|
|
|
private static X509Certificate2Collection? FindCertificates(X509Store store, string subjectName)
|
|
{
|
|
X509Certificate2Collection certificates = store.Certificates
|
|
.Find(X509FindType.FindBySubjectDistinguishedName, subjectName, false);
|
|
|
|
return certificates.Count > 0 ? certificates : null;
|
|
}
|
|
|
|
public void Dispose()
|
|
{
|
|
_myStore.Close();
|
|
_rootStore.Close();
|
|
|
|
_myStore.Dispose();
|
|
_rootStore.Dispose();
|
|
}
|
|
} |