diff --git a/runtime/engine/src/main/java/io/aklivity/zilla/runtime/engine/EngineConfiguration.java b/runtime/engine/src/main/java/io/aklivity/zilla/runtime/engine/EngineConfiguration.java index b0299b85c7..9b7e3379a2 100644 --- a/runtime/engine/src/main/java/io/aklivity/zilla/runtime/engine/EngineConfiguration.java +++ b/runtime/engine/src/main/java/io/aklivity/zilla/runtime/engine/EngineConfiguration.java @@ -43,6 +43,7 @@ import com.sun.management.OperatingSystemMXBean; import io.aklivity.zilla.runtime.engine.internal.layouts.BudgetsLayout; +import io.aklivity.zilla.runtime.engine.security.RevocationStrategy; public class EngineConfiguration extends Configuration { @@ -91,6 +92,7 @@ public class EngineConfiguration extends Configuration public static final PropertyDef ENGINE_CACERTS_STORE; public static final PropertyDef ENGINE_CACERTS_STORE_PASS; public static final PropertyDef ENGINE_ERROR_REPORTER; + public static final PropertyDef ENGINE_CERTIFICATE_REVOCATION_STRATEGY; private static final ConfigurationDef ENGINE_CONFIG; @@ -143,6 +145,8 @@ public class EngineConfiguration extends Configuration ENGINE_CACERTS_STORE_PASS = config.property("cacerts.store.pass"); ENGINE_ERROR_REPORTER = config.property(ErrorReporter.class, "error.reporter", EngineConfiguration::decodeErrorReporter, EngineConfiguration::defaultErrorReporter); + ENGINE_CERTIFICATE_REVOCATION_STRATEGY = config.property(RevocationStrategy.class, "certificate.revocation.strategy", + EngineConfiguration::decodeRevocationStrategy, RevocationStrategy.NONE); ENGINE_CONFIG = config; } @@ -607,4 +611,10 @@ private static ErrorReporter decodeErrorReporter( return reporter; } + + private static RevocationStrategy decodeRevocationStrategy( + String value) + { + return RevocationStrategy.valueOf(value.toUpperCase()); + } } diff --git a/runtime/engine/src/main/java/io/aklivity/zilla/runtime/engine/security/RevocationStrategy.java b/runtime/engine/src/main/java/io/aklivity/zilla/runtime/engine/security/RevocationStrategy.java new file mode 100644 index 0000000000..7f939d65ab --- /dev/null +++ b/runtime/engine/src/main/java/io/aklivity/zilla/runtime/engine/security/RevocationStrategy.java @@ -0,0 +1,22 @@ +/* + * Copyright 2021-2024 Aklivity Inc. + * + * Aklivity licenses this file to you under the Apache License, + * version 2.0 (the "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + */ +package io.aklivity.zilla.runtime.engine.security; + +public enum RevocationStrategy +{ + CRL, + NONE +} diff --git a/runtime/vault-filesystem/src/main/java/io/aklivity/zilla/runtime/vault/filesystem/config/FileSystemOptionsConfig.java b/runtime/vault-filesystem/src/main/java/io/aklivity/zilla/runtime/vault/filesystem/config/FileSystemOptionsConfig.java index f51f1aac1f..2a2334c52f 100644 --- a/runtime/vault-filesystem/src/main/java/io/aklivity/zilla/runtime/vault/filesystem/config/FileSystemOptionsConfig.java +++ b/runtime/vault-filesystem/src/main/java/io/aklivity/zilla/runtime/vault/filesystem/config/FileSystemOptionsConfig.java @@ -20,12 +20,14 @@ import java.util.function.Function; import io.aklivity.zilla.runtime.engine.config.OptionsConfig; +import io.aklivity.zilla.runtime.engine.security.RevocationStrategy; public final class FileSystemOptionsConfig extends OptionsConfig { public final FileSystemStoreConfig keys; public final FileSystemStoreConfig trust; public final FileSystemStoreConfig signers; + public final RevocationStrategy revocation; public static FileSystemOptionsConfigBuilder builder() { @@ -41,12 +43,14 @@ public static FileSystemOptionsConfigBuilder builder( FileSystemOptionsConfig( FileSystemStoreConfig keys, FileSystemStoreConfig trust, - FileSystemStoreConfig signers) + FileSystemStoreConfig signers, + RevocationStrategy revocation) { super(List.of(), resolveResources(keys, trust)); this.keys = keys; this.trust = trust; this.signers = signers; + this.revocation = revocation; } private static List resolveResources( diff --git a/runtime/vault-filesystem/src/main/java/io/aklivity/zilla/runtime/vault/filesystem/config/FileSystemOptionsConfigBuilder.java b/runtime/vault-filesystem/src/main/java/io/aklivity/zilla/runtime/vault/filesystem/config/FileSystemOptionsConfigBuilder.java index 6b0dfcf2e6..8b73748276 100644 --- a/runtime/vault-filesystem/src/main/java/io/aklivity/zilla/runtime/vault/filesystem/config/FileSystemOptionsConfigBuilder.java +++ b/runtime/vault-filesystem/src/main/java/io/aklivity/zilla/runtime/vault/filesystem/config/FileSystemOptionsConfigBuilder.java @@ -19,6 +19,7 @@ import io.aklivity.zilla.runtime.engine.config.ConfigBuilder; import io.aklivity.zilla.runtime.engine.config.OptionsConfig; +import io.aklivity.zilla.runtime.engine.security.RevocationStrategy; public final class FileSystemOptionsConfigBuilder extends ConfigBuilder> { @@ -27,6 +28,7 @@ public final class FileSystemOptionsConfigBuilder extends ConfigBuilder mapper) @@ -77,9 +79,16 @@ public FileSystemOptionsConfigBuilder signers( return this; } + public FileSystemOptionsConfigBuilder revocation( + RevocationStrategy revocation) + { + this.revocation = revocation; + return this; + } + @Override public T build() { - return mapper.apply(new FileSystemOptionsConfig(keys, trust, signers)); + return mapper.apply(new FileSystemOptionsConfig(keys, trust, signers, revocation)); } } diff --git a/runtime/vault-filesystem/src/main/java/io/aklivity/zilla/runtime/vault/filesystem/internal/FileSystemContext.java b/runtime/vault-filesystem/src/main/java/io/aklivity/zilla/runtime/vault/filesystem/internal/FileSystemContext.java index 30e1c2ef68..7be9b0992e 100644 --- a/runtime/vault-filesystem/src/main/java/io/aklivity/zilla/runtime/vault/filesystem/internal/FileSystemContext.java +++ b/runtime/vault-filesystem/src/main/java/io/aklivity/zilla/runtime/vault/filesystem/internal/FileSystemContext.java @@ -15,24 +15,29 @@ */ package io.aklivity.zilla.runtime.vault.filesystem.internal; +import static io.aklivity.zilla.runtime.engine.EngineConfiguration.ENGINE_CERTIFICATE_REVOCATION_STRATEGY; + import java.nio.file.Path; import java.util.function.Function; import io.aklivity.zilla.runtime.engine.Configuration; import io.aklivity.zilla.runtime.engine.EngineContext; import io.aklivity.zilla.runtime.engine.config.VaultConfig; +import io.aklivity.zilla.runtime.engine.security.RevocationStrategy; import io.aklivity.zilla.runtime.engine.vault.VaultContext; import io.aklivity.zilla.runtime.vault.filesystem.config.FileSystemOptionsConfig; final class FileSystemContext implements VaultContext { private final Function resolvePath; + private final RevocationStrategy revocation; FileSystemContext( Configuration config, EngineContext context) { this.resolvePath = context::resolvePath; + this.revocation = ENGINE_CERTIFICATE_REVOCATION_STRATEGY.get(config); } @Override @@ -40,7 +45,7 @@ public FileSystemVaultHandler attach( VaultConfig vault) { FileSystemOptionsConfig options = (FileSystemOptionsConfig) vault.options; - return new FileSystemVaultHandler(options, resolvePath); + return new FileSystemVaultHandler(options, resolvePath, revocation); } @Override diff --git a/runtime/vault-filesystem/src/main/java/io/aklivity/zilla/runtime/vault/filesystem/internal/FileSystemVaultHandler.java b/runtime/vault-filesystem/src/main/java/io/aklivity/zilla/runtime/vault/filesystem/internal/FileSystemVaultHandler.java index 2a591eaf3f..42481e6b93 100644 --- a/runtime/vault-filesystem/src/main/java/io/aklivity/zilla/runtime/vault/filesystem/internal/FileSystemVaultHandler.java +++ b/runtime/vault-filesystem/src/main/java/io/aklivity/zilla/runtime/vault/filesystem/internal/FileSystemVaultHandler.java @@ -23,22 +23,29 @@ import java.security.KeyStore.Entry; import java.security.KeyStore.PrivateKeyEntry; import java.security.KeyStore.TrustedCertificateEntry; +import java.security.cert.CertPathValidator; import java.security.cert.Certificate; +import java.security.cert.PKIXBuilderParameters; +import java.security.cert.PKIXRevocationChecker; +import java.security.cert.X509CertSelector; import java.security.cert.X509Certificate; import java.util.ArrayList; import java.util.Collections; +import java.util.EnumSet; import java.util.List; import java.util.Objects; import java.util.Optional; import java.util.function.BiFunction; import java.util.function.Function; +import javax.net.ssl.CertPathTrustManagerParameters; import javax.net.ssl.KeyManagerFactory; import javax.net.ssl.TrustManagerFactory; import javax.security.auth.x500.X500Principal; import org.agrona.LangUtil; +import io.aklivity.zilla.runtime.engine.security.RevocationStrategy; import io.aklivity.zilla.runtime.engine.vault.VaultHandler; import io.aklivity.zilla.runtime.vault.filesystem.config.FileSystemOptionsConfig; import io.aklivity.zilla.runtime.vault.filesystem.config.FileSystemStoreConfig; @@ -46,14 +53,24 @@ public class FileSystemVaultHandler implements VaultHandler { private static final String STORE_TYPE_DEFAULT = "pkcs12"; + private static final String PKIX_ALGORITHM = "PKIX"; private final Function, KeyManagerFactory> supplyKeys; private final Function, KeyManagerFactory> supplySigners; private final BiFunction, KeyStore, TrustManagerFactory> supplyTrust; + private final RevocationStrategy revocation; public FileSystemVaultHandler( FileSystemOptionsConfig options, Function resolvePath) + { + this(options, resolvePath, RevocationStrategy.NONE); + } + + public FileSystemVaultHandler( + FileSystemOptionsConfig options, + Function resolvePath, + RevocationStrategy revocation) { FileSystemStoreInfo keys = supplyStoreInfo(resolvePath, options.keys); supplyKeys = keys != null @@ -65,6 +82,7 @@ public FileSystemVaultHandler( ? aliases -> newSignersFactory(aliases, signers, keys) : aliases -> null; + this.revocation = options.revocation != null ? options.revocation : revocation; FileSystemStoreInfo trust = supplyStoreInfo(resolvePath, options.trust); supplyTrust = (aliases, cacerts) -> newTrustFactory(trust, aliases, cacerts); } @@ -185,8 +203,28 @@ private TrustManagerFactory newTrustFactory( } } - factory = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm()); - factory.init(trust); + switch (revocation) + { + case CRL: + factory = TrustManagerFactory.getInstance(PKIX_ALGORITHM); + PKIXBuilderParameters pkixParams = new PKIXBuilderParameters(trust, new X509CertSelector()); + pkixParams.setRevocationEnabled(true); + + CertPathValidator validator = CertPathValidator.getInstance(PKIX_ALGORITHM); + PKIXRevocationChecker checker = (PKIXRevocationChecker) validator.getRevocationChecker(); + checker.setOptions(EnumSet.of( + PKIXRevocationChecker.Option.PREFER_CRLS + )); + pkixParams.addCertPathChecker(checker); + + CertPathTrustManagerParameters tmParams = new CertPathTrustManagerParameters(pkixParams); + factory.init(tmParams); + break; + default: + factory = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm()); + factory.init(trust); + break; + } } } catch (Exception ex) diff --git a/runtime/vault-filesystem/src/main/java/io/aklivity/zilla/runtime/vault/filesystem/internal/config/FileSystemOptionsConfigAdapter.java b/runtime/vault-filesystem/src/main/java/io/aklivity/zilla/runtime/vault/filesystem/internal/config/FileSystemOptionsConfigAdapter.java index 81e4f9a1cd..51343c1ca4 100644 --- a/runtime/vault-filesystem/src/main/java/io/aklivity/zilla/runtime/vault/filesystem/internal/config/FileSystemOptionsConfigAdapter.java +++ b/runtime/vault-filesystem/src/main/java/io/aklivity/zilla/runtime/vault/filesystem/internal/config/FileSystemOptionsConfigAdapter.java @@ -22,6 +22,7 @@ import io.aklivity.zilla.runtime.engine.config.OptionsConfig; import io.aklivity.zilla.runtime.engine.config.OptionsConfigAdapterSpi; +import io.aklivity.zilla.runtime.engine.security.RevocationStrategy; import io.aklivity.zilla.runtime.vault.filesystem.config.FileSystemOptionsConfig; import io.aklivity.zilla.runtime.vault.filesystem.config.FileSystemOptionsConfigBuilder; import io.aklivity.zilla.runtime.vault.filesystem.internal.FileSystemVault; @@ -31,6 +32,7 @@ public final class FileSystemOptionsConfigAdapter implements OptionsConfigAdapte private static final String KEYS_NAME = "keys"; private static final String TRUST_NAME = "trust"; private static final String SIGNERS_NAME = "signers"; + private static final String REVOCATION_NAME = "revocation"; private final FileSystemStoreConfigAdapter store = new FileSystemStoreConfigAdapter(); @@ -69,6 +71,11 @@ public JsonObject adaptToJson( object.add(SIGNERS_NAME, store.adaptToJson(fsOptions.signers)); } + if (fsOptions.revocation != null) + { + object.add(REVOCATION_NAME, fsOptions.revocation.name().toLowerCase()); + } + return object.build(); } @@ -93,6 +100,11 @@ public OptionsConfig adaptFromJson( fsOptions.signers(store.adaptFromJson(object.getJsonObject(SIGNERS_NAME))); } + if (object.containsKey(REVOCATION_NAME)) + { + fsOptions.revocation(RevocationStrategy.valueOf(object.getString(REVOCATION_NAME))); + } + return fsOptions.build(); } } diff --git a/runtime/vault-filesystem/src/test/java/io/aklivity/zilla/runtime/vault/filesystem/internal/FileSystemVaultTest.java b/runtime/vault-filesystem/src/test/java/io/aklivity/zilla/runtime/vault/filesystem/internal/FileSystemVaultTest.java index e04e97bbf1..e3ee0ddab5 100644 --- a/runtime/vault-filesystem/src/test/java/io/aklivity/zilla/runtime/vault/filesystem/internal/FileSystemVaultTest.java +++ b/runtime/vault-filesystem/src/test/java/io/aklivity/zilla/runtime/vault/filesystem/internal/FileSystemVaultTest.java @@ -74,7 +74,7 @@ public void shouldResolveClient() throws Exception .build() .build(); - FileSystemVaultHandler vault = new FileSystemVaultHandler(options, FileSystemVaultTest::resourcePath); + FileSystemVaultHandler vault = new FileSystemVaultHandler(options, FileSystemVaultTest::resourcePath, null); KeyManagerFactory keys = vault.initSigners(List.of("clientca")); diff --git a/specs/vault-filesystem.spec/src/main/scripts/io/aklivity/zilla/specs/vault/filesystem/config/vault.crl.yaml b/specs/vault-filesystem.spec/src/main/scripts/io/aklivity/zilla/specs/vault/filesystem/config/vault.crl.yaml new file mode 100644 index 0000000000..e0999442ae --- /dev/null +++ b/specs/vault-filesystem.spec/src/main/scripts/io/aklivity/zilla/specs/vault/filesystem/config/vault.crl.yaml @@ -0,0 +1,50 @@ +# +# Copyright 2021-2024 Aklivity Inc. +# +# Aklivity licenses this file to you under the Apache License, +# version 2.0 (the "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at: +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. +# + +--- +name: test +vaults: + server: + type: filesystem + options: + keys: + store: stores/server/keys + type: pkcs12 + password: generated + trust: + store: stores/server/trust + type: pkcs12 + password: generated + signers: + store: stores/server/signers + type: pkcs12 + password: generated + revocation: crl + client: + type: filesystem + options: + keys: + store: stores/client/keys + type: pkcs12 + password: generated + trust: + store: stores/client/trust + type: pkcs12 + password: generated + signers: + store: stores/client/signers + type: pkcs12 + password: generated diff --git a/specs/vault-filesystem.spec/src/main/scripts/io/aklivity/zilla/specs/vault/filesystem/schema/filesystem.schema.patch.json b/specs/vault-filesystem.spec/src/main/scripts/io/aklivity/zilla/specs/vault/filesystem/schema/filesystem.schema.patch.json index 3e0c3c3888..07d2b5d974 100644 --- a/specs/vault-filesystem.spec/src/main/scripts/io/aklivity/zilla/specs/vault/filesystem/schema/filesystem.schema.patch.json +++ b/specs/vault-filesystem.spec/src/main/scripts/io/aklivity/zilla/specs/vault/filesystem/schema/filesystem.schema.patch.json @@ -96,6 +96,12 @@ } }, "additionalProperties": false + }, + "revocation": + { + "title": "Revocation Method", + "type": "string", + "enum": [ "crl" ] } }, "additionalProperties": false diff --git a/specs/vault-filesystem.spec/src/test/java/io/aklivity/zilla/specs/vault/filesystem/config/SchemaTest.java b/specs/vault-filesystem.spec/src/test/java/io/aklivity/zilla/specs/vault/filesystem/config/SchemaTest.java index 89e0505973..88cc17e90c 100644 --- a/specs/vault-filesystem.spec/src/test/java/io/aklivity/zilla/specs/vault/filesystem/config/SchemaTest.java +++ b/specs/vault-filesystem.spec/src/test/java/io/aklivity/zilla/specs/vault/filesystem/config/SchemaTest.java @@ -40,4 +40,12 @@ public void shouldValidateVault() assertThat(config, not(nullValue())); } + + @Test + public void shouldValidateVaultWithCRL() + { + JsonObject config = schema.validate("vault.crl.yaml"); + + assertThat(config, not(nullValue())); + } }