XFE Git
XFE Studio Git
Git 首页 全局搜索
XFE 主站 文档 NuGet

XFESystem

【Rust】XFE系统,基于Rust和汇编

公开
关注 0 Fork 0 Star 0
UTF-8
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(); }
}