Scenarij 5.5: Omejitve imen

Kategorija: Validacija in zaupanje
Kompleksnost: ⭐⭐⭐⭐ (Visoka)
Predpogoji: CA certifikat z razširitvijo Name Constraints
Predviden čas: 15-20 minut


Opis

Ta scenarij opisuje validacijo omejitev imen (RFC 5280 §4.2.1.10). Omejitve imen določajo, katera imena lahko CA uporablja v certifikatih:

  • permittedSubtrees - Dovoljeni imenski prostori (bela lista)
  • excludedSubtrees - Prepovedani imenski prostori (črna lista)

Primeri uporabe:

  • Omejitev vmesnih CA-jev na določene domene
  • Izolacija notranjih CA-jev od javnih domen
  • Ločitev najemnikov v večnajemniških okoljih

Potek dela

flowchart TD CERT[Končni certifikat] --> EXTRACT[Ekstrakcija Subject + SANs] EXTRACT --> CHECK_CHAIN[Za vsak CA v verigi] CHECK_CHAIN --> HAS_NC{Omejitve imen?} HAS_NC -->|Ne| NEXT[Naslednji CA] HAS_NC -->|Da| PERMITTED{V permitted?} PERMITTED -->|Ne| FAIL[Omejitev kršena] PERMITTED -->|Da| EXCLUDED{V excluded?} EXCLUDED -->|Da| FAIL EXCLUDED -->|Ne| NEXT NEXT --> CHECK_CHAIN NEXT --> OK[Vse omejitve izpolnjene] style OK fill:#e8f5e9 style FAIL fill:#ffebee


Primer kode: Nastavitev omejitev imen

using WvdS.Security.Cryptography.X509Certificates.Extensions.PQ;
 
using var ctx = PqCryptoContext.Initialize();
 
// Nalaganje korenskega CA
var rootCert = ctx.LoadCertificate("root-ca.crt.pem");
var rootKey = ctx.LoadPrivateKey("root-ca.key.pem", "RootPassword!");
 
// Generiranje ključa vmesnega CA
using var intKey = ctx.GenerateKeyPair(PqAlgorithm.MlDsa65);
 
var dn = new DnBuilder()
    .AddCN("Internal CA - example.com Only")
    .AddO("Example GmbH")
    .AddC("DE")
    .Build();
 
var csr = ctx.CreateCertificateRequest(intKey, dn);
 
// Ustvarjanje vmesnega CA z omejitvami imen
var intermediateCert = ctx.IssueCertificate(
    csr,
    issuerCert: rootCert,
    issuerKey: rootKey,
    validDays: 1825,  // 5 let
    extensions: new ExtBuilder()
        .BasicConstraints(ca: true, pathLengthConstraint: 0, critical: true)
        .KeyUsage(KeyUsageFlags.KeyCertSign | KeyUsageFlags.CrlSign, critical: true)
        // Razširitev Name Constraints
        .NameConstraints(
            permitted: new NameConstraint[]
            {
                // Dovoljeno samo *.example.com
                new DnsNameConstraint("example.com"),
                // E-pošta samo @example.com
                new EmailNameConstraint("example.com"),
                // IP obseg 192.168.0.0/16
                new IpNameConstraint("192.168.0.0", "255.255.0.0")
            },
            excluded: new NameConstraint[]
            {
                // Brez *.dev.example.com (razvojne poddomene)
                new DnsNameConstraint("dev.example.com")
            },
            critical: true  // MORA biti critical
        )
        .SubjectKeyIdentifier(intKey.PublicKey)
        .AuthorityKeyIdentifier(rootCert)
        .Build()
);
 
intermediateCert.ToPemFile("internal-intermediate-ca.crt.pem");
intKey.ToEncryptedPemFile("internal-intermediate-ca.key.pem", "IntPassword!");
 
Console.WriteLine("Vmesni CA z omejitvami imen ustvarjen:");
Console.WriteLine("  Dovoljeno: *.example.com, *@example.com, 192.168.0.0/16");
Console.WriteLine("  Prepovedano: *.dev.example.com");

Primer kode: Validacija omejitev imen

public class NameConstraintValidator
{
    public ValidationResult ValidateNameConstraints(
        X509Certificate2 endEntity,
        X509Certificate2Collection chain)
    {
        var result = new ValidationResult { IsValid = true };
 
        // Ekstrakcija vseh imen iz končnega certifikata
        var names = ExtractAllNames(endEntity);
 
        // Preverjanje vsakega CA v verigi
        foreach (var ca in chain)
        {
            var constraints = ExtractNameConstraints(ca);
            if (constraints == null) continue;
 
            foreach (var name in names)
            {
                // Preverjanje Permitted Subtrees
                if (constraints.Permitted.Any())
                {
                    bool isPermitted = constraints.Permitted.Any(p => MatchesConstraint(name, p));
                    if (!isPermitted)
                    {
                        result.IsValid = false;
                        result.Errors.Add($"Ime '{name.Value}' ni v permitted subtrees od {ca.Subject}");
                    }
                }
 
                // Preverjanje Excluded Subtrees
                bool isExcluded = constraints.Excluded.Any(e => MatchesConstraint(name, e));
                if (isExcluded)
                {
                    result.IsValid = false;
                    result.Errors.Add($"Ime '{name.Value}' je v excluded subtrees od {ca.Subject}");
                }
            }
        }
 
        return result;
    }
 
