使用基础库实现CertGenerator
This commit is contained in:
parent
ddf63b315f
commit
0ddc22a299
@ -1,23 +1,9 @@
|
|||||||
using Org.BouncyCastle.Asn1.Pkcs;
|
using System;
|
||||||
using Org.BouncyCastle.Asn1.X509;
|
|
||||||
using Org.BouncyCastle.Asn1.X9;
|
|
||||||
using Org.BouncyCastle.Crypto;
|
|
||||||
using Org.BouncyCastle.Crypto.Generators;
|
|
||||||
using Org.BouncyCastle.Crypto.Operators;
|
|
||||||
using Org.BouncyCastle.Crypto.Parameters;
|
|
||||||
using Org.BouncyCastle.Math;
|
|
||||||
using Org.BouncyCastle.OpenSsl;
|
|
||||||
using Org.BouncyCastle.Pkcs;
|
|
||||||
using Org.BouncyCastle.Security;
|
|
||||||
using Org.BouncyCastle.X509;
|
|
||||||
using Org.BouncyCastle.X509.Extension;
|
|
||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.IO;
|
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Net;
|
using System.Net;
|
||||||
using System.Text;
|
using System.Security.Cryptography;
|
||||||
using X509Certificate2 = System.Security.Cryptography.X509Certificates.X509Certificate2;
|
using System.Security.Cryptography.X509Certificates;
|
||||||
|
|
||||||
namespace FastGithub.HttpServer.Certs
|
namespace FastGithub.HttpServer.Certs
|
||||||
{
|
{
|
||||||
@ -26,178 +12,145 @@ namespace FastGithub.HttpServer.Certs
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
static class CertGenerator
|
static class CertGenerator
|
||||||
{
|
{
|
||||||
private static readonly SecureRandom secureRandom = new();
|
private static readonly Oid tlsServerOid = new("1.3.6.1.5.5.7.3.1");
|
||||||
|
private static readonly Oid tlsClientOid = new("1.3.6.1.5.5.7.3.2");
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 生成自签名证书
|
/// 生成ca证书
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="domains"></param>
|
/// <param name="subjectName"></param>
|
||||||
/// <param name="keySizeBits"></param>
|
/// <param name="notBefore"></param>
|
||||||
/// <param name="validFrom"></param>
|
/// <param name="notAfter"></param>
|
||||||
/// <param name="validTo"></param>
|
/// <param name="rsaKeySizeInBits"></param>
|
||||||
/// <param name="caPublicCerPath"></param>
|
/// <param name="pathLengthConstraint"></param>
|
||||||
/// <param name="caPrivateKeyPath"></param>
|
/// <returns></returns>
|
||||||
public static void GenerateBySelf(IEnumerable<string> domains, int keySizeBits, DateTime validFrom, DateTime validTo, string caPublicCerPath, string caPrivateKeyPath)
|
public static X509Certificate2 CreateCACertificate(
|
||||||
|
X500DistinguishedName subjectName,
|
||||||
|
DateTimeOffset notBefore,
|
||||||
|
DateTimeOffset notAfter,
|
||||||
|
int rsaKeySizeInBits = 2048,
|
||||||
|
int pathLengthConstraint = 1)
|
||||||
{
|
{
|
||||||
var keys = GenerateRsaKeyPair(keySizeBits);
|
using var rsa = RSA.Create(rsaKeySizeInBits);
|
||||||
var cert = GenerateCertificate(domains, keys.Public, validFrom, validTo, domains.First(), null, keys.Private, 1);
|
var request = new CertificateRequest(subjectName, rsa, HashAlgorithmName.SHA256, RSASignaturePadding.Pkcs1);
|
||||||
|
|
||||||
using var priWriter = new StreamWriter(caPrivateKeyPath);
|
var basicConstraints = new X509BasicConstraintsExtension(true, pathLengthConstraint > 0, pathLengthConstraint, true);
|
||||||
var priPemWriter = new PemWriter(priWriter);
|
request.CertificateExtensions.Add(basicConstraints);
|
||||||
priPemWriter.WriteObject(keys.Private);
|
|
||||||
priPemWriter.Writer.Flush();
|
|
||||||
|
|
||||||
using var pubWriter = new StreamWriter(caPublicCerPath);
|
var keyUsage = new X509KeyUsageExtension(X509KeyUsageFlags.DigitalSignature | X509KeyUsageFlags.CrlSign | X509KeyUsageFlags.KeyCertSign, true);
|
||||||
var pubPemWriter = new PemWriter(pubWriter);
|
request.CertificateExtensions.Add(keyUsage);
|
||||||
pubPemWriter.WriteObject(cert);
|
|
||||||
pubPemWriter.Writer.Flush();
|
var oids = new OidCollection { tlsServerOid, tlsClientOid };
|
||||||
|
var enhancedKeyUsage = new X509EnhancedKeyUsageExtension(oids, true);
|
||||||
|
request.CertificateExtensions.Add(enhancedKeyUsage);
|
||||||
|
|
||||||
|
var dnsBuilder = new SubjectAlternativeNameBuilder();
|
||||||
|
dnsBuilder.Add(subjectName.Name[3..]);
|
||||||
|
request.CertificateExtensions.Add(dnsBuilder.Build());
|
||||||
|
|
||||||
|
var subjectKeyId = new X509SubjectKeyIdentifierExtension(request.PublicKey, false);
|
||||||
|
request.CertificateExtensions.Add(subjectKeyId);
|
||||||
|
|
||||||
|
return request.CreateSelfSigned(notBefore, notAfter);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 生成CA签名证书
|
/// 生成服务器证书
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="domains"></param>
|
/// <param name="issuerCertificate"></param>
|
||||||
/// <param name="keySizeBits"></param>
|
/// <param name="subjectName"></param>
|
||||||
/// <param name="validFrom"></param>
|
/// <param name="extraDnsNames"></param>
|
||||||
/// <param name="validTo"></param>
|
/// <param name="notBefore"></param>
|
||||||
/// <param name="caPublicCerPath"></param>
|
/// <param name="notAfter"></param>
|
||||||
/// <param name="caPrivateKeyPath"></param>
|
/// <param name="rsaKeySizeInBits"></param>
|
||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
public static X509Certificate2 GenerateByCa(IEnumerable<string> domains, int keySizeBits, DateTime validFrom, DateTime validTo, string caPublicCerPath, string caPrivateKeyPath, string? password = default)
|
public static X509Certificate2 CreateEndCertificate(
|
||||||
|
X509Certificate2 issuerCertificate,
|
||||||
|
X500DistinguishedName subjectName,
|
||||||
|
IEnumerable<string>? extraDnsNames = default,
|
||||||
|
DateTimeOffset? notBefore = default,
|
||||||
|
DateTimeOffset? notAfter = default,
|
||||||
|
int rsaKeySizeInBits = 2048)
|
||||||
{
|
{
|
||||||
if (File.Exists(caPublicCerPath) == false)
|
using var rsa = RSA.Create(rsaKeySizeInBits);
|
||||||
|
var request = new CertificateRequest(subjectName, rsa, HashAlgorithmName.SHA256, RSASignaturePadding.Pkcs1);
|
||||||
|
|
||||||
|
var basicConstraints = new X509BasicConstraintsExtension(false, false, 0, true);
|
||||||
|
request.CertificateExtensions.Add(basicConstraints);
|
||||||
|
|
||||||
|
var keyUsage = new X509KeyUsageExtension(X509KeyUsageFlags.DigitalSignature | X509KeyUsageFlags.KeyEncipherment, true);
|
||||||
|
request.CertificateExtensions.Add(keyUsage);
|
||||||
|
|
||||||
|
var oids = new OidCollection { tlsServerOid, tlsClientOid };
|
||||||
|
var enhancedKeyUsage = new X509EnhancedKeyUsageExtension(oids, true);
|
||||||
|
request.CertificateExtensions.Add(enhancedKeyUsage);
|
||||||
|
|
||||||
|
var authorityKeyId = GetAuthorityKeyIdentifierExtension(issuerCertificate);
|
||||||
|
request.CertificateExtensions.Add(authorityKeyId);
|
||||||
|
|
||||||
|
var subjectKeyId = new X509SubjectKeyIdentifierExtension(request.PublicKey, false);
|
||||||
|
request.CertificateExtensions.Add(subjectKeyId);
|
||||||
|
|
||||||
|
var dnsBuilder = new SubjectAlternativeNameBuilder();
|
||||||
|
dnsBuilder.Add(subjectName.Name[3..]);
|
||||||
|
|
||||||
|
if (extraDnsNames != null)
|
||||||
{
|
{
|
||||||
throw new FileNotFoundException(caPublicCerPath);
|
foreach (var dnsName in extraDnsNames)
|
||||||
|
{
|
||||||
|
dnsBuilder.Add(dnsName);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (File.Exists(caPrivateKeyPath) == false)
|
var dnsNames = dnsBuilder.Build();
|
||||||
|
request.CertificateExtensions.Add(dnsNames);
|
||||||
|
|
||||||
|
if (notBefore == null || notBefore.Value < issuerCertificate.NotBefore)
|
||||||
{
|
{
|
||||||
throw new FileNotFoundException(caPublicCerPath);
|
notBefore = issuerCertificate.NotBefore;
|
||||||
}
|
}
|
||||||
|
|
||||||
using var pubReader = new StreamReader(caPublicCerPath, Encoding.ASCII);
|
if (notAfter == null || notAfter.Value > issuerCertificate.NotAfter)
|
||||||
var caCert = (X509Certificate)new PemReader(pubReader).ReadObject();
|
{
|
||||||
|
notAfter = issuerCertificate.NotAfter;
|
||||||
|
}
|
||||||
|
|
||||||
using var priReader = new StreamReader(caPrivateKeyPath, Encoding.ASCII);
|
var serialNumber = BitConverter.GetBytes(Random.Shared.NextInt64());
|
||||||
var reader = new PemReader(priReader);
|
using var certOnly = request.Create(issuerCertificate, notBefore.Value, notAfter.Value, serialNumber);
|
||||||
var caPrivateKey = ((AsymmetricCipherKeyPair)reader.ReadObject()).Private;
|
return certOnly.CopyWithPrivateKey(rsa);
|
||||||
|
|
||||||
var caSubjectName = GetSubjectName(caCert);
|
|
||||||
var keys = GenerateRsaKeyPair(keySizeBits);
|
|
||||||
var cert = GenerateCertificate(domains, keys.Public, validFrom, validTo, caSubjectName, caCert.GetPublicKey(), caPrivateKey, null);
|
|
||||||
|
|
||||||
return GeneratePfx(cert, keys.Private, password);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 生成私钥
|
|
||||||
/// </summary>
|
private static void Add(this SubjectAlternativeNameBuilder builder, string name)
|
||||||
/// <param name="length"></param>
|
|
||||||
/// <returns></returns>
|
|
||||||
private static AsymmetricCipherKeyPair GenerateRsaKeyPair(int length)
|
|
||||||
{
|
{
|
||||||
var keygenParam = new KeyGenerationParameters(secureRandom, length);
|
if (IPAddress.TryParse(name, out var address))
|
||||||
var keyGenerator = new RsaKeyPairGenerator();
|
|
||||||
keyGenerator.Init(keygenParam);
|
|
||||||
return keyGenerator.GenerateKeyPair();
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 生成证书
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="domains"></param>
|
|
||||||
/// <param name="subjectPublic"></param>
|
|
||||||
/// <param name="validFrom"></param>
|
|
||||||
/// <param name="validTo"></param>
|
|
||||||
/// <param name="issuerName"></param>
|
|
||||||
/// <param name="issuerPublic"></param>
|
|
||||||
/// <param name="issuerPrivate"></param>
|
|
||||||
/// <param name="caPathLengthConstraint"></param>
|
|
||||||
/// <returns></returns>
|
|
||||||
private static X509Certificate GenerateCertificate(IEnumerable<string> domains, AsymmetricKeyParameter subjectPublic, DateTime validFrom, DateTime validTo, string issuerName, AsymmetricKeyParameter? issuerPublic, AsymmetricKeyParameter issuerPrivate, int? caPathLengthConstraint)
|
|
||||||
{
|
|
||||||
var signatureFactory = issuerPrivate is ECPrivateKeyParameters
|
|
||||||
? new Asn1SignatureFactory(X9ObjectIdentifiers.ECDsaWithSha256.ToString(), issuerPrivate)
|
|
||||||
: new Asn1SignatureFactory(PkcsObjectIdentifiers.Sha256WithRsaEncryption.ToString(), issuerPrivate);
|
|
||||||
|
|
||||||
var certGenerator = new X509V3CertificateGenerator();
|
|
||||||
certGenerator.SetIssuerDN(new X509Name("CN=" + issuerName));
|
|
||||||
certGenerator.SetSubjectDN(new X509Name("CN=" + domains.First()));
|
|
||||||
certGenerator.SetSerialNumber(BigInteger.ProbablePrime(120, new Random()));
|
|
||||||
certGenerator.SetNotBefore(validFrom);
|
|
||||||
certGenerator.SetNotAfter(validTo);
|
|
||||||
certGenerator.SetPublicKey(subjectPublic);
|
|
||||||
|
|
||||||
if (issuerPublic != null)
|
|
||||||
{
|
{
|
||||||
var akis = new AuthorityKeyIdentifierStructure(issuerPublic);
|
builder.AddIpAddress(address);
|
||||||
certGenerator.AddExtension(X509Extensions.AuthorityKeyIdentifier, false, akis);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (caPathLengthConstraint != null && caPathLengthConstraint >= 0)
|
|
||||||
{
|
|
||||||
var basicConstraints = new BasicConstraints(caPathLengthConstraint.Value);
|
|
||||||
certGenerator.AddExtension(X509Extensions.BasicConstraints, true, basicConstraints);
|
|
||||||
certGenerator.AddExtension(X509Extensions.KeyUsage, false, new KeyUsage(KeyUsage.DigitalSignature | KeyUsage.CrlSign | KeyUsage.KeyCertSign));
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
var basicConstraints = new BasicConstraints(cA: false);
|
builder.AddDnsName(name);
|
||||||
certGenerator.AddExtension(X509Extensions.BasicConstraints, true, basicConstraints);
|
|
||||||
certGenerator.AddExtension(X509Extensions.KeyUsage, false, new KeyUsage(KeyUsage.DigitalSignature | KeyUsage.KeyEncipherment));
|
|
||||||
}
|
}
|
||||||
certGenerator.AddExtension(X509Extensions.ExtendedKeyUsage, true, new ExtendedKeyUsage(KeyPurposeID.IdKPServerAuth));
|
|
||||||
|
|
||||||
var names = domains.Select(domain =>
|
|
||||||
{
|
|
||||||
var nameType = GeneralName.DnsName;
|
|
||||||
if (IPAddress.TryParse(domain, out _))
|
|
||||||
{
|
|
||||||
nameType = GeneralName.IPAddress;
|
|
||||||
}
|
|
||||||
return new GeneralName(nameType, domain);
|
|
||||||
}).ToArray();
|
|
||||||
|
|
||||||
var subjectAltName = new GeneralNames(names);
|
|
||||||
certGenerator.AddExtension(X509Extensions.SubjectAlternativeName, false, subjectAltName);
|
|
||||||
return certGenerator.Generate(signatureFactory);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/// <summary>
|
private static X509Extension GetAuthorityKeyIdentifierExtension(X509Certificate2 certificate)
|
||||||
/// 生成pfx
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="cert"></param>
|
|
||||||
/// <param name="privateKey"></param>
|
|
||||||
/// <param name="password"></param>
|
|
||||||
/// <returns></returns>
|
|
||||||
private static X509Certificate2 GeneratePfx(X509Certificate cert, AsymmetricKeyParameter privateKey, string? password)
|
|
||||||
{
|
{
|
||||||
var subject = GetSubjectName(cert);
|
#if NET7_0_OR_GREATER
|
||||||
var pkcs12Store = new Pkcs12Store();
|
return X509AuthorityKeyIdentifierExtension.CreateFromCertificate(certificate, true, false);
|
||||||
var certEntry = new X509CertificateEntry(cert);
|
#else
|
||||||
pkcs12Store.SetCertificateEntry(subject, certEntry);
|
var extension = certificate.Extensions.OfType<X509SubjectKeyIdentifierExtension>().First();
|
||||||
pkcs12Store.SetKeyEntry(subject, new AsymmetricKeyEntry(privateKey), new[] { certEntry });
|
var subjectKeyIdentifier = extension.RawData.AsSpan(2);
|
||||||
|
var rawData = new byte[subjectKeyIdentifier.Length + 4];
|
||||||
|
rawData[0] = 0x30;
|
||||||
|
rawData[1] = 0x16;
|
||||||
|
rawData[2] = 0x80;
|
||||||
|
rawData[3] = 0x14;
|
||||||
|
subjectKeyIdentifier.CopyTo(rawData);
|
||||||
|
|
||||||
using var pfxStream = new MemoryStream();
|
return new X509Extension("2.5.29.35", rawData, false);
|
||||||
pkcs12Store.Save(pfxStream, password?.ToCharArray(), secureRandom);
|
#endif
|
||||||
return new X509Certificate2(pfxStream.ToArray());
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 获取Subject
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="cert"></param>
|
|
||||||
/// <returns></returns>
|
|
||||||
private static string GetSubjectName(X509Certificate cert)
|
|
||||||
{
|
|
||||||
var subject = cert.SubjectDN.ToString();
|
|
||||||
if (subject.StartsWith("CN=", StringComparison.OrdinalIgnoreCase))
|
|
||||||
{
|
|
||||||
subject = subject[3..];
|
|
||||||
}
|
|
||||||
return subject;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -6,7 +6,9 @@ using System.Diagnostics;
|
|||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Net;
|
using System.Net;
|
||||||
|
using System.Security.Cryptography;
|
||||||
using System.Security.Cryptography.X509Certificates;
|
using System.Security.Cryptography.X509Certificates;
|
||||||
|
using System.Text;
|
||||||
|
|
||||||
namespace FastGithub.HttpServer.Certs
|
namespace FastGithub.HttpServer.Certs
|
||||||
{
|
{
|
||||||
@ -16,10 +18,10 @@ namespace FastGithub.HttpServer.Certs
|
|||||||
sealed class CertService
|
sealed class CertService
|
||||||
{
|
{
|
||||||
private const string CACERT_PATH = "cacert";
|
private const string CACERT_PATH = "cacert";
|
||||||
private const int KEY_SIZE_BITS = 2048;
|
|
||||||
private readonly IMemoryCache serverCertCache;
|
private readonly IMemoryCache serverCertCache;
|
||||||
private readonly IEnumerable<ICaCertInstaller> certInstallers;
|
private readonly IEnumerable<ICaCertInstaller> certInstallers;
|
||||||
private readonly ILogger<CertService> logger;
|
private readonly ILogger<CertService> logger;
|
||||||
|
private X509Certificate2? caCert;
|
||||||
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@ -54,17 +56,28 @@ namespace FastGithub.HttpServer.Certs
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public bool CreateCaCertIfNotExists()
|
public bool CreateCaCertIfNotExists()
|
||||||
{
|
{
|
||||||
if (File.Exists(CaCerFilePath) && File.Exists(CaKeyFilePath))
|
if (File.Exists(this.CaCerFilePath) && File.Exists(this.CaKeyFilePath))
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
File.Delete(CaCerFilePath);
|
File.Delete(this.CaCerFilePath);
|
||||||
File.Delete(CaKeyFilePath);
|
File.Delete(this.CaKeyFilePath);
|
||||||
|
|
||||||
|
var notBefore = DateTimeOffset.Now.AddDays(-1);
|
||||||
|
var notAfter = DateTimeOffset.Now.AddYears(10);
|
||||||
|
|
||||||
|
var subjectName = new X500DistinguishedName($"CN={nameof(FastGithub)}");
|
||||||
|
this.caCert = CertGenerator.CreateCACertificate(subjectName, notBefore, notAfter);
|
||||||
|
|
||||||
|
var privateKey = this.caCert.GetRSAPrivateKey()?.ExportRSAPrivateKey();
|
||||||
|
var privateKeyPem = PemEncoding.Write("RSA PRIVATE KEY", privateKey);
|
||||||
|
File.WriteAllText(this.CaKeyFilePath, new string(privateKeyPem), Encoding.ASCII);
|
||||||
|
|
||||||
|
var cert = this.caCert.Export(X509ContentType.Cert);
|
||||||
|
var certPem = PemEncoding.Write("CERTIFICATE", cert);
|
||||||
|
File.WriteAllText(this.CaCerFilePath, new string(certPem), Encoding.ASCII);
|
||||||
|
|
||||||
var validFrom = DateTime.Today.AddDays(-1);
|
|
||||||
var validTo = DateTime.Today.AddYears(10);
|
|
||||||
CertGenerator.GenerateBySelf(new[] { nameof(FastGithub) }, KEY_SIZE_BITS, validFrom, validTo, CaCerFilePath, CaKeyFilePath);
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -73,14 +86,14 @@ namespace FastGithub.HttpServer.Certs
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public void InstallAndTrustCaCert()
|
public void InstallAndTrustCaCert()
|
||||||
{
|
{
|
||||||
var installer = certInstallers.FirstOrDefault(item => item.IsSupported());
|
var installer = this.certInstallers.FirstOrDefault(item => item.IsSupported());
|
||||||
if (installer != null)
|
if (installer != null)
|
||||||
{
|
{
|
||||||
installer.Install(CaCerFilePath);
|
installer.Install(this.CaCerFilePath);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
logger.LogWarning($"请根据你的系统平台手动安装和信任CA证书{CaCerFilePath}");
|
this.logger.LogWarning($"请根据你的系统平台手动安装和信任CA证书{this.CaCerFilePath}");
|
||||||
}
|
}
|
||||||
|
|
||||||
GitConfigSslverify(false);
|
GitConfigSslverify(false);
|
||||||
@ -118,18 +131,31 @@ namespace FastGithub.HttpServer.Certs
|
|||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
public X509Certificate2 GetOrCreateServerCert(string? domain)
|
public X509Certificate2 GetOrCreateServerCert(string? domain)
|
||||||
{
|
{
|
||||||
|
if (this.caCert == null)
|
||||||
|
{
|
||||||
|
using var rsa = RSA.Create();
|
||||||
|
rsa.ImportFromPem(File.ReadAllText(this.CaKeyFilePath));
|
||||||
|
this.caCert = new X509Certificate2(this.CaCerFilePath).CopyWithPrivateKey(rsa);
|
||||||
|
}
|
||||||
|
|
||||||
var key = $"{nameof(CertService)}:{domain}";
|
var key = $"{nameof(CertService)}:{domain}";
|
||||||
return serverCertCache.GetOrCreate(key, GetOrCreateCert);
|
var endCert = this.serverCertCache.GetOrCreate(key, GetOrCreateCert);
|
||||||
|
return endCert!;
|
||||||
|
|
||||||
// 生成域名的1年证书
|
// 生成域名的1年证书
|
||||||
X509Certificate2 GetOrCreateCert(ICacheEntry entry)
|
X509Certificate2 GetOrCreateCert(ICacheEntry entry)
|
||||||
{
|
{
|
||||||
var domains = GetDomains(domain).Distinct();
|
var notBefore = DateTimeOffset.Now.AddDays(-1);
|
||||||
var validFrom = DateTime.Today.AddDays(-1);
|
var notAfter = DateTimeOffset.Now.AddYears(1);
|
||||||
var validTo = DateTime.Today.AddYears(1);
|
entry.SetAbsoluteExpiration(notAfter);
|
||||||
|
|
||||||
entry.SetAbsoluteExpiration(validTo);
|
var extraDomains = GetExtraDomains();
|
||||||
return CertGenerator.GenerateByCa(domains, KEY_SIZE_BITS, validFrom, validTo, CaCerFilePath, CaKeyFilePath);
|
|
||||||
|
var subjectName = new X500DistinguishedName($"CN={domain}");
|
||||||
|
var endCert = CertGenerator.CreateEndCertificate(this.caCert, subjectName, extraDomains, notBefore, notAfter);
|
||||||
|
|
||||||
|
// 重新初始化证书,以兼容win平台不能使用内存证书
|
||||||
|
return new X509Certificate2(endCert.Export(X509ContentType.Pfx));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -138,14 +164,8 @@ namespace FastGithub.HttpServer.Certs
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="domain"></param>
|
/// <param name="domain"></param>
|
||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
private static IEnumerable<string> GetDomains(string? domain)
|
private static IEnumerable<string> GetExtraDomains()
|
||||||
{
|
{
|
||||||
if (string.IsNullOrEmpty(domain) == false)
|
|
||||||
{
|
|
||||||
yield return domain;
|
|
||||||
yield break;
|
|
||||||
}
|
|
||||||
|
|
||||||
yield return Environment.MachineName;
|
yield return Environment.MachineName;
|
||||||
yield return IPAddress.Loopback.ToString();
|
yield return IPAddress.Loopback.ToString();
|
||||||
yield return IPAddress.IPv6Loopback.ToString();
|
yield return IPAddress.IPv6Loopback.ToString();
|
||||||
|
|||||||
@ -6,7 +6,6 @@
|
|||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<FrameworkReference Include="Microsoft.AspNetCore.App" />
|
<FrameworkReference Include="Microsoft.AspNetCore.App" />
|
||||||
<PackageReference Include="Portable.BouncyCastle" Version="1.9.0" />
|
|
||||||
<PackageReference Include="Yarp.ReverseProxy" Version="1.1.0" />
|
<PackageReference Include="Yarp.ReverseProxy" Version="1.1.0" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user