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

XFESystem

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

公开
关注 0 Fork 0 Star 0
UTF-8
using Org.BouncyCastle.Asn1;
using Org.BouncyCastle.Asn1.Pkcs;
using Org.BouncyCastle.Asn1.X509;
using Org.BouncyCastle.Crypto.Parameters;
using Org.BouncyCastle.Pkix;
using Org.BouncyCastle.X509;

namespace XFESystem.Tls;

/// <summary>Additional server-auth policy for the actual PKIX-selected path.</summary>
internal static class TlsCertificatePolicy
{
    internal static void CheckTrustStorePublicKey(X509Certificate certificate)
    {
        var info = certificate.SubjectPublicKeyInfo;
        if (!info.Algorithm.Algorithm.Equals(PkcsObjectIdentifiers.RsaEncryption))
        {
            CheckPublicKey(certificate);
            return;
        }
        // These are explicitly trusted, pinned input roots, not peer keys.
        // Import checks their ASN.1/strength without instantiating BC RSA keys:
        // that constructor runs a randomized modulus compositeness test for
        // every anchor. Full BC validation remains mandatory for the selected
        // anchor and all certificates in the actual PKIX path below it.
        if (info.Algorithm.Parameters is not null && info.Algorithm.Parameters.ToAsn1Object() is not DerNull ||
            info.PublicKey.PadBits != 0)
            throw new InvalidOperationException("The trusted RSA key has invalid algorithm parameters or padding.");
        var key = Asn1Sequence.GetInstance(info.ParsePublicKey());
        if (key.Count != 2) throw new InvalidOperationException("The trusted RSA key must encode exactly a modulus and exponent.");
        // Use signed ASN.1 values: RsaPublicKeyStructure normalizes through
        // PositiveValue, which would hide an invalid negative INTEGER encoding.
        var modulus = DerInteger.GetInstance(key[0]).Value;
        var exponent = DerInteger.GetInstance(key[1]).Value;
        if (modulus.SignValue <= 0 || modulus.BitLength is < 2048 or > 16384 || !modulus.TestBit(0) ||
            exponent.SignValue <= 0 || exponent.BitLength < 2 || !exponent.TestBit(0) || exponent.CompareTo(modulus) >= 0)
            throw new InvalidOperationException("The trusted RSA key has unsupported strength, modulus or exponent.");
    }

    internal static void CheckPublicKey(X509Certificate certificate)
    {
        bool permitted = certificate.GetPublicKey() switch
        {
            RsaKeyParameters rsa => !rsa.IsPrivate && rsa.Modulus.BitLength >= 2048,
            ECPublicKeyParameters ec => ec.Parameters.Curve.FieldSize >= 256,
            Ed25519PublicKeyParameters or Ed448PublicKeyParameters => true,
            _ => false
        };
        if (!permitted) throw new InvalidOperationException("The certificate uses an unsupported or weak authentication key.");
    }

    internal static void CheckServerUsage(X509Certificate certificate)
    {
        var usages = certificate.GetExtendedKeyUsage();
        if (usages is not null && !usages.Contains(KeyPurposeID.id_kp_serverAuth) && !usages.Contains(KeyPurposeID.AnyExtendedKeyUsage))
            throw new InvalidOperationException("A certificate in the selected path restricts use away from TLS server authentication.");
    }

    internal static void CheckSignatureAlgorithm(X509Certificate certificate)
    {
        // A trust anchor's self-signature is not used to authenticate the path;
        // callers apply this policy only to the path below the explicit anchor.
        switch (certificate.SigAlgOid)
        {
            case "1.2.840.113549.1.1.11": // SHA-256/384/512 with RSA
            case "1.2.840.113549.1.1.12":
            case "1.2.840.113549.1.1.13":
            case "1.2.840.10045.4.3.2": // ECDSA SHA-256/384/512
            case "1.2.840.10045.4.3.3":
            case "1.2.840.10045.4.3.4":
            case "1.3.101.112": // Ed25519 / Ed448
            case "1.3.101.113":
                return;
            case "1.2.840.113549.1.1.10": // RSASSA-PSS, with explicit modern parameters
                var pss = RsassaPssParameters.GetInstance(Asn1Object.FromByteArray(certificate.GetSigAlgParams()));
                int digestBytes = pss.HashAlgorithm.Algorithm.Id switch
                {
                    "2.16.840.1.101.3.4.2.1" => 32,
                    "2.16.840.1.101.3.4.2.2" => 48,
                    "2.16.840.1.101.3.4.2.3" => 64,
                    _ => 0
                };
                if (digestBytes != 0 && pss.MaskGenAlgorithm.Algorithm.Equals(PkcsObjectIdentifiers.IdMgf1) &&
                    AlgorithmIdentifier.GetInstance(pss.MaskGenAlgorithm.Parameters).Algorithm.Equals(pss.HashAlgorithm.Algorithm) &&
                    pss.SaltLength.Value.CompareTo(Org.BouncyCastle.Math.BigInteger.ValueOf(digestBytes)) >= 0 &&
                    pss.TrailerField.Value.Equals(Org.BouncyCastle.Math.BigInteger.One)) return;
                break;
        }
        throw new InvalidOperationException("The certificate signature algorithm does not meet the TLS authentication policy.");
    }

    internal static void CheckAnchorConstraints(X509Certificate anchor, IEnumerable<X509Certificate> path)
    {
        var extension = anchor.GetExtensionValue(X509Extensions.NameConstraints);
        if (extension is null) return;
        // BC's path validator handles intermediate constraints, but its fixed
        // version does not initialize them from TrustAnchor.GetNameConstraints.
        // Apply the explicit anchor's constraints to every subordinate name.
        var constraints = NameConstraints.GetInstance(Asn1Object.FromByteArray(extension.GetOctets()));
        var validator = new PkixNameConstraintValidator();
        if (constraints.PermittedSubtreesValue is { } permitted)
        {
            CheckDistances((Asn1Sequence)permitted.ToAsn1Object());
            validator.IntersectPermittedSubtree((Asn1Sequence)permitted.ToAsn1Object());
        }
        if (constraints.ExcludedSubtreesValue is { } excluded)
        {
            var sequence = (Asn1Sequence)excluded.ToAsn1Object();
            CheckDistances(sequence);
            foreach (var item in sequence) validator.AddExcludedSubtree(GeneralSubtree.GetInstance(item));
        }
        foreach (var certificate in path)
        {
            validator.CheckDN(certificate.SubjectDN);
            var names = certificate.GetExtensionValue(X509Extensions.SubjectAlternativeName);
            if (names is not null)
                foreach (var name in GeneralNames.GetInstance(Asn1Object.FromByteArray(names.GetOctets())).GetNames()) validator.CheckName(name);
        }
    }

    private static void CheckDistances(Asn1Sequence subtrees)
    {
        foreach (var item in subtrees)
        {
            var subtree = GeneralSubtree.GetInstance(item);
            if (subtree.MinimumObject.Value.SignValue != 0 || subtree.MaximumObject is not null)
                throw new InvalidOperationException("Non-default trust-anchor name-constraint distances are unsupported.");
        }
    }
}