    private List<GeneralName> ExtractAllNames(X509Certificate2 cert)
    {
        var names = new List<GeneralName>();
 
        // Subject CN
        var cn = cert.GetNameInfo(X509NameType.SimpleName, false);
        if (!string.IsNullOrEmpty(cn))
        {
            names.Add(new GeneralName { Type = GeneralNameType.DnsName, Value = cn });
        }
 
        // Subject E-Mail
        var email = cert.GetNameInfo(X509NameType.EmailName, false);
        if (!string.IsNullOrEmpty(email))
        {
            names.Add(new GeneralName { Type = GeneralNameType.Email, Value = email });
        }
 
        // Subject Alternative Names
        var sanExt = cert.Extensions["2.5.29.17"];
        if (sanExt != null)
        {
            names.AddRange(ParseSan(sanExt.RawData));
        }
 
        return names;
    }
 
    private bool MatchesConstraint(GeneralName name, NameConstraint constraint)
    {
        if (name.Type != constraint.Type) return false;
 
        return constraint.Type switch
        {
            GeneralNameType.DnsName => MatchesDnsConstraint(name.Value, constraint.Value),
            GeneralNameType.Email => MatchesEmailConstraint(name.Value, constraint.Value),
            GeneralNameType.IpAddress => MatchesIpConstraint(name.Value, constraint.Value, constraint.Mask),
            GeneralNameType.DirectoryName => MatchesDnConstraint(name.Value, constraint.Value),
            _ => false
        };
    }
 
    private bool MatchesDnsConstraint(string dnsName, string constraint)
    {
        // Omejitev ".example.com" se ujema z "www.example.com" in "example.com"
        if (constraint.StartsWith("."))
        {
            return dnsName.EndsWith(constraint, StringComparison.OrdinalIgnoreCase) ||
                   dnsName.Equals(constraint.TrimStart('.'), StringComparison.OrdinalIgnoreCase);
        }
        // Omejitev "example.com" se ujema tudi z vsemi poddomenami
        return dnsName.Equals(constraint, StringComparison.OrdinalIgnoreCase) ||
               dnsName.EndsWith("." + constraint, StringComparison.OrdinalIgnoreCase);
    }
 
    private bool MatchesEmailConstraint(string email, string constraint)
    {
        // Omejitev "@example.com" se ujema z vsemi e-poštami te domene
        if (constraint.StartsWith("@"))
        {
            return email.EndsWith(constraint, StringComparison.OrdinalIgnoreCase);
        }
        // Omejitev "example.com" se ujema tudi z vsemi poddomenami
        var domain = email.Split('@').LastOrDefault();
        return MatchesDnsConstraint(domain ?? "", constraint);
    }
 
    private bool MatchesIpConstraint(string ip, string network, string mask)
    {
        var ipBytes = IPAddress.Parse(ip).GetAddressBytes();
        var networkBytes = IPAddress.Parse(network).GetAddressBytes();
        var maskBytes = IPAddress.Parse(mask).GetAddressBytes();
 
        for (int i = 0; i < ipBytes.Length; i++)
        {
            if ((ipBytes[i] & maskBytes[i]) != (networkBytes[i] & maskBytes[i]))
            {
                return false;
            }
        }
        return true;
    }
}

Tipi omejitev imen

Tip ASN.1 oznaka Primer Opis
dNSName 2 example.com DNS domena
rfc822Name 1 @example.com E-poštna domena
iPAddress 7 192.168.0.0/255.255.0.0 IP obseg
directoryName 4 O=Example DN predpona
uniformResourceIdentifier 6 https://example.com URI predpona

Tipični scenariji omejitev

Scenarij Permitted Excluded
Notranji CA .internal.local (prazno)
Oddelek CA .dept.example.com .admin.dept.example.com
Partner CA .partner.com .example.com
IoT CA 10.0.0.0/8 (prazno)

Preverjanje omejitev imen z X509Chain

// .NET Framework izvaja preverjanje omejitev imen samodejno
var chain = new X509Chain();
chain.ChainPolicy.ExtraStore.Add(intermediateCert);
 
bool isValid = chain.Build(endEntityCert);
 
// Prepoznavanje napak omejitev imen
var ncErrors = chain.ChainElements
    .SelectMany(e => e.ChainElementStatus)
    .Where(s => s.Status == X509ChainStatusFlags.InvalidNameConstraints ||
                s.Status == X509ChainStatusFlags.HasNotSupportedNameConstraint ||
                s.Status == X509ChainStatusFlags.HasNotPermittedNameConstraint ||
                s.Status == X509ChainStatusFlags.HasExcludedNameConstraint)
    .ToList();
 
if (ncErrors.Any())
{
    Console.WriteLine("Kršitev omejitve imen:");
    foreach (var error in ncErrors)
    {
        Console.WriteLine($"  {error.StatusInformation}");
    }
}

Povezani scenariji

Povezava Scenarij Opis
Predpogoj 5.4 Validacija politik Preverjanje politik
Povezano 1.2 Vmesni CA CA z omejitvami
Povezano 1.3 CA hierarhija Načrtovanje omejitev

« ← 5.4 Validacija politik | ↑ Pregled validacije | → Vsi scenariji »


Wolfgang van der Stille @ EMSR DATA d.o.o. - Post-Quantum Cryptography Professional

Zuletzt geändert: dne 30.01.2026 ob 06:51