CertGenerator.cs 8.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203
  1. using Org.BouncyCastle.Asn1.Pkcs;
  2. using Org.BouncyCastle.Asn1.X509;
  3. using Org.BouncyCastle.Asn1.X9;
  4. using Org.BouncyCastle.Crypto;
  5. using Org.BouncyCastle.Crypto.Generators;
  6. using Org.BouncyCastle.Crypto.Operators;
  7. using Org.BouncyCastle.Crypto.Parameters;
  8. using Org.BouncyCastle.Math;
  9. using Org.BouncyCastle.OpenSsl;
  10. using Org.BouncyCastle.Pkcs;
  11. using Org.BouncyCastle.Security;
  12. using Org.BouncyCastle.X509;
  13. using Org.BouncyCastle.X509.Extension;
  14. using System;
  15. using System.Collections.Generic;
  16. using System.IO;
  17. using System.Linq;
  18. using System.Net;
  19. using System.Text;
  20. using X509Certificate2 = System.Security.Cryptography.X509Certificates.X509Certificate2;
  21. namespace FastGithub.HttpServer
  22. {
  23. /// <summary>
  24. /// 证书生成器
  25. /// </summary>
  26. static class CertGenerator
  27. {
  28. private static readonly SecureRandom secureRandom = new();
  29. /// <summary>
  30. /// 生成自签名证书
  31. /// </summary>
  32. /// <param name="domains"></param>
  33. /// <param name="keySizeBits"></param>
  34. /// <param name="validFrom"></param>
  35. /// <param name="validTo"></param>
  36. /// <param name="caPublicCerPath"></param>
  37. /// <param name="caPrivateKeyPath"></param>
  38. public static void GenerateBySelf(IEnumerable<string> domains, int keySizeBits, DateTime validFrom, DateTime validTo, string caPublicCerPath, string caPrivateKeyPath)
  39. {
  40. var keys = GenerateRsaKeyPair(keySizeBits);
  41. var cert = GenerateCertificate(domains, keys.Public, validFrom, validTo, domains.First(), null, keys.Private, 1);
  42. using var priWriter = new StreamWriter(caPrivateKeyPath);
  43. var priPemWriter = new PemWriter(priWriter);
  44. priPemWriter.WriteObject(keys.Private);
  45. priPemWriter.Writer.Flush();
  46. using var pubWriter = new StreamWriter(caPublicCerPath);
  47. var pubPemWriter = new PemWriter(pubWriter);
  48. pubPemWriter.WriteObject(cert);
  49. pubPemWriter.Writer.Flush();
  50. }
  51. /// <summary>
  52. /// 生成CA签名证书
  53. /// </summary>
  54. /// <param name="domains"></param>
  55. /// <param name="keySizeBits"></param>
  56. /// <param name="validFrom"></param>
  57. /// <param name="validTo"></param>
  58. /// <param name="caPublicCerPath"></param>
  59. /// <param name="caPrivateKeyPath"></param>
  60. /// <returns></returns>
  61. public static X509Certificate2 GenerateByCa(IEnumerable<string> domains, int keySizeBits, DateTime validFrom, DateTime validTo, string caPublicCerPath, string caPrivateKeyPath, string? password = default)
  62. {
  63. if (File.Exists(caPublicCerPath) == false)
  64. {
  65. throw new FileNotFoundException(caPublicCerPath);
  66. }
  67. if (File.Exists(caPrivateKeyPath) == false)
  68. {
  69. throw new FileNotFoundException(caPublicCerPath);
  70. }
  71. using var pubReader = new StreamReader(caPublicCerPath, Encoding.ASCII);
  72. var caCert = (X509Certificate)new PemReader(pubReader).ReadObject();
  73. using var priReader = new StreamReader(caPrivateKeyPath, Encoding.ASCII);
  74. var reader = new PemReader(priReader);
  75. var caPrivateKey = ((AsymmetricCipherKeyPair)reader.ReadObject()).Private;
  76. var caSubjectName = GetSubjectName(caCert);
  77. var keys = GenerateRsaKeyPair(keySizeBits);
  78. var cert = GenerateCertificate(domains, keys.Public, validFrom, validTo, caSubjectName, caCert.GetPublicKey(), caPrivateKey, null);
  79. return GeneratePfx(cert, keys.Private, password);
  80. }
  81. /// <summary>
  82. /// 生成私钥
  83. /// </summary>
  84. /// <param name="length"></param>
  85. /// <returns></returns>
  86. private static AsymmetricCipherKeyPair GenerateRsaKeyPair(int length)
  87. {
  88. var keygenParam = new KeyGenerationParameters(secureRandom, length);
  89. var keyGenerator = new RsaKeyPairGenerator();
  90. keyGenerator.Init(keygenParam);
  91. return keyGenerator.GenerateKeyPair();
  92. }
  93. /// <summary>
  94. /// 生成证书
  95. /// </summary>
  96. /// <param name="domains"></param>
  97. /// <param name="subjectPublic"></param>
  98. /// <param name="validFrom"></param>
  99. /// <param name="validTo"></param>
  100. /// <param name="issuerName"></param>
  101. /// <param name="issuerPublic"></param>
  102. /// <param name="issuerPrivate"></param>
  103. /// <param name="caPathLengthConstraint"></param>
  104. /// <returns></returns>
  105. private static X509Certificate GenerateCertificate(IEnumerable<string> domains, AsymmetricKeyParameter subjectPublic, DateTime validFrom, DateTime validTo, string issuerName, AsymmetricKeyParameter? issuerPublic, AsymmetricKeyParameter issuerPrivate, int? caPathLengthConstraint)
  106. {
  107. var signatureFactory = issuerPrivate is ECPrivateKeyParameters
  108. ? new Asn1SignatureFactory(X9ObjectIdentifiers.ECDsaWithSha256.ToString(), issuerPrivate)
  109. : new Asn1SignatureFactory(PkcsObjectIdentifiers.Sha256WithRsaEncryption.ToString(), issuerPrivate);
  110. var certGenerator = new X509V3CertificateGenerator();
  111. certGenerator.SetIssuerDN(new X509Name("CN=" + issuerName));
  112. certGenerator.SetSubjectDN(new X509Name("CN=" + domains.First()));
  113. certGenerator.SetSerialNumber(BigInteger.ProbablePrime(120, new Random()));
  114. certGenerator.SetNotBefore(validFrom);
  115. certGenerator.SetNotAfter(validTo);
  116. certGenerator.SetPublicKey(subjectPublic);
  117. if (issuerPublic != null)
  118. {
  119. var akis = new AuthorityKeyIdentifierStructure(issuerPublic);
  120. certGenerator.AddExtension(X509Extensions.AuthorityKeyIdentifier, false, akis);
  121. }
  122. if (caPathLengthConstraint != null && caPathLengthConstraint >= 0)
  123. {
  124. var basicConstraints = new BasicConstraints(caPathLengthConstraint.Value);
  125. certGenerator.AddExtension(X509Extensions.BasicConstraints, true, basicConstraints);
  126. certGenerator.AddExtension(X509Extensions.KeyUsage, false, new KeyUsage(KeyUsage.DigitalSignature | KeyUsage.CrlSign | KeyUsage.KeyCertSign));
  127. }
  128. else
  129. {
  130. var basicConstraints = new BasicConstraints(cA: false);
  131. certGenerator.AddExtension(X509Extensions.BasicConstraints, true, basicConstraints);
  132. certGenerator.AddExtension(X509Extensions.KeyUsage, false, new KeyUsage(KeyUsage.DigitalSignature | KeyUsage.KeyEncipherment));
  133. }
  134. certGenerator.AddExtension(X509Extensions.ExtendedKeyUsage, true, new ExtendedKeyUsage(KeyPurposeID.IdKPServerAuth));
  135. var names = domains.Select(domain =>
  136. {
  137. var nameType = GeneralName.DnsName;
  138. if (IPAddress.TryParse(domain, out _))
  139. {
  140. nameType = GeneralName.IPAddress;
  141. }
  142. return new GeneralName(nameType, domain);
  143. }).ToArray();
  144. var subjectAltName = new GeneralNames(names);
  145. certGenerator.AddExtension(X509Extensions.SubjectAlternativeName, false, subjectAltName);
  146. return certGenerator.Generate(signatureFactory);
  147. }
  148. /// <summary>
  149. /// 生成pfx
  150. /// </summary>
  151. /// <param name="cert"></param>
  152. /// <param name="privateKey"></param>
  153. /// <param name="password"></param>
  154. /// <returns></returns>
  155. private static X509Certificate2 GeneratePfx(X509Certificate cert, AsymmetricKeyParameter privateKey, string? password)
  156. {
  157. var subject = GetSubjectName(cert);
  158. var pkcs12Store = new Pkcs12Store();
  159. var certEntry = new X509CertificateEntry(cert);
  160. pkcs12Store.SetCertificateEntry(subject, certEntry);
  161. pkcs12Store.SetKeyEntry(subject, new AsymmetricKeyEntry(privateKey), new[] { certEntry });
  162. using var pfxStream = new MemoryStream();
  163. pkcs12Store.Save(pfxStream, password?.ToCharArray(), secureRandom);
  164. return new X509Certificate2(pfxStream.ToArray());
  165. }
  166. /// <summary>
  167. /// 获取Subject
  168. /// </summary>
  169. /// <param name="cert"></param>
  170. /// <returns></returns>
  171. private static string GetSubjectName(X509Certificate cert)
  172. {
  173. var subject = cert.SubjectDN.ToString();
  174. if (subject.StartsWith("CN=", StringComparison.OrdinalIgnoreCase))
  175. {
  176. subject = subject[3..];
  177. }
  178. return subject;
  179. }
  180. }
  181. }