diff --git a/auth/client/src/main/java/org/wildfly/security/auth/client/ElytronXmlParser.java b/auth/client/src/main/java/org/wildfly/security/auth/client/ElytronXmlParser.java index 090063ebb87..4a1f8f9cb41 100644 --- a/auth/client/src/main/java/org/wildfly/security/auth/client/ElytronXmlParser.java +++ b/auth/client/src/main/java/org/wildfly/security/auth/client/ElytronXmlParser.java @@ -22,6 +22,15 @@ import static javax.xml.stream.XMLStreamConstants.START_ELEMENT; import static org.wildfly.common.Assert.checkMinimumParameter; import static org.wildfly.common.Assert.checkNotNullParam; +import static org.wildfly.security.auth.client.XMLParserUtils.isSet; +import static org.wildfly.security.auth.client.XMLParserUtils.setBit; +import static org.wildfly.security.auth.client.XMLParserUtils.checkAttributeNamespace; +import static org.wildfly.security.auth.client.XMLParserUtils.requireNoAttributes; +import static org.wildfly.security.auth.client.XMLParserUtils.requireSingleAttribute; +import static org.wildfly.security.auth.client.XMLParserUtils.requireSingleURIAttribute; +import static org.wildfly.security.auth.client.XMLParserUtils.missingAttribute; +import static org.wildfly.security.auth.client.XMLParserUtils.invalidPortNumber; +import static org.wildfly.security.auth.client.XMLParserUtils.andThenOp; import static org.wildfly.security.auth.client._private.ElytronMessages.xmlLog; import static org.wildfly.security.provider.util.ProviderUtil.INSTALLED_PROVIDERS; import static org.wildfly.security.provider.util.ProviderUtil.findProvider; @@ -222,7 +231,7 @@ public static SecurityFactory parseAuthenticationClientCo final ClientConfiguration clientConfiguration = ClientConfiguration.getInstance(uri); if (clientConfiguration != null) try (final ConfigurationXMLStreamReader streamReader = clientConfiguration.readConfiguration(KNOWN_NAMESPACES.keySet())) { if (streamReader != null) { - xmlLog.tracef("Parsig configuration from %s for namespace %s", streamReader.getUri(), streamReader.getNamespaceURI()); + xmlLog.tracef("Parsing configuration from %s for namespace %s", streamReader.getUri(), streamReader.getNamespaceURI()); return parseAuthenticationClientConfiguration(streamReader); } else { if (xmlLog.isTraceEnabled()) { @@ -1434,18 +1443,6 @@ private static MatchRule parseMatchAbstractType(final MatchRule rule, final Conf return name == null && authority == null ? rule : rule.matchAbstractType(name, authority); } - private static boolean isSet(int var, int bit) { - return (var & 1 << bit) != 0; - } - - private static int setBit(int var, int bit) { - return var | 1 << bit; - } - - private static ExceptionUnaryOperator andThenOp(ExceptionUnaryOperator first, ExceptionUnaryOperator second) { - return t -> second.apply(first.apply(t)); - } - private static ExceptionSupplier parseCredentialsType(final ConfigurationXMLStreamReader reader, final Version xmlVersion, final Map> keyStoresMap, final Map> credentialStoresMap, Supplier providers) throws ConfigXMLParseException { ExceptionUnaryOperator function = parent -> CredentialSource.NONE; requireNoAttributes(reader); @@ -3501,51 +3498,6 @@ private static void checkElementNamespace(final ConfigurationXMLStreamReader rea } } - private static void checkAttributeNamespace(final ConfigurationXMLStreamReader reader, final int idx) throws ConfigXMLParseException { - final String attributeNamespace = reader.getAttributeNamespace(idx); - if (attributeNamespace != null && ! attributeNamespace.isEmpty()) { - throw reader.unexpectedAttribute(idx); - } - } - - private static void requireNoAttributes(final ConfigurationXMLStreamReader reader) throws ConfigXMLParseException { - final int attributeCount = reader.getAttributeCount(); - if (attributeCount > 0) { - throw reader.unexpectedAttribute(0); - } - } - - private static String requireSingleAttribute(final ConfigurationXMLStreamReader reader, final String attributeName) throws ConfigXMLParseException { - return requireSingleAttribute(reader, attributeName, (ExceptionSupplier) () -> reader.getAttributeValueResolved(0)); - } - - private static URI requireSingleURIAttribute(final ConfigurationXMLStreamReader reader, final String attributeName) throws ConfigXMLParseException { - return requireSingleAttribute(reader, attributeName, () -> reader.getURIAttributeValueResolved(0)); - } - - private static A requireSingleAttribute(final ConfigurationXMLStreamReader reader, final String attributeName, ExceptionSupplier attributeFunction) throws ConfigXMLParseException { - final int attributeCount = reader.getAttributeCount(); - if (attributeCount < 1) { - throw reader.missingRequiredAttribute("", attributeName); - } - checkAttributeNamespace(reader, 0); - if (! reader.getAttributeLocalName(0).equals(attributeName)) { - throw reader.unexpectedAttribute(0); - } - if (attributeCount > 1) { - throw reader.unexpectedAttribute(1); - } - return attributeFunction.get(); - } - - private static ConfigXMLParseException missingAttribute(final ConfigurationXMLStreamReader reader, final String name) { - return reader.missingRequiredAttribute(null, name); - } - - private static ConfigXMLParseException invalidPortNumber(final ConfigurationXMLStreamReader reader, final int index) throws ConfigXMLParseException { - return xmlLog.xmlInvalidPortNumber(reader, reader.getAttributeValueResolved(index), reader.getAttributeLocalName(index), reader.getName()); - } - static final class KeyStoreCreateFactory implements ExceptionSupplier { private final String providerName; private final Supplier providers; diff --git a/auth/client/src/main/java/org/wildfly/security/auth/client/EncryptedExpressionConfig.java b/auth/client/src/main/java/org/wildfly/security/auth/client/EncryptedExpressionConfig.java new file mode 100644 index 00000000000..7f95007bbf1 --- /dev/null +++ b/auth/client/src/main/java/org/wildfly/security/auth/client/EncryptedExpressionConfig.java @@ -0,0 +1,72 @@ +package org.wildfly.security.auth.client; + + +import org.wildfly.common.Assert; +import org.wildfly.security.auth.server.IdentityCredentials; +import org.wildfly.security.credential.Credential; +import org.wildfly.security.credential.source.CredentialSource; +import org.wildfly.security.credential.source.impl.CredentialStoreCredentialSource; +import org.wildfly.security.credential.store.CredentialStore; +import org.wildfly.security.provider.util.ProviderFactory; + +import java.security.Provider; +import java.util.function.Supplier; + + +public final class EncryptedExpressionConfig { + + private static final int SET_CREDENTIAL_STORE = 0; + private static final int SET_RESOLVER = 1; + private static final int SET_DEFAULT_RESOLVER = 2; + private static final Supplier DEFAULT_PROVIDER_SUPPLIER = ProviderFactory.getDefaultProviderSupplier(EncryptedExpressionConfig.class.getClassLoader()); + + public static EncryptedExpressionConfig empty() { + return new EncryptedExpressionConfig(); + } + + CredentialSource credentialSource; + + EncryptedExpressionConfig() { + this.credentialSource = null; + } + + private EncryptedExpressionConfig(final EncryptedExpressionConfig original, final int what, final Object value) { + this.credentialSource = what == SET_CREDENTIAL_STORE ? (CredentialSource) value : original.credentialSource; + } + + private EncryptedExpressionConfig(final EncryptedExpressionConfig original, final EncryptedExpressionConfig other) { + this.credentialSource = other.credentialSource; + } + + private static T getOrDefault(T value, T defVal) { + return value != null ? value : defVal; + } + + private static int getOrDefault(int value, int defVal) { + return value != -1 ? value : defVal; + } + + CredentialSource getCredentialSource() { + return credentialSource; + } + public EncryptedExpressionConfig useCredentialStoreEntry(CredentialStore credentialStore, String alias) { + Assert.checkNotNullParam("credentialStore", credentialStore); + Assert.checkNotNullParam("alias", alias); + CredentialStoreCredentialSource csCredentialSource = new CredentialStoreCredentialSource(credentialStore, alias); + return useCredentials(getCredentialSource().with(csCredentialSource)); + } + + public EncryptedExpressionConfig useCredentials(CredentialSource credentials) { + return new EncryptedExpressionConfig(this, SET_CREDENTIAL_STORE, credentials == null ? CredentialSource.NONE : credentials); + } + + public EncryptedExpressionConfig useCredential(Credential credential) { + if (credential == null) return this; + final CredentialSource credentialSource = this.credentialSource; + if (credentialSource == CredentialSource.NONE) { + return new EncryptedExpressionConfig(this, SET_CREDENTIAL_STORE, IdentityCredentials.NONE.withCredential(credential)); + } else { + return new EncryptedExpressionConfig(this, SET_CREDENTIAL_STORE, credentialSource.with(IdentityCredentials.NONE.withCredential(credential))); + } + } +} diff --git a/auth/client/src/main/java/org/wildfly/security/auth/client/EncryptedExpressionContext.java b/auth/client/src/main/java/org/wildfly/security/auth/client/EncryptedExpressionContext.java new file mode 100644 index 00000000000..e5fe37b0844 --- /dev/null +++ b/auth/client/src/main/java/org/wildfly/security/auth/client/EncryptedExpressionContext.java @@ -0,0 +1,187 @@ +/* + * JBoss, Home of Professional Open Source. + * Copyright 2023 Red Hat, Inc., and individual contributors + * as indicated by the @author tags. + * + * Licensed 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 org.wildfly.security.auth.client; + +import org.wildfly.common.context.ContextManager; +import org.wildfly.common.context.Contextual; +import org.wildfly.security.Version; + +import java.security.PrivilegedAction; +import java.util.function.Supplier; + +import static java.security.AccessController.doPrivileged; + +/** + * A set of resolvers and credential stores used to handle encrypted expressions. + * + * @author Prarthona Paul + */ +public final class EncryptedExpressionContext implements Contextual{ + private static final ContextManager CONTEXT_MANAGER = new ContextManager(EncryptedExpressionContext.class); + + private static final Supplier SUPPLIER = doPrivileged((PrivilegedAction>) CONTEXT_MANAGER::getPrivilegedSupplier); + + static { + Version.getVersion(); + CONTEXT_MANAGER.setGlobalDefaultSupplier(() -> EncryptedExpressionContext.EMPTY); + } + + final EncryptedExpressionConfig encryptedExpressionConfig = new EncryptedExpressionConfig(); + + static final EncryptedExpressionContext EMPTY = new EncryptedExpressionContext(); + final RuleNode encryptionRuleNode; + + EncryptedExpressionContext() { + this(null); + } + + EncryptedExpressionContext(final RuleNode encryptionRuleNode) { + this.encryptionRuleNode = encryptionRuleNode; + } + + /** + * Get a new, empty encrypted expression context. + * + * @return the new encrypted expression context. + */ + public static EncryptedExpressionContext empty() { + return EMPTY; + } + + /** + * Get the current thread's captured authentication context. + * + * @return the current thread's captured authentication context + */ + public static EncryptedExpressionContext captureCurrent() { + return SUPPLIER.get(); + } + + + private static RuleNode with(RuleNode node, MatchRule rule, T item) { + return node == null ? new RuleNode(null, rule, item) : node.with(rule, item); + } + + private static RuleNode with(RuleNode node, MatchRule rule, T item, int idx) { + return node == null ? new RuleNode(null, rule, item) : node.with(rule, item, idx); + } + + private static RuleNode replacing(RuleNode node, MatchRule rule, T item, int idx) { + return node == null ? new RuleNode(null, rule, item) : node.replacing(rule, item, idx); + } + + private static RuleNode withAll(RuleNode node, RuleNode otherNode) { + return node == null ? otherNode : otherNode == null ? node : node.withAll(otherNode); + } + + private static RuleNode withAll(RuleNode node, RuleNode otherNode, int idx) { + return node == null ? otherNode : otherNode == null ? node : node.withAll(otherNode, idx); + } + + private static RuleNode without(RuleNode node, int idx) { + return node == null ? null : node.without(idx); + } + + /** + * Get a new encrypted expression context which is the same as this one, but which includes the given rule and configuration at + * the end of its list. + * + * @param rule the rule to match + * @param configuration the configuration to select when the rule matches + * @return the combined encrypted expression context + */ + public EncryptedExpressionContext with(MatchRule rule, EncryptedExpressionConfig configuration) { + if (configuration == null || rule == null) return this; + return new EncryptedExpressionContext(with(encryptionRuleNode, rule, configuration)); + } + + /** + * Get a new encryptedExpression context which is the same as this one, but which includes the configurations + * of the given context at the end of its list. + * + * @param other the other encryptedExpression context + * @return the combined encryptedExpression context + */ + public EncryptedExpressionContext with(EncryptedExpressionContext other) { + if (other == null) return this; + return new EncryptedExpressionContext(withAll(encryptionRuleNode, other.encryptionRuleNode)); + } + + /** + * Get a new encryptedExpression context which is the same as this one, but which includes the given configuration + * inserted at the position of its list indicated by the {@code idx} parameter. + * + * @param idx the index at which insertion should be done + * @param rule the rule to match + * @param configuration the configuration to select when the rule matches + * @return the combined encryptedExpression context + * @throws IndexOutOfBoundsException if the index is out of bounds + */ + public EncryptedExpressionContext with(int idx, MatchRule rule, EncryptedExpressionConfig configuration) throws IndexOutOfBoundsException { + if (configuration == null || rule == null) return this; + return new EncryptedExpressionContext(with(encryptionRuleNode, rule, configuration, idx)); + } + + /** + * Get a new encryptedExpression context which is the same as this one, but which replaces the rule and configuration at the given + * index with the given rule and configuration. + * + * @param idx the index at which insertion should be done + * @param rule the rule to match + * @param configuration the configuration to select when the rule matches + * @return the combined encryptedExpression context + * @throws IndexOutOfBoundsException if the index is out of bounds + */ + public EncryptedExpressionContext replacing(int idx, MatchRule rule, EncryptedExpressionConfig configuration) throws IndexOutOfBoundsException { + if (configuration == null || rule == null) return this; + return new EncryptedExpressionContext(replacing(encryptionRuleNode, rule, configuration, idx)); + } + + /** + * Get a new encryptedExpression context which is the same as this one, but without the rule and configuration at the index + * indicated by the {@code idx} parameter. + * + * @param idx the index at which removal should be done + * @return the modified encryptedExpression context + * @throws IndexOutOfBoundsException if the index is out of bounds + */ + public EncryptedExpressionContext without(int idx) throws IndexOutOfBoundsException { + return new EncryptedExpressionContext(without(encryptionRuleNode, idx)); + } + + /** + * Run a privileged action with this encrypted expression context associated for the duration of the task. + * + * @param action the action to run under association + * @param the action return type + * @return the action return value + */ + public T run(PrivilegedAction action) { + return runAction(action); + } + + public ContextManager getInstanceContextManager() { + return getContextManager(); + } + + public static ContextManager getContextManager() { + return CONTEXT_MANAGER; + } + +} diff --git a/auth/client/src/main/java/org/wildfly/security/auth/client/EncryptedExpressionsXmlParser.java b/auth/client/src/main/java/org/wildfly/security/auth/client/EncryptedExpressionsXmlParser.java new file mode 100644 index 00000000000..668bade3102 --- /dev/null +++ b/auth/client/src/main/java/org/wildfly/security/auth/client/EncryptedExpressionsXmlParser.java @@ -0,0 +1,611 @@ +/* + * JBoss, Home of Professional Open Source. + * Copyright 2023 Red Hat, Inc., and individual contributors + * as indicated by the @author tags. + * + * Licensed 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 org.wildfly.security.auth.client; + +import org.wildfly.client.config.ClientConfiguration; +import org.wildfly.client.config.ConfigXMLParseException; +import org.wildfly.client.config.ConfigurationXMLStreamReader; +import org.wildfly.client.config.XMLLocation; +import org.wildfly.common.Assert; +import org.wildfly.common.function.ExceptionSupplier; +import org.wildfly.common.function.ExceptionUnaryOperator; +import org.wildfly.security.SecurityFactory; +import org.wildfly.security.auth.server.IdentityCredentials; +import org.wildfly.security.auth.util.ElytronAuthenticator; +import org.wildfly.security.credential.source.CredentialSource; +import org.wildfly.security.credential.store.CredentialStore; +import org.wildfly.security.password.Password; +import org.wildfly.security.password.PasswordFactory; +import org.wildfly.security.password.interfaces.ClearPassword; +import org.wildfly.security.password.spec.ClearPasswordSpec; +import org.wildfly.security.provider.util.ProviderFactory; +import org.wildfly.security.provider.util.ProviderServiceLoaderSupplier; +import org.wildfly.security.provider.util.ProviderUtil; + +import java.net.Authenticator; +import java.net.URI; +import java.security.NoSuchAlgorithmException; +import java.security.Provider; +import java.security.spec.InvalidKeySpecException; +import java.util.Collections; +import java.util.HashMap; +import java.util.Iterator; +import java.util.function.Supplier; +import java.util.Map; +import static org.wildfly.security.auth.client.ElytronXmlParser.DeferredSupplier; + +import static javax.xml.stream.XMLStreamConstants.END_ELEMENT; +import static javax.xml.stream.XMLStreamConstants.START_ELEMENT; +import static org.wildfly.security.auth.client.ElytronXmlParser.parseModuleRefType; +import static org.wildfly.security.auth.client.XMLParserUtils.checkAttributeNamespace; +import static org.wildfly.security.auth.client.XMLParserUtils.requireNoAttributes; +import static org.wildfly.security.auth.client.XMLParserUtils.missingAttribute; +import static org.wildfly.security.auth.client.XMLParserUtils.isSet; +import static org.wildfly.security.auth.client.XMLParserUtils.setBit; +import static org.wildfly.security.auth.client.XMLParserUtils.andThenOp; +import static org.wildfly.security.auth.client._private.ElytronMessages.xmlLog; +import static org.wildfly.security.provider.util.ProviderUtil.INSTALLED_PROVIDERS; + +import org.wildfly.security.credential.PasswordCredential; + +/** + * A parser for the Encrypted Expression XML schema. + * + * @author Prarthona Paul + */ + +public class EncryptedExpressionsXmlParser { + + private static final Supplier PROVIDER_SUPPLIER = ProviderFactory.getElytronProviderSupplier(EncryptedExpressionsXmlParser.class.getClassLoader()); + + private static final Supplier DEFAULT_PROVIDER_SUPPLIER = ProviderUtil.aggregate(PROVIDER_SUPPLIER, INSTALLED_PROVIDERS); + + static final Map KNOWN_NAMESPACES; + + private enum Version { + + VERSION_1_0("urn:encrypted:expression:1.0", null); + final String namespace; + final Version parent; + + Version(String nameSpace, Version parent) { + this.namespace = nameSpace; + this.parent = parent; + } + } + + static { + Map knownNamespaces = new HashMap<>(); + for (Version version : Version.values()) { + knownNamespaces.put(version.namespace, version); + } + KNOWN_NAMESPACES = Collections.unmodifiableMap(knownNamespaces); + } + + private EncryptedExpressionsXmlParser() { + } + + /** + * Parse an Encrypted Expression client configuration from a configuration discovered using the default wildfly-client-config discovery rules. + * + * @return the Encrypted expression context factory + * @throws ConfigXMLParseException if the resource failed to be parsed + */ + public static SecurityFactory parseEncryptedExpressionClientConfiguration() throws ConfigXMLParseException { + final ClientConfiguration clientConfiguration = ClientConfiguration.getInstance(); + if (clientConfiguration != null) try (final ConfigurationXMLStreamReader streamReader = clientConfiguration.readConfiguration(KNOWN_NAMESPACES.keySet())) { + if (streamReader != null) { + xmlLog.tracef("Parsing configuration from %s for namespace %s", streamReader.getUri(), streamReader.getNamespaceURI()); + return parseEncryptedExpressionClientConfiguration(streamReader); + } else { + if (xmlLog.isTraceEnabled()) { + xmlLog.tracef("No configuration found for known namespaces '%s'", namespacesToString()); + } + } + } + xmlLog.trace("Falling back to no encrypted expression configuration."); + return () -> EncryptedExpressionContext.empty(); + } + + /** + * Parse an encrypted expression client configuration from a resource located at a specified {@link URI}. + * + * @param uri the {@link URI} of the configuration. + * @return the encrypted expression context factory + * @throws ConfigXMLParseException if the resource failed to be parsed + */ + public static SecurityFactory parseEncryptedExpressionClientConfiguration(URI uri) throws ConfigXMLParseException { + final ClientConfiguration clientConfiguration = ClientConfiguration.getInstance(uri); + if (clientConfiguration != null) try (final ConfigurationXMLStreamReader streamReader = clientConfiguration.readConfiguration(KNOWN_NAMESPACES.keySet())) { + if (streamReader != null) { + xmlLog.tracef("Parsing configuration from %s for namespace %s", streamReader.getUri(), streamReader.getNamespaceURI()); + return parseEncryptedExpressionClientConfiguration(streamReader); + } else { + if (xmlLog.isTraceEnabled()) { + xmlLog.tracef("No configuration found for known namespaces '%s'", namespacesToString()); + } + } + } + xmlLog.trace("Falling back to no encrypted expression configuration."); + return () -> EncryptedExpressionContext.empty(); + } + + /** + * Parse an encrypted expression configuration from a configuration XML reader. + * + * @param reader the XML stream reader + * @return the Encrypted Expression context factory + * @throws ConfigXMLParseException if the resource failed to be parsed + */ + static SecurityFactory parseEncryptedExpressionClientConfiguration(ConfigurationXMLStreamReader reader) throws ConfigXMLParseException { + if (reader.hasNext()) { + switch (reader.nextTag()) { + case START_ELEMENT: { + EncryptedExpressionsXmlParser.Version xmlVersion = KNOWN_NAMESPACES.get(checkGetElementNamespace(reader)); + switch (reader.getLocalName()) { + case "encrypted-expression": { + return parseEncryptedExpressionType(reader, xmlVersion); + } + default: { + throw reader.unexpectedElement(); + } + } + } + default: { + throw reader.unexpectedContent(); + } + } + } + xmlLog.trace("No encrypted expression element found, all sensitive information will need to be specified as clear text."); + return EncryptedExpressionContext::empty; + } + + static SecurityFactory parseEncryptedExpressionType(ConfigurationXMLStreamReader reader, final EncryptedExpressionsXmlParser.Version xmlVersion) throws ConfigXMLParseException { + requireNoAttributes(reader); + Map> credentialStoresMap = new HashMap<>(); + final ElytronXmlParser.DeferredSupplier providersSupplier = new DeferredSupplier<>(DEFAULT_PROVIDER_SUPPLIER); + boolean netAuthenticator = false; + int foundBits = 0; + while (reader.hasNext()) { + final int tag = reader.nextTag(); + if (tag == START_ELEMENT) { + checkElementNamespace(reader, xmlVersion); + switch (reader.getLocalName()) { + case "credential-stores": { + if (isSet(foundBits, 0)) throw reader.unexpectedElement(); + foundBits = setBit(foundBits, 0); + parseCredentialStoresType(reader, xmlVersion, credentialStoresMap, providersSupplier); + break; + } + default: throw reader.unexpectedElement(); + } + } else if (tag == END_ELEMENT) { + assert reader.getLocalName().equals("encrypted-expression"); + if (netAuthenticator) { + Authenticator.setDefault(new ElytronAuthenticator()); + } + // validate key and credential stores... + for (ExceptionSupplier supplier : credentialStoresMap.values()) { + supplier.get(); + } + return () -> new EncryptedExpressionContext(); + } else { + throw reader.unexpectedContent(); + } + } + throw reader.unexpectedDocumentEnd(); + } + + private static void parseCredentialStoresType(ConfigurationXMLStreamReader reader, final EncryptedExpressionsXmlParser.Version xmlVersion, final Map> credentialStoresMap, final Supplier providers) throws ConfigXMLParseException { + final int attributeCount = reader.getAttributeCount(); + if (attributeCount > 0) { + throw reader.unexpectedAttribute(0); + } + while (reader.hasNext()) { + final int tag = reader.nextTag(); + if (tag == START_ELEMENT) { + checkElementNamespace(reader, xmlVersion); + switch (reader.getLocalName()) { + case "credential-store": { + parseCredentialStoreType(reader, xmlVersion, credentialStoresMap, providers); + break; + } + default: throw reader.unexpectedElement(); + } + } else if (tag == END_ELEMENT) { + return; + } else { + throw reader.unexpectedContent(); + } + } + throw reader.unexpectedDocumentEnd(); + } + + /** + * Parse an XML element of type {@code credential-store-type} from an XML reader. + * + * @param reader the XML stream reader + * @param xmlVersion the version of parsed XML + * @param credentialStoresMap the map of credential stores to fill @throws ConfigXMLParseException if the resource failed to be parsed + */ + private static void parseCredentialStoreType(ConfigurationXMLStreamReader reader, final EncryptedExpressionsXmlParser.Version xmlVersion, final Map> credentialStoresMap, final Supplier providers) throws ConfigXMLParseException { + final XMLLocation location = reader.getLocation(); + final int attributeCount = reader.getAttributeCount(); + String name = null; + String type = null; + String provider = null; + for (int i = 0; i < attributeCount; i ++) { + final String attributeNamespace = reader.getAttributeNamespace(i); + if (attributeNamespace != null && ! attributeNamespace.isEmpty()) { + throw reader.unexpectedAttribute(i); + } + switch (reader.getAttributeLocalName(i)) { + case "type": { + if (type != null) throw reader.unexpectedAttribute(i); + type = reader.getAttributeValueResolved(i); + break; + } + case "provider": { + if (provider != null) throw reader.unexpectedAttribute(i); + provider = reader.getAttributeValueResolved(i); + break; + } + case "name": { + if (name != null) throw reader.unexpectedAttribute(i); + name = reader.getAttributeValueResolved(i); + break; + } + default: + throw reader.unexpectedAttribute(i); + } + } + if (name == null) { + throw missingAttribute(reader, "name"); + } + + final Map attributesMap = new HashMap<>(); + int foundBits = 0; + ExceptionSupplier credentialSourceSupplier = null; + ElytronXmlParser.DeferredSupplier providersSupplier = new ElytronXmlParser.DeferredSupplier<>(providers); + while (reader.hasNext()) { + final int tag = reader.nextTag(); + if (tag == START_ELEMENT) { + checkElementNamespace(reader, xmlVersion); + switch (reader.getLocalName()) { + case "attributes": { + if (isSet(foundBits, 1)) throw reader.unexpectedElement(); + foundBits = setBit(foundBits, 1); + parseAttributesType(reader, xmlVersion, attributesMap); + break; + } + case "protection-parameter-credentials": { + if (isSet(foundBits, 2)) throw reader.unexpectedElement(); + foundBits = setBit(foundBits, 2); + credentialSourceSupplier = parseCredentialsType(reader, xmlVersion, credentialStoresMap, providersSupplier); + break; + } + case "providers": { + if (isSet(foundBits, 3)) throw reader.unexpectedElement(); + foundBits = setBit(foundBits, 3); + Supplier supplier = parseProvidersType(reader, xmlVersion); + if (supplier != null) { + providersSupplier.setSupplier(supplier); + } + break; + } + default: throw reader.unexpectedElement(); + } + } else if (tag == END_ELEMENT) { + if (!credentialStoresMap.containsKey(name)) { + ExceptionSupplier credentialStoreSecurityFactory = new CredentialStoreFactory(name, type, attributesMap, provider, location, credentialSourceSupplier, providersSupplier); + credentialStoresMap.put(name, credentialStoreSecurityFactory); + } else { + throw xmlLog.duplicateCredentialStoreName(reader, name); + } + return; + } else { + throw reader.unexpectedContent(); + } + } + throw reader.unexpectedDocumentEnd(); + } + + // common types + + /** + * Parse attributes {@code attributes-type} from an XML reader. + * + * @param reader the XML stream reader + * @param attributesMap the map to put attributes to. + * @throws ConfigXMLParseException if the resource failed to be parsed + */ + private static void parseAttributesType(ConfigurationXMLStreamReader reader, final EncryptedExpressionsXmlParser.Version xmlVersion, final Map attributesMap) throws ConfigXMLParseException { + final int attributeCount = reader.getAttributeCount(); + if (attributeCount > 0) { + throw reader.unexpectedAttribute(0); + } + while (reader.hasNext()) { + final int tag = reader.nextTag(); + if (tag == START_ELEMENT) { + checkElementNamespace(reader, xmlVersion); + switch (reader.getLocalName()) { + case "attribute": { + parseAttributeType(reader, attributesMap); + break; + } + default: throw reader.unexpectedElement(); + } + } else if (tag == END_ELEMENT) { + return; + } else { + throw reader.unexpectedContent(); + } + } + throw reader.unexpectedDocumentEnd(); + } + + /** + * Parse an attribute {@code attribute-type} from an XML reader. + * + * @param reader the XML stream reader + * @param attributesMap the map to put attributes to. + * @throws ConfigXMLParseException if the resource failed to be parsed + */ + private static void parseAttributeType(ConfigurationXMLStreamReader reader, final Map attributesMap) throws ConfigXMLParseException { + final int attributeCount = reader.getAttributeCount(); + String name = null; + String value = null; + for (int i = 0; i < attributeCount; i ++) { + final String attributeNamespace = reader.getAttributeNamespace(i); + if (attributeNamespace != null && ! attributeNamespace.isEmpty()) { + throw reader.unexpectedAttribute(i); + } + switch (reader.getAttributeLocalName(i)) { + case "name": { + if (name != null) throw reader.unexpectedAttribute(i); + name = reader.getAttributeValueResolved(i); + break; + } + case "value": { + if (value != null) throw reader.unexpectedAttribute(i); + value = reader.getAttributeValueResolved(i); + break; + } + default: + throw reader.unexpectedAttribute(i); + } + } + if (reader.hasNext()) { + final int tag = reader.nextTag(); + if (tag == START_ELEMENT) { + throw reader.unexpectedContent(); + } else if (tag == END_ELEMENT) { + if (!attributesMap.containsKey(name)) { + attributesMap.put(name, value); + } else { + throw xmlLog.duplicateAttributeFound(reader, name); + } + return; + } + throw reader.unexpectedContent(); + } + throw reader.unexpectedContent(); + } + + /** + * Parse an XML element of type {@code empty-type} from an XML reader. + * + * @param reader the XML stream reader + * @throws ConfigXMLParseException if the resource failed to be parsed + */ + static void parseEmptyType(ConfigurationXMLStreamReader reader) throws ConfigXMLParseException { + requireNoAttributes(reader); + if (reader.hasNext()) { + final int tag = reader.nextTag(); + if (tag == START_ELEMENT) { + throw reader.unexpectedElement(); + } else if (tag == END_ELEMENT) { + return; + } else { + throw reader.unexpectedContent(); + } + } + throw reader.unexpectedDocumentEnd(); + } + + /** + * Parse an XML element of type {@code name-type} from an XML reader. + * + * @param reader the XML stream reader + * @return the parsed name + * @throws ConfigXMLParseException if the resource failed to be parsed + */ + static String parseNameType(ConfigurationXMLStreamReader reader) throws ConfigXMLParseException { + return parseNameType(reader, false); + } + + /** + * Parse an XML element of type {@code name-type} from an XML reader. + * + * @param reader the XML stream reader + * @param optional is the name attribute optional? + * @return the parsed name + * @throws ConfigXMLParseException if the resource failed to be parsed + */ + static String parseNameType(ConfigurationXMLStreamReader reader, boolean optional) throws ConfigXMLParseException { + final int attributeCount = reader.getAttributeCount(); + String name = null; + for (int i = 0; i < attributeCount; i ++) { + checkAttributeNamespace(reader, i); + if (reader.getAttributeLocalName(i).equals("name")) { + name = reader.getAttributeValueResolved(i); + } else { + throw reader.unexpectedAttribute(i); + } + } + if (name == null && !optional) { + throw missingAttribute(reader, "name"); + } + if (reader.hasNext()) { + final int tag = reader.nextTag(); + if (tag == START_ELEMENT) { + throw reader.unexpectedElement(); + } else if (tag == END_ELEMENT) { + return name; + } else { + throw reader.unexpectedContent(); + } + } + throw reader.unexpectedDocumentEnd(); + } + + private static ExceptionSupplier parseCredentialsType(final ConfigurationXMLStreamReader reader, final EncryptedExpressionsXmlParser.Version xmlVersion, final Map> credentialStoresMap, Supplier providers) throws ConfigXMLParseException { + ExceptionUnaryOperator function = parent -> CredentialSource.NONE; + requireNoAttributes(reader); + while (reader.hasNext()) { + final int tag = reader.nextTag(); + if (tag == START_ELEMENT) { + checkElementNamespace(reader, xmlVersion); + switch (reader.getLocalName()) { + case "clear-password": { + ExceptionSupplier password = parseClearPassword(reader, providers); + function = andThenOp(function, credentialSource -> credentialSource.with(IdentityCredentials.NONE.withCredential(new PasswordCredential(password.get())))); + break; + } + default: { + throw reader.unexpectedElement(); + } + } + } else if (tag == END_ELEMENT) { + assert reader.getLocalName().equals("credentials") || reader.getLocalName().equals("protection-parameter-credentials"); + final ExceptionUnaryOperator finalFunction = function; + return () -> finalFunction.apply(null); + } else { + throw reader.unexpectedContent(); + } + } + throw reader.unexpectedDocumentEnd(); + } + + static Supplier parseProvidersType(ConfigurationXMLStreamReader reader, final EncryptedExpressionsXmlParser.Version xmlVersion) throws ConfigXMLParseException { + requireNoAttributes(reader); + + Supplier providerSupplier = null; + + int foundBits = 0; + while (reader.hasNext()) { + final int tag = reader.nextTag(); + if (tag == START_ELEMENT) { + checkElementNamespace(reader, xmlVersion); + switch (reader.getLocalName()) { + case "global": { + if (isSet(foundBits, 1)) throw reader.unexpectedElement(); + foundBits = setBit(foundBits, 1); + parseEmptyType(reader); + providerSupplier = providerSupplier == null ? INSTALLED_PROVIDERS : ProviderUtil.aggregate(providerSupplier, INSTALLED_PROVIDERS); + break; + } + case "use-service-loader": { + if (isSet(foundBits, 2)) throw reader.unexpectedElement(); + foundBits = setBit(foundBits, 2); + final String moduleName = parseModuleRefType(reader); + Supplier serviceLoaderSupplier = (moduleName == null) ? + PROVIDER_SUPPLIER : + new ProviderServiceLoaderSupplier(ModuleLoader.getClassLoaderFromModule(reader, moduleName)); + providerSupplier = providerSupplier == null ? serviceLoaderSupplier : ProviderUtil.aggregate(providerSupplier, serviceLoaderSupplier); + break; + } + default: throw reader.unexpectedElement(); + } + } else if (tag == END_ELEMENT) { + return providerSupplier; + } else { + throw reader.unexpectedContent(); + } + } + throw reader.unexpectedDocumentEnd(); + } + + /** + * Parse an XML element of type {@code clear-password-type} from an XML reader. + * + * @param reader the XML stream reader + * @return the clear password characters + * @throws ConfigXMLParseException if the resource failed to be parsed or the module is not found + */ + static ExceptionSupplier parseClearPassword(ConfigurationXMLStreamReader reader, Supplier providers) throws ConfigXMLParseException { + final int attributeCount = reader.getAttributeCount(); + char[] password = null; + for (int i = 0; i < attributeCount; i ++) { + checkAttributeNamespace(reader, i); + if (reader.getAttributeLocalName(i).equals("password")) { + password = reader.getAttributeValueResolved(i).toCharArray(); + } else { + throw reader.unexpectedAttribute(i); + } + } + if (password == null) { + throw missingAttribute(reader, "password"); + } + if (reader.hasNext()) { + final int tag = reader.nextTag(); + if (tag == START_ELEMENT) { + throw reader.unexpectedElement(); + } else if (tag == END_ELEMENT) { + final XMLLocation location = reader.getLocation(); + final char[] finalPassword = password; + return () -> { + try { + PasswordFactory factory = PasswordFactory.getInstance(ClearPassword.ALGORITHM_CLEAR, providers); + return Assert.assertNotNull(factory.generatePassword(new ClearPasswordSpec(finalPassword)).castAs(ClearPassword.class)); + } catch (InvalidKeySpecException | NoSuchAlgorithmException cause) { + throw xmlLog.xmlFailedToCreateCredential(location, cause); + } + }; + } else { + throw reader.unexpectedContent(); + } + } + throw reader.unexpectedDocumentEnd(); + } + + private static void checkElementNamespace(final ConfigurationXMLStreamReader reader, final EncryptedExpressionsXmlParser.Version xmlVersion) throws ConfigXMLParseException { + if (! xmlVersion.namespace.equals(reader.getNamespaceURI())) { + throw reader.unexpectedElement(); + } + } + + private static String checkGetElementNamespace(final ConfigurationXMLStreamReader reader) throws ConfigXMLParseException { + String namespaceUri = reader.getNamespaceURI(); + if (! KNOWN_NAMESPACES.containsKey(namespaceUri)) { + throw reader.unexpectedElement(); + } + return namespaceUri; + } + + private static String namespacesToString() { + Iterator namespaceIterator = KNOWN_NAMESPACES.keySet().iterator(); + StringBuilder namespaces = new StringBuilder(namespaceIterator.next()); + while (namespaceIterator.hasNext()) { + namespaces.append(",").append(namespaceIterator.next()); + } + + return namespaces.toString(); + } +} diff --git a/auth/client/src/main/java/org/wildfly/security/auth/client/XMLParserUtils.java b/auth/client/src/main/java/org/wildfly/security/auth/client/XMLParserUtils.java new file mode 100644 index 00000000000..f09ac14383c --- /dev/null +++ b/auth/client/src/main/java/org/wildfly/security/auth/client/XMLParserUtils.java @@ -0,0 +1,94 @@ +/* + * JBoss, Home of Professional Open Source. + * Copyright 2023 Red Hat, Inc., and individual contributors + * as indicated by the @author tags. + * + * Licensed 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 org.wildfly.security.auth.client; + +import org.wildfly.client.config.ConfigXMLParseException; +import org.wildfly.client.config.ConfigurationXMLStreamReader; +import org.wildfly.common.function.ExceptionSupplier; +import org.wildfly.common.function.ExceptionUnaryOperator; + +import java.net.URI; + +import static org.wildfly.security.auth.client._private.ElytronMessages.xmlLog; + +/** + * An interface to get and check information about attributes in XML file. + * + * @author Prarthona Paul + */ + +public class XMLParserUtils { + + public static boolean isSet(int var, int bit) { + return (var & 1 << bit) != 0; + } + + public static int setBit(int var, int bit) { + return var | 1 << bit; + } + public static void checkAttributeNamespace(final ConfigurationXMLStreamReader reader, final int idx) throws ConfigXMLParseException { + final String attributeNamespace = reader.getAttributeNamespace(idx); + if (attributeNamespace != null && ! attributeNamespace.isEmpty()) { + throw reader.unexpectedAttribute(idx); + } + } + + public static void requireNoAttributes(final ConfigurationXMLStreamReader reader) throws ConfigXMLParseException { + final int attributeCount = reader.getAttributeCount(); + if (attributeCount > 0) { + throw reader.unexpectedAttribute(0); + } + } + + public static String requireSingleAttribute(final ConfigurationXMLStreamReader reader, final String attributeName) throws ConfigXMLParseException { + return requireSingleAttribute(reader, attributeName, (ExceptionSupplier) () -> reader.getAttributeValueResolved(0)); + } + + public static URI requireSingleURIAttribute(final ConfigurationXMLStreamReader reader, final String attributeName) throws ConfigXMLParseException { + return requireSingleAttribute(reader, attributeName, () -> reader.getURIAttributeValueResolved(0)); + } + + public static A requireSingleAttribute(final ConfigurationXMLStreamReader reader, final String attributeName, ExceptionSupplier attributeFunction) throws ConfigXMLParseException { + final int attributeCount = reader.getAttributeCount(); + if (attributeCount < 1) { + throw reader.missingRequiredAttribute("", attributeName); + } + checkAttributeNamespace(reader, 0); + if (! reader.getAttributeLocalName(0).equals(attributeName)) { + throw reader.unexpectedAttribute(0); + } + if (attributeCount > 1) { + throw reader.unexpectedAttribute(1); + } + return attributeFunction.get(); + } + + public static ConfigXMLParseException missingAttribute(final ConfigurationXMLStreamReader reader, final String name) { + return reader.missingRequiredAttribute(null, name); + } + + public static ConfigXMLParseException invalidPortNumber(final ConfigurationXMLStreamReader reader, final int index) throws ConfigXMLParseException { + return xmlLog.xmlInvalidPortNumber(reader, reader.getAttributeValueResolved(index), reader.getAttributeLocalName(index), reader.getName()); + } + + + public static ExceptionUnaryOperator andThenOp(ExceptionUnaryOperator first, ExceptionUnaryOperator second) { + return t -> second.apply(first.apply(t)); + } +} diff --git a/auth/client/src/main/resources/schema/encrypted-expression-1_0.xsd b/auth/client/src/main/resources/schema/encrypted-expression-1_0.xsd new file mode 100644 index 00000000000..265bf5604de --- /dev/null +++ b/auth/client/src/main/resources/schema/encrypted-expression-1_0.xsd @@ -0,0 +1,252 @@ + + + + + + + + + + + + Configuration for encrypted expressions and resolvers to be used across different client configurations. + + + + + + + + + + + + + + Complex type to contain the definitions of all the credential stores used to back the resolvers configured. + + + + + + + + + + + An individual credential store definition used to back one or more resolver(s). + + + + + + + + + + + + + The name of the credential store. + + + + + + + The credential store type, e.g. KeyStoreCredentialStore. + + + + + + + The name of the provider to use to instantiate the CredentialStoreSpi. + If the provider is not specified then the first provider found that can create an instance of the specified 'type' will be used. + + + + + + + + + Complex type to contain the definitions of the attributes. + + + + + + + + + + + An individual attribute definition. + + + + + + Name used for referencing the attribute. + + + + + + + The value of the attribute. + + + + + + + + + One or more credentials to be assembled into a protection parameter when initialising the credential store. + + + + + + + + + + + A clear password definition. + + + + + + A password specified in the clear. + + + + + + + + + Define how java.security.Provider instances are located when required. + + + + + + + The providers from java.security.Security.getProviders() + + + + + + + Providers loaded using service loader discovery from the module specified, + if no module is specified the ClassLoader which loaded the authentication client is used. + + + + + + + + + + + + Module reference. + + + + + + Module name. + + + + + + + + + + List of all the resolvers used in encrypted expressions. + + + + + + Individual resolvers listed in the configuration. They are used to handle the encrypted expressions. + + + + + + + + + Individual resolver used to handle encrypted expressions. + + + + + + Name of the resolver. + + + + + + + The keystore used to back this resolver. + + + + + + + Alias for the key used to handle the encrypted expression. + + + + + + + + + The default resolver to be used if the resolver for an encrypted expression is not defined. + + + + + + Name of the default resolver to be used if the resolver for an encrypted expression is not defined. + + + + + \ No newline at end of file diff --git a/auth/client/src/test/java/org/wildfly/security/auth/client/EncryptedExpressionXMLParserTest.java b/auth/client/src/test/java/org/wildfly/security/auth/client/EncryptedExpressionXMLParserTest.java new file mode 100644 index 00000000000..1928cfb4d23 --- /dev/null +++ b/auth/client/src/test/java/org/wildfly/security/auth/client/EncryptedExpressionXMLParserTest.java @@ -0,0 +1,113 @@ +package org.wildfly.security.auth.client; + +import org.junit.AfterClass; +import org.junit.Assert; +import org.junit.BeforeClass; +import org.junit.Test; +import org.wildfly.security.SecurityFactory; +import org.wildfly.security.x500.cert.SelfSignedX509CertificateAndSigningKey; +import org.wildfly.security.x500.cert.X509CertificateBuilder; + +import javax.security.auth.x500.X500Principal; +import java.io.File; +import java.io.FileOutputStream; +import java.net.URL; +import java.security.KeyPair; +import java.security.PublicKey; +import java.security.PrivateKey; +import java.security.KeyPairGenerator; +import java.security.KeyStore; +import java.security.cert.X509Certificate; + +public class EncryptedExpressionXMLParserTest { + + private static final File CREDSTORE_DIR = new File("./target/credstore"); + private static final String CLIENT_CREDSTORE_FILENAME = "/mycredstore.cs"; + private static final char[] PASSWORD = "password".toCharArray(); + + @Test + public void testEncryptedExpressionConfig() throws Exception { + URL config = getClass().getResource("test-encrypted-expression-v1_0.xml"); + SecurityFactory clientConfiguration = EncryptedExpressionsXmlParser.parseEncryptedExpressionClientConfiguration(config.toURI()); + Assert.assertNotNull(clientConfiguration); + } + + @BeforeClass + public static void prepareCredStores() throws Exception { + if (!CREDSTORE_DIR.exists()) { + CREDSTORE_DIR.mkdirs(); + } + + KeyStore credentialStore = KeyStore.getInstance("JCEKS"); + credentialStore.load(null, null); + + createCredentialStore(credentialStore); + + File clientFile = new File(CREDSTORE_DIR, CLIENT_CREDSTORE_FILENAME); + + try (FileOutputStream clientStream = new FileOutputStream(clientFile)) { + credentialStore.store(clientStream, PASSWORD); + } + } + + + @AfterClass + public static void removeProvider() { + Assert.assertTrue("Credential Store deleted", new File(CREDSTORE_DIR, CLIENT_CREDSTORE_FILENAME).delete()); + Assert.assertTrue("Credential store directory deleted", CREDSTORE_DIR.delete()); + } + + private static void createCredentialStore(KeyStore credentialStore) throws Exception { + // Generate testclient2.example.com self signed certificate + X500Principal testClient2DN = new X500Principal("CN=testclient2.example.com, OU=JBoss, O=Red Hat, L=Raleigh, ST=North Carolina, C=US"); + SelfSignedX509CertificateAndSigningKey testClient2SelfSignedX509CertificateAndSigningKey = SelfSignedX509CertificateAndSigningKey.builder() + .setKeyAlgorithmName("DSA") + .setSignatureAlgorithmName("SHA1withDSA") + .setDn(testClient2DN) + .setKeySize(1024) + .build(); + X509Certificate testClient2Certificate = testClient2SelfSignedX509CertificateAndSigningKey.getSelfSignedCertificate(); + credentialStore.setKeyEntry("dnsincnclient", testClient2SelfSignedX509CertificateAndSigningKey.getSigningKey(), PASSWORD, new X509Certificate[]{testClient2Certificate}); + + + // Generate Test Authority self signed certificate + X500Principal testAuthorityDN = new X500Principal("CN=Test Authority, OU=JBoss, O=Red Hat, L=Raleigh, ST=North Carolina, C=US"); + SelfSignedX509CertificateAndSigningKey testAuthoritySelfSignedX509CertificateAndSigningKey = SelfSignedX509CertificateAndSigningKey.builder() + .setDn(testAuthorityDN) + .setKeyAlgorithmName("RSA") + .setSignatureAlgorithmName("SHA1withRSA") + .build(); + X509Certificate testAuthorityCertificate = testAuthoritySelfSignedX509CertificateAndSigningKey.getSelfSignedCertificate(); + credentialStore.setKeyEntry("testauthority", testAuthoritySelfSignedX509CertificateAndSigningKey.getSigningKey(), PASSWORD, new X509Certificate[]{testAuthorityCertificate}); + + + // Generate Test Client 1 self signed certificate + X500Principal testClient1DN = new X500Principal("CN=Test Client 1, OU=JBoss, O=Red Hat, L=Raleigh, ST=North Carolina, C=US"); + SelfSignedX509CertificateAndSigningKey testClient1SelfSignedX509CertificateAndSigningKey = SelfSignedX509CertificateAndSigningKey.builder() + .setDn(testClient1DN) + .setKeyAlgorithmName("RSA") + .setSignatureAlgorithmName("SHA1withRSA") + .addExtension(false, "SubjectAlternativeName", "DNS:testclient1.example.com") + .build(); + X509Certificate testClient1Certificate = testClient1SelfSignedX509CertificateAndSigningKey.getSelfSignedCertificate(); + credentialStore.setKeyEntry("testclient1", testClient1SelfSignedX509CertificateAndSigningKey.getSigningKey(), PASSWORD, new X509Certificate[]{testClient1Certificate}); + + + // Generate Signed Test Client certificate signed by Test Authority + X500Principal signedTestClientDN = new X500Principal("CN=Signed Test Client, OU=JBoss, O=Red Hat, ST=North Carolina, C=US"); + + KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance("RSA"); + KeyPair signedTestClientGeneratedKeys = keyPairGenerator.generateKeyPair(); + PrivateKey signedTestClientSigningKey = signedTestClientGeneratedKeys.getPrivate(); + PublicKey signedTestClientPublicKey = signedTestClientGeneratedKeys.getPublic(); + + X509Certificate signedTestClientCertificate = new X509CertificateBuilder() + .setIssuerDn(testAuthorityDN) + .setSubjectDn(signedTestClientDN) + .setSignatureAlgorithmName("SHA1withRSA") + .setSigningKey(testAuthoritySelfSignedX509CertificateAndSigningKey.getSigningKey()) + .setPublicKey(signedTestClientPublicKey) + .build(); + credentialStore.setKeyEntry("testclientsignedbyca", signedTestClientSigningKey, PASSWORD, new X509Certificate[]{signedTestClientCertificate, testAuthorityCertificate}); + } +} \ No newline at end of file diff --git a/auth/client/src/test/resources/mycredstore.cs b/auth/client/src/test/resources/mycredstore.cs new file mode 100644 index 00000000000..fad3935f06f Binary files /dev/null and b/auth/client/src/test/resources/mycredstore.cs differ diff --git a/auth/client/src/test/resources/org/wildfly/security/auth/client/test-encrypted-expression-v1_0.xml b/auth/client/src/test/resources/org/wildfly/security/auth/client/test-encrypted-expression-v1_0.xml new file mode 100644 index 00000000000..5da12cec680 --- /dev/null +++ b/auth/client/src/test/resources/org/wildfly/security/auth/client/test-encrypted-expression-v1_0.xml @@ -0,0 +1,33 @@ + + + + + + + + + + + + + + + + +