~~NOTOC~~
====== Scenarij 5.5: Ograničenja imena ======
**Kategorija:** [[.:start|Validacija i povjerenje]] \\
**Složenost:** ⭐⭐⭐⭐ (Visoka) \\
**Preduvjeti:** CA certifikat s Name Constraints ekstenzijom \\
**Procijenjeno vrijeme:** 15-20 minuta
----
===== Opis =====
Ovaj scenarij opisuje **validaciju Name Constraints** (RFC 5280 §4.2.1.10). Name Constraints ograničavaju koja imena CA smije koristiti u certifikatima:
* **permittedSubtrees** - Dopušteni prostori imena (Whitelist)
* **excludedSubtrees** - Zabranjeni prostori imena (Blacklist)
**Slučajevi korištenja:**
* Ograničavanje Intermediate-CA na određene domene
* Izolacija internih CA od javnih domena
* Odvajanje zakupaca u višezakupničkim okruženjima
----
===== Tijek rada =====
flowchart TD
CERT[End-Entity certifikat] --> EXTRACT[Ekstrakcija Subject + SAN]
EXTRACT --> CHECK_CHAIN[Za svaki CA u lancu]
CHECK_CHAIN --> HAS_NC{Name Constraints?}
HAS_NC -->|Ne| NEXT[Sljedeći CA]
HAS_NC -->|Da| PERMITTED{U permitted?}
PERMITTED -->|Ne| FAIL[Constraint prekršen]
PERMITTED -->|Da| EXCLUDED{U excluded?}
EXCLUDED -->|Da| FAIL
EXCLUDED -->|Ne| NEXT
NEXT --> CHECK_CHAIN
NEXT --> OK[Sva ograničenja ispunjena]
style OK fill:#e8f5e9
style FAIL fill:#ffebee
----
===== Primjer koda: Postavljanje Name Constraints =====
using WvdS.Security.Cryptography.X509Certificates.Extensions.PQ;
using var ctx = PqCryptoContext.Initialize();
// Učitavanje Root-CA
var rootCert = ctx.LoadCertificate("root-ca.crt.pem");
var rootKey = ctx.LoadPrivateKey("root-ca.key.pem", "RootPassword!");
// Generiranje ključa za Intermediate-CA
using var intKey = ctx.GenerateKeyPair(PqAlgorithm.MlDsa65);
var dn = new DnBuilder()
.AddCN("Interni CA - samo example.com")
.AddO("Example GmbH")
.AddC("DE")
.Build();
var csr = ctx.CreateCertificateRequest(intKey, dn);
// Kreiranje Intermediate-CA s Name Constraints
var intermediateCert = ctx.IssueCertificate(
csr,
issuerCert: rootCert,
issuerKey: rootKey,
validDays: 1825, // 5 godina
extensions: new ExtBuilder()
.BasicConstraints(ca: true, pathLengthConstraint: 0, critical: true)
.KeyUsage(KeyUsageFlags.KeyCertSign | KeyUsageFlags.CrlSign, critical: true)
// Name Constraints Extension
.NameConstraints(
permitted: new NameConstraint[]
{
// Samo *.example.com dopušteno
new DnsNameConstraint("example.com"),
// E-mail samo @example.com
new EmailNameConstraint("example.com"),
// IP raspon 192.168.0.0/16
new IpNameConstraint("192.168.0.0", "255.255.0.0")
},
excluded: new NameConstraint[]
{
// Bez *.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("Intermediate-CA s Name Constraints kreiran:");
Console.WriteLine(" Dopušteno: *.example.com, *@example.com, 192.168.0.0/16");
Console.WriteLine(" Zabranjeno: *.dev.example.com");
----
===== Primjer koda: Validacija Name Constraints =====
public class NameConstraintValidator
{
public ValidationResult ValidateNameConstraints(
X509Certificate2 endEntity,
X509Certificate2Collection chain)
{
var result = new ValidationResult { IsValid = true };
// Ekstrakcija svih imena iz End-Entity
var names = ExtractAllNames(endEntity);
// Provjera svakog CA u lancu
foreach (var ca in chain)
{
var constraints = ExtractNameConstraints(ca);
if (constraints == null) continue;
foreach (var name in names)
{
// Provjera 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}' nije u permitted subtrees od {ca.Subject}");
}
}
// Provjera Excluded Subtrees
bool isExcluded = constraints.Excluded.Any(e => MatchesConstraint(name, e));
if (isExcluded)
{
result.IsValid = false;
result.Errors.Add($"Ime '{name.Value}' je u 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)
{
// Constraint ".example.com" odgovara "www.example.com" i "example.com"
if (constraint.StartsWith("."))
{
return dnsName.EndsWith(constraint, StringComparison.OrdinalIgnoreCase) ||
dnsName.Equals(constraint.TrimStart('.'), StringComparison.OrdinalIgnoreCase);
}
// Constraint "example.com" odgovara i svim poddomenama
return dnsName.Equals(constraint, StringComparison.OrdinalIgnoreCase) ||
dnsName.EndsWith("." + constraint, StringComparison.OrdinalIgnoreCase);
}
private bool MatchesEmailConstraint(string email, string constraint)
{
// Constraint "@example.com" odgovara svim e-mailovima te domene
if (constraint.StartsWith("@"))
{
return email.EndsWith(constraint, StringComparison.OrdinalIgnoreCase);
}
// Constraint "example.com" odgovara i svim poddomenama
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;
}
}
----
===== Tipovi Name Constraint =====
^ Tip ^ ASN.1 Tag ^ Primjer ^ Opis ^
| **dNSName** | 2 | example.com | DNS domena |
| **rfc822Name** | 1 | @example.com | E-mail domena |
| **iPAddress** | 7 | 192.168.0.0/255.255.0.0 | IP raspon |
| **directoryName** | 4 | O=Example | DN prefiks |
| **uniformResourceIdentifier** | 6 | https://example.com | URI prefiks |
----
===== Tipični scenariji ograničenja =====
^ Scenarij ^ Permitted ^ Excluded ^
| **Interni CA** | .internal.local | (prazno) |
| **Odjeljenje CA** | .dept.example.com | .admin.dept.example.com |
| **Partner CA** | .partner.com | .example.com |
| **IoT CA** | 10.0.0.0/8 | (prazno) |
----
===== X509Chain provjera Name Constraint =====
// .NET Framework automatski provodi provjeru Name Constraint
var chain = new X509Chain();
chain.ChainPolicy.ExtraStore.Add(intermediateCert);
bool isValid = chain.Build(endEntityCert);
// Prepoznavanje grešaka Name Constraint
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("Prekršenje Name Constraint:");
foreach (var error in ncErrors)
{
Console.WriteLine($" {error.StatusInformation}");
}
}
----
===== Povezani scenariji =====
^ Odnos ^ Scenarij ^ Opis ^
| **Preduvjet** | [[.:policy_validation|5.4 Validacija politika]] | Provjera politika |
| **Povezano** | [[hr:int:pqcrypt:szenarien:pki:intermediate_ca_erstellen|1.2 Intermediate-CA]] | CA s ograničenjima |
| **Povezano** | [[hr:int:pqcrypt:szenarien:pki:ca_hierarchie_aufbauen|1.3 CA hijerarhija]] | Dizajn ograničenja |
----
<< [[.:policy_validation|← 5.4 Validacija politika]] | [[.:start|↑ Pregled validacije]] | [[hr:int:pqcrypt:szenarien:start|→ Svi scenariji]] >>
{{tag>scenarij validacija name-constraints x509 rfc5280}}
----
//Wolfgang van der Stille @ EMSR DATA d.o.o. - Post-Quantum Cryptography Professional//