using System.Security.Cryptography; using System.Security.Cryptography.X509Certificates; using BcCertificate = Org.BouncyCastle.X509.X509Certificate; namespace XFESystem.NativeTls.Tests; internal sealed class CertificateFixtures : IDisposable { public DateTime Now { get; } = DateTime.UtcNow; public ECDsa RootKey { get; } = ECDsa.Create(ECCurve.NamedCurves.nistP256); public X509Certificate2 Root { get; } public BcCertificate RootBc => new Org.BouncyCastle.X509.X509CertificateParser().ReadCertificate(Root.RawData); public CertificateFixtures(string? permittedDns = null, string? excludedDns = null, bool serverAuthRoot = true, bool certificateSigning = true) { var request = new CertificateRequest("CN=XFE controlled TLS test root", RootKey, HashAlgorithmName.SHA256); request.CertificateExtensions.Add(new X509BasicConstraintsExtension(true, false, 0, true)); request.CertificateExtensions.Add(new X509KeyUsageExtension(certificateSigning ? X509KeyUsageFlags.KeyCertSign | X509KeyUsageFlags.CrlSign : X509KeyUsageFlags.DigitalSignature, true)); if (!serverAuthRoot) request.CertificateExtensions.Add(new X509EnhancedKeyUsageExtension(new OidCollection { new("1.3.6.1.5.5.7.3.2") }, false)); if (permittedDns is not null || excludedDns is not null) { Org.BouncyCastle.Asn1.X509.GeneralSubtree[]? permitted = permittedDns is null ? null : [new(new Org.BouncyCastle.Asn1.X509.GeneralName(Org.BouncyCastle.Asn1.X509.GeneralName.DnsName, permittedDns))]; Org.BouncyCastle.Asn1.X509.GeneralSubtree[]? excluded = excludedDns is null ? null : [new(new Org.BouncyCastle.Asn1.X509.GeneralName(Org.BouncyCastle.Asn1.X509.GeneralName.DnsName, excludedDns))]; var constraints = new Org.BouncyCastle.Asn1.X509.NameConstraints( permitted is null ? null! : new Org.BouncyCastle.Asn1.X509.GeneralSubtrees(permitted), excluded is null ? null! : new Org.BouncyCastle.Asn1.X509.GeneralSubtrees(excluded)); request.CertificateExtensions.Add(new X509Extension("2.5.29.30", constraints.GetDerEncoded(), true)); } Root = request.CreateSelfSigned(Now.AddDays(-10), Now.AddDays(10)); } public X509Certificate2 Leaf(string? dns = "test.xfe.invalid", string? ip = null, bool ca = false, bool serverAuth = true, DateTime? before = null, DateTime? after = null, ECDsa? signingKey = null, bool unknownCriticalExtension = false, bool signingUsage = true) { using var key = ECDsa.Create(ECCurve.NamedCurves.nistP256); var request = new CertificateRequest("CN=never-used-for-name-match.invalid", key, HashAlgorithmName.SHA256); request.CertificateExtensions.Add(new X509BasicConstraintsExtension(ca, false, 0, true)); request.CertificateExtensions.Add(new X509KeyUsageExtension(signingUsage ? X509KeyUsageFlags.DigitalSignature : X509KeyUsageFlags.KeyEncipherment, true)); request.CertificateExtensions.Add(new X509EnhancedKeyUsageExtension(new OidCollection { new(serverAuth ? "1.3.6.1.5.5.7.3.1" : "1.3.6.1.5.5.7.3.2") }, false)); if (dns is not null || ip is not null) { var names = new SubjectAlternativeNameBuilder(); if (dns is not null) names.AddDnsName(dns); if (ip is not null) names.AddIpAddress(System.Net.IPAddress.Parse(ip)); request.CertificateExtensions.Add(names.Build()); } if (unknownCriticalExtension) request.CertificateExtensions.Add(new X509Extension("1.3.6.1.4.1.55555.99", [0x05, 0x00], true)); var signature = X509SignatureGenerator.CreateForECDsa(signingKey ?? RootKey); using var certificate = request.Create(Root.SubjectName, signature, before ?? Now.AddDays(-1), after ?? Now.AddDays(1), RandomNumberGenerator.GetBytes(16)); return certificate.CopyWithPrivateKey(key); } public void Dispose() { Root.Dispose(); RootKey.Dispose(); } }