~~NOTOC~~
====== Scenarij 5.5: Omejitve imen ======
**Kategorija:** [[.:start|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 ExtractAllNames(X509Certificate2 cert)
{
var names = new List();
// 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** | [[.:policy_validation|5.4 Validacija politik]] | Preverjanje politik |
| **Povezano** | [[sl:int:pqcrypt:szenarien:pki:intermediate_ca_erstellen|1.2 Vmesni CA]] | CA z omejitvami |
| **Povezano** | [[sl:int:pqcrypt:szenarien:pki:ca_hierarchie_aufbauen|1.3 CA hierarhija]] | Načrtovanje omejitev |
----
<< [[.:policy_validation|← 5.4 Validacija politik]] | [[.:start|↑ Pregled validacije]] | [[sl:int:pqcrypt:szenarien:start|→ Vsi scenariji]] >>
{{tag>scenarij validacija omejitve-imen x509 rfc5280}}
----
//Wolfgang van der Stille @ EMSR DATA d.o.o. - Post-Quantum Cryptography Professional//