From 105e06f87fbc0733e4700bcbb8e3d60f25707e8f Mon Sep 17 00:00:00 2001 From: Martin Mazanek Date: Tue, 30 Oct 2018 14:59:48 +0100 Subject: [PATCH] [WFCORE-4058] Automatic detection of file based KeyStore types https://issues.jboss.org/browse/WFCORE-4058 requires ELY-1650 https://issues.jboss.org/browse/ELY-1650 --- .../elytron/ElytronSubsystemTransformers.java | 5 +- .../extension/elytron/KeyStoreDefinition.java | 8 +-- .../extension/elytron/KeyStoreService.java | 32 +++++++++-- .../_private/ElytronSubsystemMessages.java | 6 +++ .../resources/schema/wildfly-elytron_6_0.xsd | 2 +- .../extension/elytron/KeyStoresTestCase.java | 53 +++++++++++++++++++ .../elytron/SubsystemTransformerTestCase.java | 2 + .../elytron-transformers-4.0-reject.xml | 5 ++ .../org/wildfly/extension/elytron/tls-ibm.xml | 5 ++ .../org/wildfly/extension/elytron/tls-sun.xml | 5 ++ 10 files changed, 113 insertions(+), 10 deletions(-) diff --git a/elytron/src/main/java/org/wildfly/extension/elytron/ElytronSubsystemTransformers.java b/elytron/src/main/java/org/wildfly/extension/elytron/ElytronSubsystemTransformers.java index 13df93f7d6a..d2eaf07a07e 100644 --- a/elytron/src/main/java/org/wildfly/extension/elytron/ElytronSubsystemTransformers.java +++ b/elytron/src/main/java/org/wildfly/extension/elytron/ElytronSubsystemTransformers.java @@ -83,7 +83,10 @@ public void registerTransformers(SubsystemTransformerRegistration registration) private static void from6(ChainedTransformationDescriptionBuilder chainedBuilder) { ResourceTransformationDescriptionBuilder builder = chainedBuilder.createBuilder(ELYTRON_6_0_0, ELYTRON_5_0_0); - + builder.addChildResource(PathElement.pathElement(ElytronDescriptionConstants.KEY_STORE)) + .getAttributeBuilder() + .addRejectCheck(RejectAttributeChecker.UNDEFINED, ElytronDescriptionConstants.TYPE) + .end(); } diff --git a/elytron/src/main/java/org/wildfly/extension/elytron/KeyStoreDefinition.java b/elytron/src/main/java/org/wildfly/extension/elytron/KeyStoreDefinition.java index d2b0a47e9d8..0b2a0b09cdc 100644 --- a/elytron/src/main/java/org/wildfly/extension/elytron/KeyStoreDefinition.java +++ b/elytron/src/main/java/org/wildfly/extension/elytron/KeyStoreDefinition.java @@ -82,7 +82,7 @@ final class KeyStoreDefinition extends SimpleResourceDefinition { static final ServiceUtil KEY_STORE_UTIL = ServiceUtil.newInstance(KEY_STORE_RUNTIME_CAPABILITY, ElytronDescriptionConstants.KEY_STORE, KeyStore.class); - static final SimpleAttributeDefinition TYPE = new SimpleAttributeDefinitionBuilder(ElytronDescriptionConstants.TYPE, ModelType.STRING, false) + static final SimpleAttributeDefinition TYPE = new SimpleAttributeDefinitionBuilder(ElytronDescriptionConstants.TYPE, ModelType.STRING, true) .setAttributeGroup(ElytronDescriptionConstants.IMPLEMENTATION) .setAllowExpression(true) .setMinSize(1) @@ -241,7 +241,7 @@ protected void performRuntime(OperationContext context, ModelNode operation, Res ModelNode model = resource.getModel(); String providers = PROVIDERS.resolveModelAttribute(context, model).asStringOrNull(); String providerName = PROVIDER_NAME.resolveModelAttribute(context, model).asStringOrNull(); - String type = TYPE.resolveModelAttribute(context, model).asString(); + String type = TYPE.resolveModelAttribute(context, model).asStringOrNull(); String path = PATH.resolveModelAttribute(context, model).asStringOrNull(); String relativeTo = null; boolean required; @@ -251,9 +251,11 @@ protected void performRuntime(OperationContext context, ModelNode operation, Res if (path != null) { relativeTo = RELATIVE_TO.resolveModelAttribute(context, model).asStringOrNull(); required = REQUIRED.resolveModelAttribute(context, model).asBoolean(); - keyStoreService = KeyStoreService.createFileBasedKeyStoreService(providerName, type, relativeTo, path, required, aliasFilter); } else { + if (type == null) { + throw ROOT_LOGGER.filelessKeyStoreMissingType(); + } keyStoreService = KeyStoreService.createFileLessKeyStoreService(providerName, type, aliasFilter); } diff --git a/elytron/src/main/java/org/wildfly/extension/elytron/KeyStoreService.java b/elytron/src/main/java/org/wildfly/extension/elytron/KeyStoreService.java index 451385cbf05..1d64160d694 100644 --- a/elytron/src/main/java/org/wildfly/extension/elytron/KeyStoreService.java +++ b/elytron/src/main/java/org/wildfly/extension/elytron/KeyStoreService.java @@ -54,6 +54,7 @@ import org.wildfly.security.keystore.AliasFilter; import org.wildfly.security.keystore.AtomicLoadKeyStore; import org.wildfly.security.keystore.FilteringKeyStore; +import org.wildfly.security.keystore.KeyStoreUtil; import org.wildfly.security.keystore.ModifyTrackingKeyStore; import org.wildfly.security.keystore.UnmodifiableKeyStore; import org.wildfly.security.password.interfaces.ClearPassword; @@ -108,8 +109,13 @@ static KeyStoreService createFileBasedKeyStoreService(String provider, String ty @Override public void start(StartContext startContext) throws StartException { try { - Provider provider = resolveProvider(); - AtomicLoadKeyStore keyStore = AtomicLoadKeyStore.newInstance(type, provider); + AtomicLoadKeyStore keyStore = null; + + if (type != null) { + Provider provider = resolveProvider(); + keyStore = AtomicLoadKeyStore.newInstance(type, provider); + } + if (path != null) { pathResolver = pathResolver(); resolvedPath = getResolvedPath(pathResolver, path, relativeTo); @@ -117,13 +123,14 @@ public void start(StartContext startContext) throws StartException { synched = System.currentTimeMillis(); if (resolvedPath != null && ! resolvedPath.exists()) { - if (required) { + if (required || type == null) { throw ROOT_LOGGER.keyStoreFileNotExists(resolvedPath.getAbsolutePath()); } else { ROOT_LOGGER.keyStoreFileNotExistsButIgnored(resolvedPath.getAbsolutePath()); } } - try (InputStream is = (resolvedPath != null && resolvedPath.exists()) ? new FileInputStream(resolvedPath) : null) { + + try (FileInputStream is = (resolvedPath != null && resolvedPath.exists()) ? new FileInputStream(resolvedPath) : null) { char[] password = resolvePassword(); ROOT_LOGGER.tracef( @@ -132,7 +139,22 @@ public void start(StartContext startContext) throws StartException { ); if (is != null) { - keyStore.load(is, password); + if (type != null) { + keyStore.load(is, password); + } else { + Provider[] resolvedProviders = providers.getOptionalValue(); + if (resolvedProviders == null) { + resolvedProviders = Security.getProviders(); + } + final Provider[] finalProviders = resolvedProviders; + KeyStore detected = KeyStoreUtil.loadKeyStore(() -> finalProviders, this.provider, is, resolvedPath.getPath(), password); + + if (detected == null) { + throw ROOT_LOGGER.unableToDetectKeyStore(resolvedPath.getPath()); + } + + keyStore = AtomicLoadKeyStore.atomize(detected); + } } else { synchronized (EmptyProvider.getInstance()) { keyStore.load(null, password); diff --git a/elytron/src/main/java/org/wildfly/extension/elytron/_private/ElytronSubsystemMessages.java b/elytron/src/main/java/org/wildfly/extension/elytron/_private/ElytronSubsystemMessages.java index b5670bdbcb4..294612ad093 100644 --- a/elytron/src/main/java/org/wildfly/extension/elytron/_private/ElytronSubsystemMessages.java +++ b/elytron/src/main/java/org/wildfly/extension/elytron/_private/ElytronSubsystemMessages.java @@ -551,4 +551,10 @@ public interface ElytronSubsystemMessages extends BasicLogger { @Message(id = 1058, value = "Failed to parse PEM public key with kid: %s") OperationFailedException failedToParsePEMPublicKey(String kid); + @Message(id = 1059, value = "Unable to detect KeyStore '%s'") + StartException unableToDetectKeyStore(String path); + + @Message(id = 1060, value = "Fileless KeyStore needs to have a defined type.") + OperationFailedException filelessKeyStoreMissingType(); + } diff --git a/elytron/src/main/resources/schema/wildfly-elytron_6_0.xsd b/elytron/src/main/resources/schema/wildfly-elytron_6_0.xsd index 481be26268d..86fb32cb39f 100644 --- a/elytron/src/main/resources/schema/wildfly-elytron_6_0.xsd +++ b/elytron/src/main/resources/schema/wildfly-elytron_6_0.xsd @@ -4610,7 +4610,7 @@ keystore implementation details - + The KeyStore type, e.g. jks, pkcs#12. diff --git a/elytron/src/test/java/org/wildfly/extension/elytron/KeyStoresTestCase.java b/elytron/src/test/java/org/wildfly/extension/elytron/KeyStoresTestCase.java index b9f8f5a8924..f2f2f9f3e8d 100644 --- a/elytron/src/test/java/org/wildfly/extension/elytron/KeyStoresTestCase.java +++ b/elytron/src/test/java/org/wildfly/extension/elytron/KeyStoresTestCase.java @@ -500,6 +500,59 @@ public void testFilteringKeystoreCli() throws Exception { assertTrue(firefly.get(ElytronDescriptionConstants.CERTIFICATE_CHAIN).isDefined()); } + @Test + public void testAutomaticKeystoreService() throws Exception { + ServiceName serviceName = Capabilities.KEY_STORE_RUNTIME_CAPABILITY.getCapabilityServiceName("AutomaticKeystore"); + KeyStore keyStore = (KeyStore) services.getContainer().getService(serviceName).getValue(); + assertNotNull(keyStore); + + assertTrue(keyStore.containsAlias("firefly")); + assertTrue(keyStore.isKeyEntry("firefly")); + X509Certificate cert = (X509Certificate) keyStore.getCertificate("firefly"); + assertEquals("OU=Elytron, O=Elytron, C=UK, ST=Elytron, CN=Firefly", cert.getSubjectDN().getName()); + assertEquals("firefly", keyStore.getCertificateAlias(cert)); + + Certificate[] chain = keyStore.getCertificateChain("firefly"); + assertEquals("OU=Elytron, O=Elytron, C=UK, ST=Elytron, CN=Firefly", ((X509Certificate) chain[0]).getSubjectDN().getName()); + assertEquals("O=Root Certificate Authority, EMAILADDRESS=elytron@wildfly.org, C=UK, ST=Elytron, CN=Elytron CA", ((X509Certificate) chain[1]).getSubjectDN().getName()); + + assertTrue(keyStore.containsAlias("ca")); + assertTrue(keyStore.isCertificateEntry("ca")); + X509Certificate certCa = (X509Certificate) keyStore.getCertificate("ca"); + assertEquals("O=Root Certificate Authority, EMAILADDRESS=elytron@wildfly.org, C=UK, ST=Elytron, CN=Elytron CA", certCa.getSubjectDN().getName()); + assertEquals("ca", keyStore.getCertificateAlias(certCa)); + } + + @Test + public void testAutomaticKeystoreCli() throws Exception { + ModelNode operation = new ModelNode(); + operation.get(ClientConstants.OP_ADDR).add("subsystem","elytron"); + operation.get(ClientConstants.OP).set("read-resource"); + System.out.println(services.executeOperation(operation).get(ClientConstants.RESULT).asString()); + operation = new ModelNode(); + operation.get(ClientConstants.OP_ADDR).add("subsystem","elytron").add(ElytronDescriptionConstants.KEY_STORE,"AutomaticKeystore"); + operation.get(ClientConstants.OP).set(ElytronDescriptionConstants.READ_ALIASES); + List nodes = assertSuccess(services.executeOperation(operation)).get(ClientConstants.RESULT).asList(); + assertEquals(2, nodes.size()); + + operation = new ModelNode(); + operation.get(ClientConstants.OP_ADDR).add("subsystem","elytron").add(ElytronDescriptionConstants.KEY_STORE,"AutomaticKeystore"); + operation.get(ClientConstants.OP).set(ElytronDescriptionConstants.READ_ALIAS); + operation.get(ElytronDescriptionConstants.ALIAS).set("firefly"); + ModelNode firefly = assertSuccess(services.executeOperation(operation)).get(ClientConstants.RESULT); + assertEquals("firefly", firefly.get(ElytronDescriptionConstants.ALIAS).asString()); + assertEquals(KeyStore.PrivateKeyEntry.class.getSimpleName(), firefly.get(ElytronDescriptionConstants.ENTRY_TYPE).asString()); + assertTrue(firefly.get(ElytronDescriptionConstants.CERTIFICATE_CHAIN).isDefined()); + + operation = new ModelNode(); + operation.get(ClientConstants.OP_ADDR).add("subsystem","elytron").add(ElytronDescriptionConstants.KEY_STORE,"AutomaticKeystore"); + operation.get(ClientConstants.OP).set(ElytronDescriptionConstants.READ_ALIAS); + operation.get(ElytronDescriptionConstants.ALIAS).set("ca"); + ModelNode ca = assertSuccess(services.executeOperation(operation)).get(ClientConstants.RESULT); + assertEquals("ca", ca.get(ElytronDescriptionConstants.ALIAS).asString()); + assertEquals(KeyStore.TrustedCertificateEntry.class.getSimpleName(), ca.get(ElytronDescriptionConstants.ENTRY_TYPE).asString()); + } + @Test public void testGenerateKeyPair() throws Exception { addKeyStore(); diff --git a/elytron/src/test/java/org/wildfly/extension/elytron/SubsystemTransformerTestCase.java b/elytron/src/test/java/org/wildfly/extension/elytron/SubsystemTransformerTestCase.java index 680aa89c7ba..e1fc1a73f9f 100644 --- a/elytron/src/test/java/org/wildfly/extension/elytron/SubsystemTransformerTestCase.java +++ b/elytron/src/test/java/org/wildfly/extension/elytron/SubsystemTransformerTestCase.java @@ -115,6 +115,8 @@ public void testRejectingTransformersEAP720() throws Exception { FailedOperationTransformationConfig.REJECTED_RESOURCE) .addFailedAttribute(SUBSYSTEM_ADDRESS.append(PathElement.pathElement(ElytronDescriptionConstants.TOKEN_REALM, "KeyMapTokenRealm")), FailedOperationTransformationConfig.REJECTED_RESOURCE + ).addFailedAttribute(SUBSYSTEM_ADDRESS.append(PathElement.pathElement(ElytronDescriptionConstants.KEY_STORE, "automatic.keystore")), + FailedOperationTransformationConfig.REJECTED_RESOURCE )); } diff --git a/elytron/src/test/resources/org/wildfly/extension/elytron/elytron-transformers-4.0-reject.xml b/elytron/src/test/resources/org/wildfly/extension/elytron/elytron-transformers-4.0-reject.xml index cb7a04a4be2..d2fb9753551 100644 --- a/elytron/src/test/resources/org/wildfly/extension/elytron/elytron-transformers-4.0-reject.xml +++ b/elytron/src/test/resources/org/wildfly/extension/elytron/elytron-transformers-4.0-reject.xml @@ -32,6 +32,11 @@ + + + + + diff --git a/elytron/src/test/resources/org/wildfly/extension/elytron/tls-ibm.xml b/elytron/src/test/resources/org/wildfly/extension/elytron/tls-ibm.xml index 38874ad8257..8713c4d0f41 100644 --- a/elytron/src/test/resources/org/wildfly/extension/elytron/tls-ibm.xml +++ b/elytron/src/test/resources/org/wildfly/extension/elytron/tls-ibm.xml @@ -43,6 +43,11 @@ + + + + + diff --git a/elytron/src/test/resources/org/wildfly/extension/elytron/tls-sun.xml b/elytron/src/test/resources/org/wildfly/extension/elytron/tls-sun.xml index 8a40bae86a5..213e8d2ffef 100644 --- a/elytron/src/test/resources/org/wildfly/extension/elytron/tls-sun.xml +++ b/elytron/src/test/resources/org/wildfly/extension/elytron/tls-sun.xml @@ -43,6 +43,11 @@ + + + + +