Skip to content

Commit

Permalink
[WFCORE-4058] Automatic detection of file based KeyStore types
Browse files Browse the repository at this point in the history
  • Loading branch information
nekdozjam committed Jan 16, 2019
1 parent 7315627 commit 105e06f
Show file tree
Hide file tree
Showing 10 changed files with 113 additions and 10 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -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();
}


Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,7 @@ final class KeyStoreDefinition extends SimpleResourceDefinition {

static final ServiceUtil<KeyStore> 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)
Expand Down Expand Up @@ -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;
Expand All @@ -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);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -108,22 +109,28 @@ 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);
}

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(
Expand All @@ -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);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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();

}
2 changes: 1 addition & 1 deletion elytron/src/main/resources/schema/wildfly-elytron_6_0.xsd
Original file line number Diff line number Diff line change
Expand Up @@ -4610,7 +4610,7 @@
keystore implementation details
</xs:documentation>
</xs:annotation>
<xs:attribute name="type" type="xs:string" use="required">
<xs:attribute name="type" type="xs:string" use="optional">
<xs:annotation>
<xs:documentation>
The KeyStore type, e.g. jks, pkcs#12.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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, [email protected], 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, [email protected], 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<ModelNode> 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();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
));
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,11 @@
<implementation type="JKS"/>
<file path="accounts.keystore.jks" relative-to="jboss.server.config.dir"/>
</key-store>
<key-store name="automatic.keystore">
<credential-reference clear-text="elytron"/>
<implementation/>
<file path="accounts.keystore.jks" relative-to="jboss.server.config.dir"/>
</key-store>
</key-stores>
<key-managers>
<key-manager name="key1" key-store="accounts.keystore">
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,11 @@
<implementation type="JKS" />
<file path="target/not-existing.keystore" required="false"/>
</key-store>
<key-store name="AutomaticKeystore" >
<credential-reference clear-text="Elytron"/>
<implementation/>
<file path="firefly.keystore" relative-to="jboss.server.config.dir"/>
</key-store>
<filtering-key-store name="FilteringKeyStore" key-store="FireflyKeystore" alias-filter="NONE:+firefly"/>
</key-stores>
<key-managers>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,11 @@
<implementation type="JKS" />
<file path="target/not-existing.keystore" required="false"/>
</key-store>
<key-store name="AutomaticKeystore" >
<credential-reference clear-text="Elytron"/>
<implementation/>
<file path="firefly.keystore" relative-to="jboss.server.config.dir"/>
</key-store>
<filtering-key-store name="FilteringKeyStore" key-store="FireflyKeystore" alias-filter="NONE:+firefly"/>
</key-stores>
<key-managers>
Expand Down

0 comments on commit 105e06f

Please sign in to comment.