feat(pki-server): add ACME certificate management

Add directory-bound ACME accounts, orders, authorizations, challenge
evidence, strict JWS processing, issuance, rollover and revocation.

Isolate bounded ACME execution from administrative and public services
while preserving explicit authority, profile, issuer and chain-path
selection.
This commit is contained in:
2026-08-05 18:16:00 +02:00
parent c3bd3a33e9
commit b19edf17fd
58 changed files with 6295 additions and 46 deletions

View File

@@ -47,6 +47,7 @@ import zeroecho.pki.application.PkiOperationExecutor;
import zeroecho.pki.application.PkiOperationOutcome;
import zeroecho.pki.application.PkiOperationResult;
import zeroecho.pki.application.PkiOperationValue;
import zeroecho.pki.server.acme.AcmeService;
import zeroecho.pki.application.PkiResourceScopeResolver;
/**
@@ -59,7 +60,8 @@ import zeroecho.pki.application.PkiResourceScopeResolver;
*/
@SuppressWarnings({ "PMD.ControlStatementBraces", "PMD.FieldDeclarationsShouldBeAtStartOfClass",
"PMD.ExcessiveParameterList", "PMD.CyclomaticComplexity", "PMD.AvoidInstantiatingObjectsInLoops",
"PMD.NcssCount", "PMD.ConfusingTernary", "PMD.ExceptionAsFlowControl" })
"PMD.NcssCount", "PMD.ConfusingTernary", "PMD.ExceptionAsFlowControl",
"PMD.CouplingBetweenObjects" })
public final class ServerOperationGateway {
/**
* Complete transport-neutral request admission input.
@@ -186,6 +188,9 @@ public final class ServerOperationGateway {
this.openCheck = Objects.requireNonNull(openCheck, "openCheck");
}
/** Installs the optional configured ACME control capability once before listener readiness. */
public void installAcme(AcmeService service) { controlExecutor.installAcme(service); }
/** Creates the pre-control-plane gateway surface for embedded source compatibility. */
public ServerOperationGateway(RealmId realmId, AuthorityExposurePolicy exposure, ServerControlStore control,
RoleTemplateCatalog roles, AuthorizationEngine authorization, ApprovalService approvals,
@@ -526,6 +531,13 @@ public final class ServerOperationGateway {
case ServerControlOperation.ActivateBreakGlass value -> breakGlass.requireCurrent(value.breakGlassId()).grant().scope();
case ServerControlOperation.InspectBreakGlass value -> breakGlass.requireCurrent(value.breakGlassId()).grant().scope();
case ServerControlOperation.RevokeBreakGlass value -> breakGlass.requireCurrent(value.breakGlassId()).grant().scope();
case ServerControlOperation.RegisterAcmeDirectory value -> new Permission.Scope(realmId,
Optional.of(value.registration().authorityId()), Optional.empty(),
Optional.of(value.registration().profileId()));
case ServerControlOperation.InspectAcmeDirectory value -> controlExecutor.acmeScope(value);
case ServerControlOperation.SetAcmeDirectoryActive value -> controlExecutor.acmeScope(value);
case ServerControlOperation.InspectAcmeAccount value -> controlExecutor.acmeScope(value);
case ServerControlOperation.DeactivateAcmeAccount value -> controlExecutor.acmeScope(value);
default -> resource.scope();
};
if (!actual.equals(resource.scope())) throw new SecurityException("Control scope differs");