123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159 |
- using Org.BouncyCastle.Asn1;
- using Org.BouncyCastle.Asn1.Pkcs;
- 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.IO;
- using System.Linq;
- using System.Text;
- using X509Certificate2 = System.Security.Cryptography.X509Certificates.X509Certificate2;
- namespace FastGithub.ReverseProxy
- {
- /// <summary>
- /// 证书生成器
- /// </summary>
- static class CertGenerator
- {
- private static readonly SecureRandom secureRandom = new();
- /// <summary>
- /// 生成CA签名证书
- /// </summary>
- /// <param name="domains"></param>
- /// <param name="keySizeBits"></param>
- /// <param name="validFrom"></param>
- /// <param name="validTo"></param>
- /// <param name="caPublicCerPath"></param>
- /// <param name="caPrivateKeyPath"></param>
- /// <returns></returns>
- public static X509Certificate2 Generate(IEnumerable<string> domains, int keySizeBits, DateTime validFrom, DateTime validTo, string caPublicCerPath, string caPrivateKeyPath)
- {
- if (File.Exists(caPublicCerPath) == false)
- {
- throw new FileNotFoundException(caPublicCerPath);
- }
- if (File.Exists(caPrivateKeyPath) == false)
- {
- throw new FileNotFoundException(caPublicCerPath);
- }
- using var pubReader = new StreamReader(caPublicCerPath, Encoding.ASCII);
- var caCert = (X509Certificate)new PemReader(pubReader).ReadObject();
- using var priReader = new StreamReader(caPrivateKeyPath, Encoding.ASCII);
- var reader = new PemReader(priReader);
- var caPrivateKey = ((AsymmetricCipherKeyPair)reader.ReadObject()).Private;
- 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: null);
- }
- /// <summary>
- /// 生成私钥
- /// </summary>
- /// <param name="length"></param>
- /// <returns></returns>
- private static AsymmetricCipherKeyPair GenerateRsaKeyPair(int length)
- {
- var keygenParam = new KeyGenerationParameters(secureRandom, length);
- 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="CA_PathLengthConstraint"></param>
- /// <returns></returns>
- private static X509Certificate GenerateCertificate(IEnumerable<string> domains, AsymmetricKeyParameter subjectPublic, DateTime validFrom, DateTime validTo, string issuerName, AsymmetricKeyParameter issuerPublic, AsymmetricKeyParameter issuerPrivate, int? CA_PathLengthConstraint)
- {
- 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);
- certGenerator.AddExtension(X509Extensions.AuthorityKeyIdentifier, false, akis);
- }
- if (CA_PathLengthConstraint != null && CA_PathLengthConstraint >= 0)
- {
- var extension = new X509Extension(true, new DerOctetString(new BasicConstraints(CA_PathLengthConstraint.Value)));
- certGenerator.AddExtension(X509Extensions.BasicConstraints, extension.IsCritical, extension.GetParsedValue());
- }
- var names = domains.Select(domain => new GeneralName(GeneralName.DnsName, domain)).ToArray();
- var subjectAltName = new GeneralNames(names);
- certGenerator.AddExtension(X509Extensions.SubjectAlternativeName, false, subjectAltName);
- return certGenerator.Generate(signatureFactory);
- }
- /// <summary>
- /// 生成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);
- var pkcs12Store = new Pkcs12Store();
- var certEntry = new X509CertificateEntry(cert);
- pkcs12Store.SetCertificateEntry(subject, certEntry);
- pkcs12Store.SetKeyEntry(subject, new AsymmetricKeyEntry(privateKey), new[] { certEntry });
- using var pfxStream = new MemoryStream();
- pkcs12Store.Save(pfxStream, password?.ToCharArray(), secureRandom);
- 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;
- }
- }
- }
|