security(pki): enforce configuration-driven CA profiles

* add versioned root and intermediate CA profile documents
* extend the strict profile schema with closed certificate kinds
* package canonical built-in root and intermediate profiles
* enforce immutable import and explicit activation for CA profiles
* bind CA credentials to exact profile ID, version, and canonical hash
* resolve active profiles for root and intermediate issuance
* validate issuer-controlled CA requests before backend execution
* enforce complete CA DER and extension postconditions
* reject inactive, mismatched, and malicious profile/backend inputs
* preserve historical credential bindings across profile activation changes
* add root and intermediate profile version-switch coverage

BREAKING CHANGE: root and intermediate CA issuance now requires an explicitly imported and activated versioned CA profile.
This commit is contained in:
2026-07-30 20:14:11 +02:00
parent 9e40e8b5a2
commit 849c8c82cb
50 changed files with 2608 additions and 899 deletions

View File

@@ -54,6 +54,7 @@ import zeroecho.pki.api.RevocationService;
import zeroecho.pki.api.ProfileService;
import zeroecho.pki.api.StatusObjectService;
import zeroecho.pki.api.credential.EffectiveCredentialStatusResolver;
import zeroecho.pki.api.profile.BuiltInCertificateProfileCatalog;
import zeroecho.pki.impl.core.DefaultCaService;
import zeroecho.pki.impl.core.DefaultCertificationRequestService;
import zeroecho.pki.impl.core.DefaultIssuanceService;
@@ -104,6 +105,7 @@ public final class PkiTestRuntime implements AutoCloseable {
private final Map<String, PublicKey> publicKeysByKeyRef;
private Runnable publicKeyResolveHook;
private boolean caProfilesProvisioned;
private PkiTestRuntime(FilesystemPkiStore store, PkiSigningBus signingBus, SignatureWorkflow signatureWorkflow,
CredentialFramework framework, CredentialIssuerBackend issuerBackend,
@@ -129,7 +131,7 @@ public final class PkiTestRuntime implements AutoCloseable {
this.statusObjectService = new DefaultStatusObjectService(store, framework, auditSink, statusResolver);
this.caService = new DefaultCaService(store, framework, issuerBackend, this::resolvePublicKeyInfo, signingBus,
auditSink, statusResolver, "SHA256withRSA", signingTtl);
auditSink, statusResolver, profileService, clock, "SHA256withRSA", signingTtl);
}
/**
@@ -306,27 +308,53 @@ public final class PkiTestRuntime implements AutoCloseable {
}
public CaService caService() {
provisionCaProfiles();
return caService;
}
public CaService caService(CredentialFramework credentialFramework) {
provisionCaProfiles();
return new DefaultCaService(store, Objects.requireNonNull(credentialFramework, "credentialFramework"),
issuerBackend, this::resolvePublicKeyInfo, signingBus, auditSink, statusResolver, "SHA256withRSA",
Duration.ofSeconds(2));
issuerBackend, this::resolvePublicKeyInfo, signingBus, auditSink, statusResolver, profileService,
Clock.systemUTC(), "SHA256withRSA", Duration.ofSeconds(2));
}
public CaService caService(CredentialIssuerBackend backend) {
provisionCaProfiles();
return new DefaultCaService(store, framework, Objects.requireNonNull(backend, "backend"),
this::resolvePublicKeyInfo, signingBus, auditSink, statusResolver, "SHA256withRSA",
Duration.ofSeconds(2));
this::resolvePublicKeyInfo, signingBus, auditSink, statusResolver, profileService, Clock.systemUTC(),
"SHA256withRSA", Duration.ofSeconds(2));
}
public CaService caService(CredentialIssuerBackend backend, EffectiveCredentialStatusResolver resolver) {
provisionCaProfiles();
return new DefaultCaService(store, framework, Objects.requireNonNull(backend, "backend"),
this::resolvePublicKeyInfo, signingBus, auditSink, Objects.requireNonNull(resolver, "resolver"),
profileService, Clock.systemUTC(), "SHA256withRSA", Duration.ofSeconds(2));
}
public CaService caService(ProfileService profiles) {
provisionCaProfiles();
return new DefaultCaService(store, framework, issuerBackend, this::resolvePublicKeyInfo, signingBus,
auditSink, statusResolver, Objects.requireNonNull(profiles, "profiles"), Clock.systemUTC(),
"SHA256withRSA", Duration.ofSeconds(2));
}
private synchronized void provisionCaProfiles() {
if (caProfilesProvisioned) {
return;
}
BuiltInCertificateProfileCatalog.load(PkiTestRuntime.class.getClassLoader()).stream()
.filter(template -> template.definition().profileId().equals("root-ca")
|| template.definition().profileId().equals("intermediate-ca"))
.forEach(template -> {
zeroecho.pki.api.profile.CertificateProfileRef reference =
profileService.importBuiltIn(template);
profileService.activateProfile(reference.profileId(), reference.profileVersion());
});
caProfilesProvisioned = true;
}
public CertificationRequestService certificationRequestService() {
return certificationRequestService;
}