using System; using System.Collections.Generic; using System.Net; using System.Security.Cryptography; using System.Security.Cryptography.X509Certificates; namespace FastGithub.HttpServer.Certs { /// /// 证书生成器 /// static class CertGenerator { 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"); /// /// 生成ca证书 /// /// /// /// /// /// /// public static X509Certificate2 CreateCACertificate( X500DistinguishedName subjectName, DateTimeOffset notBefore, DateTimeOffset notAfter, int rsaKeySizeInBits = 2048, int pathLengthConstraint = 1) { using var rsa = RSA.Create(rsaKeySizeInBits); var request = new CertificateRequest(subjectName, rsa, HashAlgorithmName.SHA256, RSASignaturePadding.Pkcs1); var basicConstraints = new X509BasicConstraintsExtension(true, pathLengthConstraint > 0, pathLengthConstraint, true); request.CertificateExtensions.Add(basicConstraints); var keyUsage = new X509KeyUsageExtension(X509KeyUsageFlags.DigitalSignature | X509KeyUsageFlags.CrlSign | X509KeyUsageFlags.KeyCertSign, true); request.CertificateExtensions.Add(keyUsage); 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); } /// /// 生成服务器证书 /// /// /// /// /// /// /// /// public static X509Certificate2 CreateEndCertificate( X509Certificate2 issuerCertificate, X500DistinguishedName subjectName, IEnumerable? extraDnsNames = default, DateTimeOffset? notBefore = default, DateTimeOffset? notAfter = default, int rsaKeySizeInBits = 2048) { 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) { foreach (var dnsName in extraDnsNames) { dnsBuilder.Add(dnsName); } } var dnsNames = dnsBuilder.Build(); request.CertificateExtensions.Add(dnsNames); if (notBefore == null || notBefore.Value < issuerCertificate.NotBefore) { notBefore = issuerCertificate.NotBefore; } if (notAfter == null || notAfter.Value > issuerCertificate.NotAfter) { notAfter = issuerCertificate.NotAfter; } var serialNumber = BitConverter.GetBytes(Random.Shared.NextInt64()); using var certOnly = request.Create(issuerCertificate, notBefore.Value, notAfter.Value, serialNumber); return certOnly.CopyWithPrivateKey(rsa); } private static void Add(this SubjectAlternativeNameBuilder builder, string name) { if (IPAddress.TryParse(name, out var address)) { builder.AddIpAddress(address); } else { builder.AddDnsName(name); } } private static X509Extension GetAuthorityKeyIdentifierExtension(X509Certificate2 certificate) { var extension = new X509SubjectKeyIdentifierExtension(certificate.PublicKey, false); #if NET7_0_OR_GREATER return X509AuthorityKeyIdentifierExtension.CreateFromSubjectKeyIdentifier(extension); #else 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); return new X509Extension("2.5.29.35", rawData, false); #endif } } }