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:
@@ -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;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user