All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.wildfly.security.auth.client.ElytronXmlParser Maven / Gradle / Ivy

The newest version!
/*
 * JBoss, Home of Professional Open Source.
 * Copyright 2014 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 static javax.xml.stream.XMLStreamConstants.END_ELEMENT;
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._private.ElytronMessages.xmlLog;
import static org.wildfly.security.provider.util.ProviderUtil.INSTALLED_PROVIDERS;
import static org.wildfly.security.provider.util.ProviderUtil.findProvider;

import java.io.Closeable;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.net.Authenticator;
import java.net.MalformedURLException;
import java.net.URI;
import java.nio.charset.StandardCharsets;
import java.security.GeneralSecurityException;
import java.security.KeyPair;
import java.security.KeyStore;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
import java.security.PrivateKey;
import java.security.Provider;
import java.security.PublicKey;
import java.security.cert.X509Certificate;
import java.security.spec.InvalidKeySpecException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.ListIterator;
import java.util.Map;
import java.util.ServiceConfigurationError;
import java.util.ServiceLoader;
import java.util.function.IntFunction;
import java.util.function.Supplier;
import java.util.regex.Pattern;

import javax.crypto.SecretKey;
import javax.crypto.SecretKeyFactory;
import javax.crypto.spec.SecretKeySpec;
import javax.net.ssl.KeyManager;
import javax.net.ssl.KeyManagerFactory;
import javax.net.ssl.SSLContext;
import javax.net.ssl.TrustManager;
import javax.net.ssl.TrustManagerFactory;
import javax.net.ssl.X509ExtendedKeyManager;
import javax.net.ssl.X509TrustManager;
import javax.xml.stream.Location;

import org.ietf.jgss.GSSException;
import org.ietf.jgss.Oid;
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.ExceptionBiFunction;
import org.wildfly.common.function.ExceptionSupplier;
import org.wildfly.common.function.ExceptionUnaryOperator;
import org.wildfly.common.iteration.CodePointIterator;
import org.wildfly.security.FixedSecurityFactory;
import org.wildfly.security.SecurityFactory;
import org.wildfly.security.asn1.OidsUtil;
import org.wildfly.security.auth.client._private.ElytronMessages;
import org.wildfly.security.auth.server.IdentityCredentials;
import org.wildfly.security.auth.server.NameRewriter;
import org.wildfly.security.auth.util.ElytronAuthenticator;
import org.wildfly.security.auth.util.ElytronFilePasswordProvider;
import org.wildfly.security.auth.util.RegexNameRewriter;
import org.wildfly.security.credential.BearerTokenCredential;
import org.wildfly.security.credential.Credential;
import org.wildfly.security.credential.KeyPairCredential;
import org.wildfly.security.credential.PasswordCredential;
import org.wildfly.security.credential.PublicKeyCredential;
import org.wildfly.security.credential.SSHCredential;
import org.wildfly.security.credential.X509CertificateChainPrivateCredential;
import org.wildfly.security.credential.source.CredentialSource;
import org.wildfly.security.credential.source.impl.CredentialStoreCredentialSource;
import org.wildfly.security.credential.source.impl.KeyStoreCredentialSource;
import org.wildfly.security.credential.source.impl.LocalKerberosCredentialSource;
import org.wildfly.security.credential.source.OAuth2CredentialSource;
import org.wildfly.security.credential.store.CredentialStore;
import org.wildfly.security.keystore.AliasFilter;
import org.wildfly.security.keystore.FilteringKeyStore;
import org.wildfly.security.keystore.KeyStoreUtil;
import org.wildfly.security.keystore.PasswordEntry;
import org.wildfly.security.keystore.WrappingPasswordKeyStore;
import org.wildfly.security.mechanism.gssapi.GSSCredentialSecurityFactory;
import org.wildfly.security.password.Password;
import org.wildfly.security.password.PasswordFactory;
import org.wildfly.security.password.WildFlyElytronPasswordProvider;
import org.wildfly.security.password.interfaces.ClearPassword;
import org.wildfly.security.password.interfaces.MaskedPassword;
import org.wildfly.security.password.spec.ClearPasswordSpec;
import org.wildfly.security.password.spec.MaskedPasswordSpec;
import org.wildfly.security.pem.Pem;
import org.wildfly.security.pem.PemEntry;
import org.wildfly.security.provider.util.ProviderFactory;
import org.wildfly.security.provider.util.ProviderServiceLoaderSupplier;
import org.wildfly.security.provider.util.ProviderUtil;
import org.wildfly.security.sasl.SaslMechanismSelector;
import org.wildfly.security.sasl.util.ServiceLoaderSaslClientFactory;
import org.wildfly.security.ssl.CipherSuiteSelector;
import org.wildfly.security.ssl.ProtocolSelector;
import org.wildfly.security.ssl.SSLContextBuilder;
import org.wildfly.security.ssl.X509RevocationTrustManager;
import org.wildfly.security.ssh.util.SshUtil;

/**
 * A parser for the Elytron XML schema.
 *
 * @author David M. Lloyd
 */
public final class ElytronXmlParser {

    private static final Supplier ELYTRON_PROVIDER_SUPPLIER = ProviderFactory.getElytronProviderSupplier(ElytronXmlParser.class.getClassLoader());

    private static final Supplier DEFAULT_PROVIDER_SUPPLIER = ProviderUtil.aggregate(ELYTRON_PROVIDER_SUPPLIER, INSTALLED_PROVIDERS);

    static final Map KNOWN_NAMESPACES;

    private enum Version {

        VERSION_1_0("urn:elytron:1.0", null),
        VERSION_1_0_1("urn:elytron:1.0.1", VERSION_1_0),
        VERSION_1_1("urn:elytron:client:1.1", VERSION_1_0_1),
        VERSION_1_2("urn:elytron:client:1.2", VERSION_1_1),
        VERSION_1_3("urn:elytron:client:1.3", VERSION_1_2),
        VERSION_1_4("urn:elytron:client:1.4", VERSION_1_3),
        VERSION_1_5("urn:elytron:client:1.5", VERSION_1_4),
        VERSION_1_6("urn:elytron:client:1.6", VERSION_1_5),
        VERSION_1_7("urn:elytron:client:1.7", VERSION_1_6);

        final String namespace;

        /*
         * In the future we could support multiple parents but wait until that becomes a reality before adding it.
         */
        final Version parent;

        Version(String namespace, Version parent) {
            this.namespace = namespace;
            this.parent = parent;
        }


        boolean isAtLeast(Version version) {
            return this.equals(version) || (parent != null ? parent.isAtLeast(version) : false);
        }

    }

    static {
        Map knownNamespaces = new HashMap<>();
        for (Version version : Version.values()) {
            knownNamespaces.put(version.namespace, version);
        }
        KNOWN_NAMESPACES = Collections.unmodifiableMap(knownNamespaces);
    }

    private ElytronXmlParser() {
    }

    // authentication client document

    /**
     * Parse an Elytron authentication client configuration from a configuration discovered using the default wildfly-client-config discovery rules.
     *
     * @return the authentication context factory
     * @throws ConfigXMLParseException if the resource failed to be parsed
     */
    public static SecurityFactory parseAuthenticationClientConfiguration() 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 parseAuthenticationClientConfiguration(streamReader);
            } else {
                if (xmlLog.isTraceEnabled()) {
                    xmlLog.tracef("No configuration found for known namespaces '%s'", namespacesToString());
                }
            }
        }
        xmlLog.trace("Fallback to parse legacy configuration.");
        // Try legacy configuration next
        return parseLegacyConfiguration();
    }

    /**
     * Parse an Elytron authentication client configuration from a resource located at a specified {@link URI}.
     *
     * @param uri the {@link URI} of the configuration.
     * @return the authentication context factory
     * @throws ConfigXMLParseException if the resource failed to be parsed
     */
    public static SecurityFactory parseAuthenticationClientConfiguration(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("Parsig configuration from %s for namespace %s", streamReader.getUri(), streamReader.getNamespaceURI());
                return parseAuthenticationClientConfiguration(streamReader);
            } else {
                if (xmlLog.isTraceEnabled()) {
                    xmlLog.tracef("No configuration found for known namespaces '%s'", namespacesToString());
                }
            }
        }
        xmlLog.trace("Fallback to parse legacy configuration.");
        // Try legacy configuration next
        return parseLegacyConfiguration();
    }

    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();
    }

    /**
     * Parse a Elytron authentication client configuration from a configuration XML reader.
     *
     * @param reader the XML stream reader
     * @return the authentication context factory
     * @throws ConfigXMLParseException if the resource failed to be parsed
     */
    static SecurityFactory parseAuthenticationClientConfiguration(ConfigurationXMLStreamReader reader) throws ConfigXMLParseException {
        if (reader.hasNext()) {
            switch (reader.nextTag()) {
                case START_ELEMENT: {
                    Version xmlVersion = KNOWN_NAMESPACES.get(checkGetElementNamespace(reader));
                    switch (reader.getLocalName()) {
                        case "authentication-client": {
                            return parseAuthenticationClientType(reader, xmlVersion);
                        }
                        default: {
                            throw reader.unexpectedElement();
                        }
                    }
                }
                default: {
                    throw reader.unexpectedContent();
                }
            }
        }
        xmlLog.trace("No authentication-client element found, falling back to empty AuthenticationContext");
        return AuthenticationContext::empty;
    }

    private static SecurityFactory parseLegacyConfiguration() {
        final ServiceLoader loader = ServiceLoader.load(LegacyConfiguration.class, ElytronXmlParser.class.getClassLoader());
        final Iterator iterator = loader.iterator();
        final List configs = new ArrayList<>();
        for (;;) try {
            if (! iterator.hasNext()) break;
            configs.add(iterator.next());
        } catch (ServiceConfigurationError ignored) {}
        return () -> {
            for (LegacyConfiguration config : configs) {
                final AuthenticationContext context = config.getConfiguredAuthenticationContext();
                if (context != null) {
                    xmlLog.trace("Found AuthenticationContext in legacy configuration");
                    return context;
                }
            }
            xmlLog.trace("No legacy configuration available, using AuthenticationContext.empty()");
            return AuthenticationContext.empty();
        };
    }

    // authentication client types

    /**
     * Parse an XML element of type {@code authentication-client-type} from an XML reader.
     *
     * @param reader the XML stream reader
     * @param xmlVersion the version of parsed XML
     * @return the authentication context factory
     * @throws ConfigXMLParseException if the resource failed to be parsed
     */
    static SecurityFactory parseAuthenticationClientType(ConfigurationXMLStreamReader reader, final Version xmlVersion) throws ConfigXMLParseException {
        requireNoAttributes(reader);
        ExceptionSupplier, ConfigXMLParseException> authFactory = () -> null;
        ExceptionSupplier>, ConfigXMLParseException> sslFactory = () -> null;
        Map> keyStoresMap = new HashMap<>();
        Map> credentialStoresMap = new HashMap<>();
        Map, ConfigXMLParseException>> sslContextsMap = new HashMap<>();
        Map> authenticationConfigurationsMap = new HashMap<>();
        final 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 "authentication-rules": {
                        if (isSet(foundBits, 0)) throw reader.unexpectedElement();
                        foundBits = setBit(foundBits, 0);
                        authFactory = parseRulesType(reader, xmlVersion, authenticationConfigurationsMap, (r, m) -> parseAuthenticationRuleType(r, xmlVersion, m));
                        break;
                    }
                    case "ssl-context-rules": {
                        if (isSet(foundBits, 1)) throw reader.unexpectedElement();
                        foundBits = setBit(foundBits, 1);
                        sslFactory = parseRulesType(reader, xmlVersion, sslContextsMap, (r,m) -> parseSslContextRuleType(r, xmlVersion, m));
                        break;
                    }
                    case "authentication-configurations": {
                        if (isSet(foundBits, 2)) throw reader.unexpectedElement();
                        foundBits = setBit(foundBits, 2);
                        parseAuthenticationConfigurationsType(reader, xmlVersion, authenticationConfigurationsMap, keyStoresMap, credentialStoresMap, providersSupplier);
                        break;
                    }
                    case "ssl-contexts": {
                        if (isSet(foundBits, 3)) throw reader.unexpectedElement();
                        foundBits = setBit(foundBits, 3);
                        parseSslContextsType(reader, xmlVersion, sslContextsMap, keyStoresMap, credentialStoresMap, providersSupplier);
                        break;
                    }
                    case "key-stores": {
                        if (isSet(foundBits, 4)) throw reader.unexpectedElement();
                        foundBits = setBit(foundBits, 4);
                        parseKeyStoresType(reader, xmlVersion, keyStoresMap, credentialStoresMap, providersSupplier);
                        break;
                    }
                    case "net-authenticator": {
                        if (isSet(foundBits, 5)) throw reader.unexpectedElement();
                        foundBits = setBit(foundBits, 5);
                        netAuthenticator = true;
                        parseEmptyType(reader);
                        break;
                    }
                    case "credential-stores": {
                        if (isSet(foundBits, 6)) throw reader.unexpectedElement();
                        foundBits = setBit(foundBits, 6);
                        parseCredentialStoresType(reader, xmlVersion, keyStoresMap, credentialStoresMap, providersSupplier);
                        break;
                    }
                    case "providers": {
                        if (isSet(foundBits, 7)) throw reader.unexpectedElement();
                        foundBits = setBit(foundBits, 7);
                        Supplier supplier = parseProvidersType(reader, xmlVersion);
                        if (supplier != null) {
                            providersSupplier.setSupplier(supplier);
                        }
                        break;
                    }
                    default: throw reader.unexpectedElement();
                }
            } else if (tag == END_ELEMENT) {
                assert reader.getLocalName().equals("authentication-client");
                if (netAuthenticator) {
                    Authenticator.setDefault(new ElytronAuthenticator());
                }
                // validate key and credential stores...
                for (ExceptionSupplier supplier : keyStoresMap.values()) {
                    supplier.get();
                }
                for (ExceptionSupplier supplier : credentialStoresMap.values()) {
                    supplier.get();
                }
                final RuleNode authNode = authFactory.get();
                final RuleNode> sslNode = sslFactory.get();
                return () -> new AuthenticationContext(authNode, sslNode);
            } else {
                throw reader.unexpectedContent();
            }
        }
        throw reader.unexpectedDocumentEnd();
    }

    private static void parseAuthenticationConfigurationsType(final ConfigurationXMLStreamReader reader, final Version xmlVersion, final Map> authenticationConfigurationsMap, final Map> keyStoresMap, final Map> credentialStoresMap, final Supplier providers) throws ConfigXMLParseException {
        requireNoAttributes(reader);
        while (reader.hasNext()) {
            final int tag = reader.nextTag();
            if (tag == START_ELEMENT) {
                checkElementNamespace(reader, xmlVersion);
                switch (reader.getLocalName()) {
                    case "configuration": {
                        parseAuthenticationConfigurationType(reader, xmlVersion, authenticationConfigurationsMap, keyStoresMap, credentialStoresMap, providers);
                        break;
                    }
                    default: {
                        throw reader.unexpectedElement();
                    }
                }
            } else if (tag == END_ELEMENT) {
                return;
            } else {
                throw reader.unexpectedContent();
            }
        }
        throw reader.unexpectedDocumentEnd();
    }

    private static void parseSslContextsType(final ConfigurationXMLStreamReader reader, final Version xmlVersion, final Map, ConfigXMLParseException>> sslContextsMap, final Map> keyStoresMap, final Map> credentialStoresMap, final Supplier providers) throws ConfigXMLParseException {
        requireNoAttributes(reader);
        while (reader.hasNext()) {
            final int tag = reader.nextTag();
            if (tag == START_ELEMENT) {
                checkElementNamespace(reader, xmlVersion);
                switch (reader.getLocalName()) {
                    case "ssl-context": {
                        parseSslContextType(reader, xmlVersion, sslContextsMap, keyStoresMap, credentialStoresMap, providers);
                        break;
                    }
                    case "default-ssl-context": {
                        final String name = parseNameType(reader);
                        sslContextsMap.put(name, () -> SSLContext::getDefault);
                        break;
                    }
                    default: {
                        throw reader.unexpectedElement();
                    }
                }
            } else if (tag == END_ELEMENT) {
                return;
            } else {
                throw reader.unexpectedContent();
            }
        }
        throw reader.unexpectedDocumentEnd();
    }

    private static void parseSslContextType(final ConfigurationXMLStreamReader reader, final Version xmlVersion, final Map, ConfigXMLParseException>> sslContextsMap, final Map> keyStoresMap, final Map> credentialStoresMap, final Supplier providers) throws ConfigXMLParseException {
        final String name = requireSingleAttribute(reader, "name");
        if (sslContextsMap.containsKey(name)) {
            throw xmlLog.xmlDuplicateSslContextName(name, reader);
        }
        final XMLLocation location = reader.getLocation();
        int foundBits = 0;
        String providerName = null;
        CipherSuiteSelector cipherSuiteSelector = null;
        ProtocolSelector protocolSelector = null;
        ExceptionSupplier keyManagerSupplier = null;
        ExceptionSupplier trustStoreSupplier = null;
        DeferredSupplier providersSupplier = new DeferredSupplier<>(providers);
        TrustManagerBuilder trustManagerBuilder = new TrustManagerBuilder(providersSupplier, location);

        while (reader.hasNext()) {
            final int tag = reader.nextTag();
            if (tag == START_ELEMENT) {
                checkElementNamespace(reader, xmlVersion);
                switch (reader.getLocalName()) {
                    case "key-store-ssl-certificate": {
                        if (isSet(foundBits, 0)) throw reader.unexpectedElement();
                        foundBits = setBit(foundBits, 0);
                        keyManagerSupplier = parseKeyStoreSslCertificate(reader, xmlVersion, keyStoresMap, credentialStoresMap, providers);
                        break;
                    }
                    case "cipher-suite": {
                        if (isSet(foundBits, 1)) throw reader.unexpectedElement();
                        foundBits = setBit(foundBits, 1);
                        cipherSuiteSelector = parseCipherSuiteSelectorType(reader, xmlVersion);
                        break;
                    }
                    case "protocol": {
                        if (isSet(foundBits, 2)) throw reader.unexpectedElement();
                        foundBits = setBit(foundBits, 2);
                        protocolSelector = parseProtocolSelectorNamesType(reader);
                        break;
                    }
                    case "provider-name": {
                        if (isSet(foundBits, 3)) throw reader.unexpectedElement();
                        foundBits = setBit(foundBits, 3);
                        providerName = parseNameType(reader);
                        break;
                    }
                    case "providers": {
                        if (isSet(foundBits, 4)) throw reader.unexpectedElement();
                        foundBits = setBit(foundBits, 4);
                        Supplier supplier = parseProvidersType(reader, xmlVersion);
                        if (supplier != null) {
                            providersSupplier.setSupplier(supplier);
                        }
                        break;
                    }
                    case "trust-store": {
                        if (isSet(foundBits, 5)) throw reader.unexpectedElement();
                        foundBits = setBit(foundBits, 5);
                        trustStoreSupplier = parseTrustStoreRefType(reader, keyStoresMap);
                        break;
                    }
                    case "certificate-revocation-list": {
                        // rejects attribute if certificate-revocation-lists has been set
                        if (isSet(foundBits, 6) || isSet(foundBits, 9)) throw reader.unexpectedElement();
                        foundBits = setBit(foundBits, 6);
                        parseCertificateRevocationList(reader, trustManagerBuilder, xmlVersion, false);
                        break;
                    }
                    case "trust-manager": {
                        if (isSet(foundBits, 7) || !xmlVersion.isAtLeast(Version.VERSION_1_1)) throw reader.unexpectedElement();
                        foundBits = setBit(foundBits, 7);
                        parseTrustManager(reader, trustManagerBuilder, xmlVersion);
                        break;
                    }
                    case "ocsp": {
                        if (isSet(foundBits, 8) || !xmlVersion.isAtLeast(Version.VERSION_1_4)) throw reader.unexpectedElement();
                        foundBits = setBit(foundBits, 8);
                        parseOcsp(reader, trustManagerBuilder, keyStoresMap);
                        break;
                    }
                    case "certificate-revocation-lists" : {
                        // rejects attribute if certificate-revocation-list has been set
                        if (isSet(foundBits, 9) || isSet(foundBits, 6) || !xmlVersion.isAtLeast(Version.VERSION_1_7)) throw reader.unexpectedElement();
                        foundBits = setBit(foundBits, 9);
                        parseCertificateRevocationLists(reader, trustManagerBuilder, xmlVersion);
                        break;
                    }
                    default: throw reader.unexpectedElement();
                }
            } else if (tag != END_ELEMENT) {
                throw reader.unexpectedContent();
            } else {
                // ready to register!
                final Supplier finalProvidersSupplier = providersSupplier;
                final ProtocolSelector finalProtocolSelector = protocolSelector;
                final CipherSuiteSelector finalCipherSuiteSelector = cipherSuiteSelector;
                final String finalProviderName = providerName;
                final ExceptionSupplier finalKeyManagerSupplier = keyManagerSupplier;
                final ExceptionSupplier finalTrustStoreSupplier = trustStoreSupplier;
                final boolean initTrustManager = finalTrustStoreSupplier != null || isSet(foundBits, 7);
                sslContextsMap.putIfAbsent(name, () -> {
                    final SSLContextBuilder sslContextBuilder = new SSLContextBuilder();
                    sslContextBuilder.setClientMode(true);
                    if (finalCipherSuiteSelector != null) {
                        sslContextBuilder.setCipherSuiteSelector(finalCipherSuiteSelector);
                    }
                    if (finalProtocolSelector != null) {
                        sslContextBuilder.setProtocolSelector(finalProtocolSelector);
                    }
                    if (finalKeyManagerSupplier != null) {
                        sslContextBuilder.setKeyManager(finalKeyManagerSupplier.get());
                    }
                    if (initTrustManager) {
                        if (finalTrustStoreSupplier != null) {
                            trustManagerBuilder.setTrustStore(finalTrustStoreSupplier.get());
                        }
                        try {
                            sslContextBuilder.setTrustManager(trustManagerBuilder.build());
                        } catch (GeneralSecurityException e) {
                            throw new ConfigXMLParseException(e);
                        }
                    }
                    sslContextBuilder.setProviderName(finalProviderName);
                    sslContextBuilder.setProviderSupplier(finalProvidersSupplier);
                    sslContextBuilder.setUseCipherSuitesOrder(true);
                    return sslContextBuilder.build();
                });
                return;
            }
        }
        throw reader.unexpectedDocumentEnd();
    }

    private static class TrustManagerBuilder {
        final Supplier providers;
        final Location xmlLocation;
        String providerName = null;
        String algorithm = null;
        KeyStore trustStore;
        boolean crl = false;
        List crlStreams = new ArrayList<>();
        int maxCertPath = 5;
        boolean ocsp = false;
        boolean preferCrls = false;
        boolean onlyLeafCert = false;
        boolean softFail = false;
        URI ocspResponder = null;
        boolean maxCertPathSet = false;
        String responderCertAlias = null;
        ExceptionSupplier responderStoreSupplier = null;

        TrustManagerBuilder(Supplier providers, Location xmlLocation) {
            this.providers = providers;
            this.xmlLocation = xmlLocation;
        }

        void setProviderName(String providerName) {
            this.providerName = providerName;
        }

        void setAlgorithm(String algorithm) {
            this.algorithm = algorithm;
        }

        void setTrustStore(KeyStore trustStore) {
            this.trustStore = trustStore;
        }

        void setCrl() {
            this.crl = true;
        }

        void addCrlStream(InputStream crlStream) {
            this.crlStreams.add(crlStream);
        }

        void setMaxCertPath(int maxCertPath) {
            checkMinimumParameter("maxCertPath", 1, maxCertPath);
            this.maxCertPath = maxCertPath;
            this.maxCertPathSet = true;
        }

        boolean isMaxCertPathSet() {
            return maxCertPathSet;
        }

        public void setOcsp() {
            this.ocsp = true;
        }

        public void setPreferCrls(boolean preferCrls) {
            this.preferCrls = preferCrls;
        }

        public void setOnlyLeafCert(boolean onlyLeafCert) {
            this.onlyLeafCert = onlyLeafCert;
        }

        public void setSoftFail(boolean softFail) {
            this.softFail = softFail;
        }

        public void setOcspResponder(URI ocspResponder) {
            this.ocspResponder = ocspResponder;
        }

        public void setOcspRescponderCertAlias(String alias) {
            this.responderCertAlias = alias;
        }

        public void setOcspResponderCertKeystoreSupplier(ExceptionSupplier supplier) {
            this.responderStoreSupplier = supplier;
        }

        X509TrustManager build() throws NoSuchAlgorithmException, KeyStoreException, ConfigXMLParseException {
            final String algorithm = this.algorithm != null ? this.algorithm : TrustManagerFactory.getDefaultAlgorithm();
            Provider provider = findProvider(providers, providerName, TrustManagerFactory.class, algorithm);
            if (provider == null) {
                throw xmlLog.xmlUnableToIdentifyProvider(xmlLocation, providerName, "TrustManagerFactory", algorithm);
            }

            final TrustManagerFactory trustManagerFactory = TrustManagerFactory.getInstance(algorithm, provider);
            if (crl || ocsp) {
                X509RevocationTrustManager.Builder revocationBuilder = X509RevocationTrustManager.builder();
                revocationBuilder.setCrlStreams(crlStreams);
                revocationBuilder.setResponderURI(ocspResponder);
                revocationBuilder.setTrustManagerFactory(trustManagerFactory);
                revocationBuilder.setTrustStore(trustStore);
                revocationBuilder.setOnlyEndEntity(onlyLeafCert);
                revocationBuilder.setSoftFail(softFail);
                revocationBuilder.setMaxCertPath(maxCertPath);

                if (crl && ocsp) {
                    revocationBuilder.setPreferCrls(preferCrls);
                    revocationBuilder.setNoFallback(false);
                } else if (crl) {
                    revocationBuilder.setPreferCrls(true);
                    revocationBuilder.setNoFallback(true);
                } else {
                    revocationBuilder.setPreferCrls(false);
                    revocationBuilder.setNoFallback(true);
                }

                if (responderCertAlias != null) {
                    KeyStore responderStore = responderStoreSupplier != null ? responderStoreSupplier.get() : trustStore;
                    revocationBuilder.setOcspResponderCert((X509Certificate) responderStore.getCertificate(responderCertAlias));
                }

                return revocationBuilder.build();
            } else {
                trustManagerFactory.init(trustStore);
            }
            for (TrustManager trustManager : trustManagerFactory.getTrustManagers()) {
                if (trustManager instanceof X509TrustManager) {
                    return (X509TrustManager) trustManager;
                }
            }
            throw ElytronMessages.log.noDefaultTrustManager();
        }
    }

    private static void parseCertificateRevocationLists(ConfigurationXMLStreamReader reader, TrustManagerBuilder builder, final Version xmlVersion) throws ConfigXMLParseException {
        while (reader.hasNext()) {
            final int tag = reader.nextTag();
            if (tag == START_ELEMENT) {
                if (reader.getLocalName().equals("certificate-revocation-list")) {
                    parseCertificateRevocationList(reader, builder, xmlVersion, true);
                } else {
                    throw reader.unexpectedElement();
                }
            } else if (tag != END_ELEMENT) {
                throw reader.unexpectedContent();
            } else if (tag == END_ELEMENT) {
                return;
            }
        }
        throw reader.unexpectedDocumentEnd();
    }

    private static void parseCertificateRevocationList(ConfigurationXMLStreamReader reader, TrustManagerBuilder builder, final Version xmlVersion, boolean multipleCrls) throws ConfigXMLParseException {
        final int attributeCount = reader.getAttributeCount();
        String path = null;
        ExceptionSupplier resourceSource = null;
        URI uriSource = null;
        boolean gotSource = false;
        for (int i = 0; i < attributeCount; i ++) {
            checkAttributeNamespace(reader, i);
            switch (reader.getAttributeLocalName(i)) {
                case "path": {
                    if (gotSource) throw reader.unexpectedAttribute(i);
                    gotSource = true;
                    path = reader.getAttributeValueResolved(i);
                    break;
                }
                case "maximum-cert-path": { //Deprecated
                    if (builder.isMaxCertPathSet() || multipleCrls) throw reader.unexpectedAttribute(i);
                    xmlLog.xmlDeprecatedElement("maximum-cert-path", reader.getLocation());
                    // only set the maximum-cert-path inside a CRL if it's a single CRL.
                    builder.setMaxCertPath(reader.getIntAttributeValueResolved(i, 1, Integer.MAX_VALUE));
                    break;
                }
                default: throw reader.unexpectedAttribute(i);
            }
        }
        while (reader.hasNext()) {
            final int tag = reader.nextTag();
            if (tag == START_ELEMENT) {
                checkElementNamespace(reader, xmlVersion);
                switch (reader.getLocalName()) {
                    case "resource": {
                        if (gotSource || !xmlVersion.isAtLeast(Version.VERSION_1_1)) {
                            throw reader.unexpectedElement();
                        }
                        gotSource = true;
                        resourceSource = parseResourceType(reader, xmlVersion);
                        break;
                    }
                    case "uri": {
                        if (gotSource || !xmlVersion.isAtLeast(Version.VERSION_1_1)) {
                            throw reader.unexpectedElement();
                        }
                        gotSource = true;
                        uriSource = parseUriType(reader);
                        break;
                    }
                    default: throw reader.unexpectedElement();
                }
            } else if (tag == END_ELEMENT) {
                builder.setCrl();
                if (gotSource) {
                    try {
                        if (path != null) builder.addCrlStream(new FileInputStream(path));
                        else if (resourceSource != null) builder.addCrlStream(resourceSource.get());
                        else if (uriSource != null) builder.addCrlStream(uriSource.toURL().openStream());
                    } catch (IOException e) {
                        throw new ConfigXMLParseException(e);
                    }
                }
                return;
            } else {
                throw reader.unexpectedContent();
            }
        }
        throw reader.unexpectedDocumentEnd();
    }

    private static void parseOcsp(ConfigurationXMLStreamReader reader, TrustManagerBuilder builder, Map> keyStoresMap) throws ConfigXMLParseException {
        final int attributeCount = reader.getAttributeCount();
        boolean gotPreferCrls = false;
        boolean gotResponder = false;
        boolean gotResponderCertAlias = false;
        boolean gotResponderKeystore = false;
        builder.setOcsp();
        for (int i = 0; i < attributeCount; i ++) {
            checkAttributeNamespace(reader, i);
            switch (reader.getAttributeLocalName(i)) {
                case "responder": {
                    if (gotResponder) throw reader.unexpectedAttribute(i);
                    builder.setOcspResponder(reader.getURIAttributeValueResolved(i));
                    gotResponder = true;
                    break;
                }
                case "prefer-crls": {
                    if (gotPreferCrls) throw reader.unexpectedAttribute(i);
                    builder.setPreferCrls(reader.getBooleanAttributeValueResolved(i));
                    gotPreferCrls = true;
                    break;
                }
                case "responder-certificate": {
                    if (gotResponderCertAlias) throw reader.unexpectedAttribute(i);
                    builder.setOcspRescponderCertAlias(reader.getAttributeValueResolved(i));
                    gotResponderCertAlias = true;
                    break;
                }
                case "responder-keystore": {
                    if (gotResponderKeystore) throw reader.unexpectedAttribute(i);
                    builder.setOcspResponderCertKeystoreSupplier(keyStoresMap.get(reader.getAttributeValueResolved(i)));
                    gotResponderKeystore = true;
                    break;
                }
                default: throw reader.unexpectedAttribute(i);
            }
        }
        while (reader.hasNext()) {
            final int tag = reader.nextTag();
            if (tag == END_ELEMENT) {
                return;
            } else {
                throw reader.unexpectedContent();
            }
        }
        throw reader.unexpectedDocumentEnd();
    }

    private static ExceptionSupplier parseKeyStoreSslCertificate(ConfigurationXMLStreamReader reader,
            final Version xmlVersion, Map> keyStoresMap,
            final Map> credentialStoresMap, final Supplier providers)
            throws ConfigXMLParseException {
        final int attributeCount = reader.getAttributeCount();
        final XMLLocation location = reader.getLocation();
        String providerName = null;
        String algorithm = null;
        String keyStoreName = null;
        String alias = null;
        for (int i = 0; i < attributeCount; i ++) {
            checkAttributeNamespace(reader, i);
            switch (reader.getAttributeLocalName(i)) {
                case "provider-name": {
                    if (providerName != null || !xmlVersion.isAtLeast(Version.VERSION_1_2)) throw reader.unexpectedAttribute(i);
                    providerName = reader.getAttributeValueResolved(i);
                    break;
                }
                case "algorithm": {
                    if (providerName != null || !xmlVersion.isAtLeast(Version.VERSION_1_2)) throw reader.unexpectedAttribute(i);
                    if (algorithm != null) throw reader.unexpectedAttribute(i);
                    algorithm = reader.getAttributeValueResolved(i);
                    break;
                }
                case "key-store-name": {
                    if (keyStoreName != null) throw reader.unexpectedAttribute(i);
                    keyStoreName = reader.getAttributeValueResolved(i);
                    break;
                }
                case "alias": {
                    if (alias != null) throw reader.unexpectedAttribute(i);
                    alias = reader.getAttributeValueResolved(i);
                    break;
                }
                default: throw reader.unexpectedAttribute(i);
            }
        }
        if (keyStoreName == null) {
            throw missingAttribute(reader, "key-store-name");
        }
        ExceptionSupplier keyStoreCredential = null;
        while (reader.hasNext()) {
            final int tag = reader.nextTag();
            if (tag == START_ELEMENT) {
                checkElementNamespace(reader, xmlVersion);
                switch (reader.getLocalName()) {
                    case "key-store-credential": {
                        if (keyStoreCredential != null) throw reader.unexpectedElement();
                        keyStoreCredential = parseKeyStoreRefType(reader, xmlVersion, keyStoresMap, credentialStoresMap, providers);
                        break;
                    }
                    case "key-store-clear-password": {
                        if (keyStoreCredential != null) throw reader.unexpectedElement();
                        ExceptionSupplier credential = parseClearPassword(reader, providers);
                        keyStoreCredential = () -> new PasswordEntry(credential.get());
                        break;
                    }
                    case "key-store-masked-password": {
                        if (keyStoreCredential != null || !xmlVersion.isAtLeast(Version.VERSION_1_4)) throw reader.unexpectedElement();
                        ExceptionSupplier credential = parseMaskedPassword(reader, providers);
                        keyStoreCredential = () -> new PasswordEntry(credential.get());
                        break;
                    }
                    case "credential-store-reference": {
                        if (keyStoreCredential != null || !xmlVersion.isAtLeast(Version.VERSION_1_0_1)) {
                            throw reader.unexpectedElement();
                        }
                        final XMLLocation nestedLocation = reader.getLocation();
                        ExceptionSupplier credentialSourceSupplier = parseCredentialStoreRefType(reader, credentialStoresMap);
                        keyStoreCredential = () -> {
                            try {
                                PasswordCredential passwordCredential = credentialSourceSupplier.get().getCredential(PasswordCredential.class);
                                if (passwordCredential == null) {
                                    throw new ConfigXMLParseException(xmlLog.couldNotObtainCredential(), reader);
                                }
                                return new PasswordEntry(passwordCredential.getPassword());
                            } catch (IOException e) {
                                throw xmlLog.xmlFailedToCreateCredential(nestedLocation, e);
                            }
                        };
                        break;
                    }
                    default: throw reader.unexpectedElement();
                }
            } else if (tag == END_ELEMENT) {
                final ExceptionSupplier finalKeyStoreCredential = keyStoreCredential;
                final String finalAlgorithm = algorithm;
                final String finalProviderName = providerName;
                final String finalKeyStoreName = keyStoreName;
                final String finalAlias = alias;
                return () -> {
                    try {
                        final ExceptionSupplier keyStoreSupplier = keyStoresMap.get(finalKeyStoreName);
                        if (keyStoreSupplier == null) {
                            throw xmlLog.xmlUnknownKeyStoreSpecified(location);
                        }
                        KeyStore keyStore = keyStoreSupplier.get();

                        if (xmlLog.isTraceEnabled()) {
                            xmlLog.tracef("Using KeyStore [%s] containing aliases %s", finalKeyStoreName, aliasesToString(keyStore.aliases()));
                        }

                        if (finalAlias != null) {
                            keyStore = FilteringKeyStore.filteringKeyStore(keyStore, AliasFilter.fromString(finalAlias));
                            if (xmlLog.isTraceEnabled()) xmlLog.tracef("Filtered aliases %s", aliasesToString(keyStore.aliases()));
                            if (keyStore.size() < 1) throw xmlLog.keyStoreEntryMissing(location, finalAlias);
                        }

                        String algorithmResolved = finalAlgorithm != null ? finalAlgorithm : KeyManagerFactory.getDefaultAlgorithm();
                        Provider provider = findProvider(providers, finalProviderName, KeyManagerFactory.class, algorithmResolved);
                        if (provider == null) {
                            throw xmlLog.xmlUnableToIdentifyProvider(location, finalProviderName, "KeyManagerFactory", algorithmResolved);
                        }

                        char[] password = keyStoreCredentialToPassword(finalKeyStoreCredential, providers);

                        KeyManagerFactory keyManagerFactory = KeyManagerFactory.getInstance(algorithmResolved, provider);
                        keyManagerFactory.init(keyStoreSupplier.get(), password);

                        for (KeyManager keyManager : keyManagerFactory.getKeyManagers()) {
                            if (keyManager instanceof X509ExtendedKeyManager) {
                                return (X509ExtendedKeyManager) keyManager;
                            }
                        }
                        throw ElytronMessages.log.noDefaultKeyManager();
                    } catch (GeneralSecurityException | IOException e) {
                        throw xmlLog.xmlFailedToLoadKeyStoreData(location, e);
                    }
                };
            } else {
                throw reader.unexpectedContent();
            }
        }
        throw reader.unexpectedDocumentEnd();
    }

    private static String aliasesToString(Enumeration aliases) {
        StringBuilder builder = new StringBuilder("[");
        while (aliases.hasMoreElements()) {
            builder.append(aliases.nextElement());
            if (aliases.hasMoreElements()) builder.append(", ");
        }
        return builder.append(']').toString();
    }

    private static void parseTrustManager(ConfigurationXMLStreamReader reader, TrustManagerBuilder builder, final Version xmlVersion) throws ConfigXMLParseException {
        final int attributeCount = reader.getAttributeCount();
        String providerName = null;
        String algorithm = null;
        boolean gotSoftFail = false;
        boolean gotOnlyLeafCert = false;
        for (int i = 0; i < attributeCount; i ++) {
            checkAttributeNamespace(reader, i);
            switch (reader.getAttributeLocalName(i)) {
                case "provider-name": {
                    if (providerName != null) throw reader.unexpectedAttribute(i);
                    providerName = reader.getAttributeValueResolved(i);
                    break;
                }
                case "algorithm": {
                    if (algorithm != null) throw reader.unexpectedAttribute(i);
                    algorithm = reader.getAttributeValueResolved(i);
                    break;
                }
                case "soft-fail": {
                    if (gotSoftFail || !xmlVersion.isAtLeast(Version.VERSION_1_4)) {
                        throw reader.unexpectedAttribute(i);
                    }
                    gotSoftFail = true;
                    builder.setSoftFail(reader.getBooleanAttributeValueResolved(i));
                    break;
                }
                case "maximum-cert-path": {
                    if (builder.isMaxCertPathSet() || !xmlVersion.isAtLeast(Version.VERSION_1_4)) {
                        throw reader.unexpectedAttribute(i);
                    }
                    builder.setMaxCertPath(reader.getIntAttributeValueResolved(i, 1, Integer.MAX_VALUE));
                    break;
                }
                case "only-leaf-cert": {
                    if (gotOnlyLeafCert || !xmlVersion.isAtLeast(Version.VERSION_1_4)) {
                        throw reader.unexpectedAttribute(i);
                    }
                    gotOnlyLeafCert = true;
                    builder.setOnlyLeafCert(reader.getBooleanAttributeValueResolved(i));
                    break;
                }
                default: throw reader.unexpectedAttribute(i);
            }
        }
        while (reader.hasNext()) {
            final int tag = reader.nextTag();
            if (tag == START_ELEMENT) {
                throw reader.unexpectedElement();
            } else if (tag == END_ELEMENT) {
                builder.setProviderName(providerName);
                builder.setAlgorithm(algorithm);
                return;
            } else {
                throw reader.unexpectedContent();
            }
        }
        throw reader.unexpectedDocumentEnd();
    }

    static ExceptionUnaryOperator>, ConfigXMLParseException> parseSslContextRuleType(final ConfigurationXMLStreamReader reader, final Version xmlVersion, final Map, ConfigXMLParseException>> sslContextsMap) throws ConfigXMLParseException {
        final String attributeName = "use-ssl-context";
        final String name = requireSingleAttribute(reader, attributeName);
        final XMLLocation location = reader.getLocation();
        final MatchRule rule = parseAbstractMatchRuleType(reader, xmlVersion);
        return next -> {
            final ExceptionSupplier, ConfigXMLParseException> factory = sslContextsMap.get(name);
            if (factory == null) throw xmlLog.xmlUnknownSslContextSpecified(location, name);
            return new RuleNode<>(next, rule, factory.get());
        };
    }

    static ExceptionUnaryOperator, ConfigXMLParseException> parseAuthenticationRuleType(final ConfigurationXMLStreamReader reader, final Version xmlVersion, final Map> authenticationConfigurationsMap) throws ConfigXMLParseException {
        final String attributeName = "use-configuration";
        final String name = requireSingleAttribute(reader, attributeName);
        final XMLLocation location = reader.getLocation();
        final MatchRule rule = parseAbstractMatchRuleType(reader, xmlVersion);
        return next -> {
            final ExceptionSupplier factory = authenticationConfigurationsMap.get(name);
            if (factory == null) throw xmlLog.xmlUnknownAuthenticationConfigurationSpecified(location, name);
            return new RuleNode<>(next, rule, factory.get());
        };
    }

    static  ExceptionSupplier, ConfigXMLParseException> parseRulesType(ConfigurationXMLStreamReader reader, final Version xmlVersion,
            final Map> configurations, ExceptionBiFunction>, ExceptionUnaryOperator, ConfigXMLParseException>, ConfigXMLParseException> ruleParseFunction)
            throws ConfigXMLParseException {
        requireNoAttributes(reader);
        final List, ConfigXMLParseException>> rulesList = new ArrayList<>();
        while (reader.hasNext()) {
            final int tag = reader.nextTag();
            if (tag == START_ELEMENT) {
                checkElementNamespace(reader, xmlVersion);
                if (reader.getLocalName().equals("rule")) {
                    rulesList.add(ruleParseFunction.apply(reader, configurations));
                } else {
                    throw reader.unexpectedElement();
                }
            } else if (tag == END_ELEMENT) {
                return () -> {
                    RuleNode node = null;
                    final ListIterator, ConfigXMLParseException>> iterator = rulesList.listIterator(rulesList.size());
                    // iterate backwards to build the singly-linked list in constant time
                    while (iterator.hasPrevious()) {
                        node = iterator.previous().apply(node);
                    }
                    return node;
                };
            } else {
                throw reader.unexpectedContent();
            }
        }
        throw reader.unexpectedDocumentEnd();
    }

    static void parseAuthenticationConfigurationType(ConfigurationXMLStreamReader reader, final Version xmlVersion, final Map> authenticationConfigurationsMap,
            final Map> keyStoresMap, final Map> credentialStoresMap,
            final Supplier providers) throws ConfigXMLParseException {
        final String name = requireSingleAttribute(reader, "name");
        if (authenticationConfigurationsMap.containsKey(name)) {
            throw xmlLog.xmlDuplicateAuthenticationConfigurationName(name, reader);
        }

        ExceptionUnaryOperator configuration = ignored -> AuthenticationConfiguration.empty();
        DeferredSupplier providerSupplier = new DeferredSupplier<>(providers);
        configuration = andThenOp(configuration, parent -> parent.useProviders(providerSupplier));

        int foundBits = 0;
        if (! reader.hasNext()) {
            throw reader.unexpectedDocumentEnd();
        }
        while (reader.hasNext()) {
            int tag = reader.nextTag();
            if (tag == START_ELEMENT) {
                checkElementNamespace(reader, xmlVersion);
                switch (reader.getLocalName()) {
                    // -- set --
                    case "set-host": {
                        if (isSet(foundBits, 0)) throw reader.unexpectedElement();
                        foundBits = setBit(foundBits, 0);
                        final String hostName = parseNameType(reader);
                        configuration = andThenOp(configuration, parentConfig -> parentConfig.useHost(hostName));
                        xmlLog.xmlDeprecatedElement(reader.getLocalName(), reader.getLocation());
                        break;
                    }
                    case "set-port": {
                        if (isSet(foundBits, 1)) throw reader.unexpectedElement();
                        foundBits = setBit(foundBits, 1);
                        final int port = parsePortType(reader);
                        configuration = andThenOp(configuration, parentConfig -> parentConfig.usePort(port));
                        xmlLog.xmlDeprecatedElement(reader.getLocalName(), reader.getLocation());
                        break;
                    }
                    // these two are a  which is why they share a bit #; you can have only one of them
                    case "set-user-name": {
                        if (isSet(foundBits, 2)) throw reader.unexpectedElement();
                        foundBits = setBit(foundBits, 2);
                        final String userName = parseNameType(reader);
                        configuration = andThenOp(configuration, parentConfig -> parentConfig.useName(userName));
                        break;
                    }
                    case "set-anonymous": {
                        if (isSet(foundBits, 2)) throw reader.unexpectedElement();
                        foundBits = setBit(foundBits, 2);
                        parseEmptyType(reader);
                        configuration = andThenOp(configuration, AuthenticationConfiguration::useAnonymous);
                        break;
                    }
                    case "set-mechanism-realm": {
                        if (isSet(foundBits, 3)) throw reader.unexpectedElement();
                        foundBits = setBit(foundBits, 3);
                        final String realm = parseNameType(reader);
                        configuration = andThenOp(configuration, parentConfig -> parentConfig.useRealm(realm));
                        break;
                    }
                    case "rewrite-user-name-regex": {
                        if (isSet(foundBits, 4)) throw reader.unexpectedElement();
                        foundBits = setBit(foundBits, 4);
                        final NameRewriter nameRewriter = parseRegexSubstitutionType(reader);
                        configuration = andThenOp(configuration, parentConfig -> parentConfig.rewriteUser(nameRewriter));
                        break;
                    }
                    case "set-mechanism-properties": {
                        if (isSet(foundBits, 5)) throw reader.unexpectedElement();
                        foundBits = setBit(foundBits, 5);
                        final Map mechanismProperties = parsePropertiesType(reader, xmlVersion);
                        configuration = andThenOp(configuration, parentConfig -> parentConfig.useSaslMechanismProperties(mechanismProperties, true));
                        break;
                    }
                    case "sasl-mechanism-selector": {
                        if (isSet(foundBits, 6)) throw reader.unexpectedElement();
                        foundBits = setBit(foundBits, 6);
                        final SaslMechanismSelector selector = parseSaslMechanismSelectorType(reader);
                        configuration = andThenOp(configuration, parentConfig -> parentConfig.setSaslMechanismSelector(selector));
                        break;
                    }
                    case "credentials": {
                        if (isSet(foundBits, 9)) throw reader.unexpectedElement();
                        foundBits = setBit(foundBits, 9);
                        final ExceptionSupplier credentialSource = parseCredentialsType(reader, xmlVersion, keyStoresMap, credentialStoresMap, providerSupplier);
                        configuration = andThenOp(configuration, parentConfig -> parentConfig.useCredentials(credentialSource.get()));
                        break;
                    }
                    case "set-authorization-name": {
                        if (isSet(foundBits, 10)) throw reader.unexpectedElement();
                        foundBits = setBit(foundBits, 10);
                        final String authName = parseNameType(reader);
                        configuration = andThenOp(configuration, parentConfig -> parentConfig.useAuthorizationName(authName));
                        break;
                    }
                    case "providers": {
                        if (isSet(foundBits, 11)) throw reader.unexpectedElement();
                        foundBits = setBit(foundBits, 11);
                        Supplier supplier = parseProvidersType(reader, xmlVersion);
                        if (supplier != null) {
                            providerSupplier.setSupplier(supplier);
                        }
                        break;
                    }
                    // these two are a  which is why they share a bit #; you can have only one of them
                    case "use-provider-sasl-factory": {
                        if (isSet(foundBits, 12)) throw reader.unexpectedElement();
                        foundBits = setBit(foundBits, 12);
                        parseEmptyType(reader);
                        configuration = andThenOp(configuration, AuthenticationConfiguration::useSaslClientFactoryFromProviders);
                        break;
                    }
                    case "use-service-loader-sasl-factory": {
                        if (isSet(foundBits, 12)) throw reader.unexpectedElement();
                        foundBits = setBit(foundBits, 12);
                        final String moduleName = parseModuleRefType(reader);
                        final ClassLoader classLoader = (moduleName == null) ? ElytronXmlParser.class.getClassLoader() : ModuleLoader.getClassLoaderFromModule(reader, moduleName);
                        configuration = andThenOp(configuration, parentConfig -> parentConfig.useSaslClientFactory(new ServiceLoaderSaslClientFactory(classLoader)));
                        break;
                    }
                    case "set-protocol": {
                        if (isSet(foundBits, 13)) throw reader.unexpectedElement();
                        foundBits = setBit(foundBits, 13);
                        final String protocol = parseNameType(reader);
                        configuration = andThenOp(configuration, parentConfig -> parentConfig.useProtocol(protocol));
                        xmlLog.xmlDeprecatedElement(reader.getLocalName(), reader.getLocation());
                        break;
                    }
                    case "webservices": {
                        if (isSet(foundBits, 14)) throw reader.unexpectedElement();
                        foundBits = setBit(foundBits, 14);
                        Map webServices = parseWebServicesType(reader, xmlVersion);
                        configuration = andThenOp(configuration, parentConfig -> parentConfig.useWebServices(webServices));
                        break;
                    }
                    default: {
                        throw reader.unexpectedElement();
                    }
                }
            } else if (tag == END_ELEMENT) {
                final ExceptionUnaryOperator finalConfiguration = configuration;
                authenticationConfigurationsMap.put(name, () -> finalConfiguration.apply(AuthenticationConfiguration.empty()));
                return;
            } else {
                throw reader.unexpectedContent();
            }
        }
        throw reader.unexpectedDocumentEnd();
    }

    static Supplier parseProvidersType(ConfigurationXMLStreamReader reader, final 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) ?
                                ELYTRON_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();
    }

    static Map parseWebServicesType(ConfigurationXMLStreamReader reader, final Version xmlVersion) throws ConfigXMLParseException {
        requireNoAttributes(reader);

        int foundBits = 0;
        if (! reader.hasNext()) {
            throw reader.unexpectedDocumentEnd();
        }
        Map propertiesMap = new HashMap<>();

        while (reader.hasNext()) {
            final int tag = reader.nextTag();
            if (tag == START_ELEMENT) {
                checkElementNamespace(reader, xmlVersion);
                switch (reader.getLocalName()) {
                    case "set-http-mechanism": {
                        if (isSet(foundBits, 0)) throw reader.unexpectedElement();
                        foundBits = setBit(foundBits, 0);
                        final String httpMechName = parseNameType(reader);
                        propertiesMap.put("http-mechanism", httpMechName);
                        break;
                    }
                    case "set-ws-security-type": {
                        if (isSet(foundBits, 1)) throw reader.unexpectedElement();
                        foundBits = setBit(foundBits, 1);
                        final String wsSecurityType = parseNameType(reader);
                        propertiesMap.put("ws-security-type", wsSecurityType);
                        break;
                    }
                    default: throw reader.unexpectedElement();
                }
            } else if (tag == END_ELEMENT) {
                return propertiesMap;
            } else {
                throw reader.unexpectedContent();
            }
        }
        throw reader.unexpectedDocumentEnd();
    }

    /**
     * Parse the XML match-rule group.  On return, the reader will be positioned either at a start tag for an element
     * that is not included in this group, or at an end tag.
     *
     * @param reader the XML reader
     * @param xmlVersion the version of parsed XML
     * @return the parsed match rule
     * @throws ConfigXMLParseException if the resource failed to be parsed
     */
    static MatchRule parseAbstractMatchRuleType(ConfigurationXMLStreamReader reader, final Version xmlVersion) throws ConfigXMLParseException {
        MatchRule rule = MatchRule.ALL;
        int foundBits = 0;
        while (reader.hasNext()) {
            final int tag = reader.nextTag();
            if (tag == START_ELEMENT) {
                checkElementNamespace(reader, xmlVersion);
                switch (reader.getLocalName()) {
                    // -- match --
                    case "match-no-user": {
                        if (isSet(foundBits, 0)) throw reader.unexpectedElement();
                        foundBits = setBit(foundBits, 0);
                        parseEmptyType(reader);
                        rule = rule.matchNoUser();
                        break;
                    }
                    case "match-user": {
                        if (isSet(foundBits, 0)) throw reader.unexpectedElement();
                        foundBits = setBit(foundBits, 0);
                        rule = rule.matchUser(parseNameType(reader));
                        break;
                    }
                    case "match-protocol": {
                        if (isSet(foundBits, 1)) throw reader.unexpectedElement();
                        foundBits = setBit(foundBits, 1);
                        rule = rule.matchProtocol(parseNameType(reader));
                        break;
                    }
                    case "match-host": {
                        if (isSet(foundBits, 2)) throw reader.unexpectedElement();
                        foundBits = setBit(foundBits, 2);
                        rule = rule.matchHost(parseNameType(reader));
                        break;
                    }
                    case "match-path": {
                        if (isSet(foundBits, 3)) throw reader.unexpectedElement();
                        foundBits = setBit(foundBits, 3);
                        rule = rule.matchPath(parseNameType(reader));
                        break;
                    }
                    case "match-port": {
                        if (isSet(foundBits, 4)) throw reader.unexpectedElement();
                        foundBits = setBit(foundBits, 4);
                        rule = rule.matchPort(parsePortType(reader));
                        break;
                    }
                    case "match-urn": {
                        if (isSet(foundBits, 5)) throw reader.unexpectedElement();
                        foundBits = setBit(foundBits, 5);
                        rule = rule.matchUrnName(parseNameType(reader));
                        break;
                    }
                    case "match-domain": {
                        if (isSet(foundBits, 6)) throw reader.unexpectedElement();
                        foundBits = setBit(foundBits, 6);
                        rule = rule.matchLocalSecurityDomain(parseNameType(reader));
                        break;
                    }
                    case "match-abstract-type": {
                        if (isSet(foundBits, 7)) throw reader.unexpectedElement();
                        foundBits = setBit(foundBits, 7);
                        rule = parseMatchAbstractType(rule, reader);
                        break;
                    }
                    default: {
                        return rule;
                    }
                }
            } else {
                return rule;
            }
        }
        throw reader.unexpectedDocumentEnd();
    }

    private static MatchRule parseMatchAbstractType(final MatchRule rule, final ConfigurationXMLStreamReader reader) throws ConfigXMLParseException {
        final int attributeCount = reader.getAttributeCount();
        String name = null;
        String authority = null;
        for (int i = 0; i < attributeCount; i ++) {
            checkAttributeNamespace(reader, i);
            switch (reader.getAttributeLocalName(i)) {
                case "name": name = reader.getAttributeValueResolved(i); break;
                case "authority": authority = reader.getAttributeValueResolved(i); break;
                default: throw reader.unexpectedAttribute(i);
            }
        }
        if (! reader.hasNext()) throw reader.unexpectedDocumentEnd();
        if (reader.nextTag() != END_ELEMENT) throw reader.unexpectedElement();
        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);
        while (reader.hasNext()) {
            final int tag = reader.nextTag();
            if (tag == START_ELEMENT) {
                checkElementNamespace(reader, xmlVersion);
                switch (reader.getLocalName()) {
                    case "key-store-reference": {
                        final ExceptionSupplier supplier = parseKeyStoreRefType(reader, xmlVersion, keyStoresMap, credentialStoresMap, providers);
                        function = andThenOp(function, credentialSource -> credentialSource.with(new KeyStoreCredentialSource(new FixedSecurityFactory(supplier.get()))));
                        break;
                    }
                    case "credential-store-reference": {
                        final ExceptionSupplier supplier = parseCredentialStoreRefType(reader, credentialStoresMap);
                        function = andThenOp(function, credentialSource -> credentialSource.with(supplier.get()));
                        break;
                    }
                    case "clear-password": {
                        ExceptionSupplier password = parseClearPassword(reader, providers);
                        function = andThenOp(function, credentialSource -> credentialSource.with(IdentityCredentials.NONE.withCredential(new PasswordCredential(password.get()))));
                        break;
                    }
                    case "masked-password": {
                        if ( ! xmlVersion.isAtLeast(Version.VERSION_1_4)) {
                            throw reader.unexpectedElement();
                        }
                        final XMLLocation location = reader.getLocation();
                        ExceptionSupplier password = parseMaskedPassword(reader, providers);
                        Password maskedPassword = password.get();
                        Password finalPassword;
                        try {
                            final PasswordFactory passwordFactory = PasswordFactory.getInstance(maskedPassword.getAlgorithm(), providers);
                            final ClearPasswordSpec spec = passwordFactory.getKeySpec(maskedPassword, ClearPasswordSpec.class);
                            final char[] clearPassword = spec.getEncodedPassword();
                            PasswordFactory clearPasswordFactory = PasswordFactory.getInstance(ClearPassword.ALGORITHM_CLEAR, providers);
                            finalPassword = clearPasswordFactory.generatePassword(new ClearPasswordSpec(clearPassword)).castAs(ClearPassword.class);
                        } catch (InvalidKeySpecException | NoSuchAlgorithmException cause) {
                            throw xmlLog.xmlFailedToCreateCredential(location, cause);
                        }
                        function = andThenOp(function, credentialSource -> credentialSource.with(IdentityCredentials.NONE.withCredential(new PasswordCredential(finalPassword))));
                        break;
                    }
                    case "key-pair": {
                        KeyPairCredential keyPairCredential = parseKeyPair(reader, xmlVersion, credentialStoresMap, providers);
                        function = andThenOp(function, credentialSource -> credentialSource.with(IdentityCredentials.NONE.withCredential(keyPairCredential)));
                        break;
                    }
                    case "certificate": {
                        X509CertificateChainPrivateCredential credential = parseCertificateType(reader, xmlVersion);
                        function = andThenOp(function, credentialSource -> credentialSource.with(IdentityCredentials.NONE.withCredential(credential)));
                        break;
                    }
                    case "public-key-pem": {
                        PublicKey publicKey = parsePem(reader, PublicKey.class);
                        function = andThenOp(function, credentialSource -> credentialSource.with(IdentityCredentials.NONE.withCredential(new PublicKeyCredential(publicKey))));
                        break;
                    }
                    case "bearer-token": {
                        BearerTokenCredential bearerToken = parseBearerTokenType(reader);
                        function = andThenOp(function, credentialSource -> credentialSource.with(IdentityCredentials.NONE.withCredential(bearerToken)));
                        break;
                    }
                    case "oauth2-bearer-token": {
                        final ExceptionSupplier oauthCredentialSourceSupplier = parseOAuth2BearerTokenType(reader, credentialStoresMap, xmlVersion);
                        function = andThenOp(function, credentialSource -> credentialSource.with(oauthCredentialSourceSupplier.get()));
                        break;
                    }
                    case "local-kerberos": {
                        if ( ! xmlVersion.isAtLeast(Version.VERSION_1_1)) {
                            throw reader.unexpectedElement();
                        }
                        CredentialSource kerberosCredentialSource = parseLocalKerberos(reader);
                        function = andThenOp(function, credentialSource -> credentialSource.with(kerberosCredentialSource));
                        xmlLog.xmlDeprecatedElement(reader.getLocalName(), reader.getLocation());
                        break;
                    }
                    case "ssh-credential": {
                        if ( ! xmlVersion.isAtLeast(Version.VERSION_1_6)) {
                            throw reader.unexpectedElement();
                        }
                        SSHCredential sshCredential = parseSSHKeyLocationCredential(reader, xmlVersion, credentialStoresMap, providers);
                        function = andThenOp(function, credentialSource -> credentialSource.with(credentialSource.with(IdentityCredentials.NONE.withCredential(sshCredential))));
                        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();
    }

    private static SSHCredential parseSSHKeyLocationCredential(final ConfigurationXMLStreamReader reader, final Version xmlVersion, final Map> credentialStoresMap, Supplier providers) throws ConfigXMLParseException {
        final int attributeCount = reader.getAttributeCount();
        String sshDirectory = null;
        String privateKeyIdentity = null;
        Credential passphrase = null;
        String knownHostsFile = null;
        for (int i = 0; i < attributeCount; i ++) {
            checkAttributeNamespace(reader, i);
            switch (reader.getAttributeLocalName(i)) {
                case "ssh-directory": {
                    sshDirectory = reader.getAttributeValueResolved(i);
                    break;
                }
                case "private-key-file": {
                    privateKeyIdentity = reader.getAttributeValueResolved(i);
                    break;
                }
                case "known-hosts-file": {
                    knownHostsFile = reader.getAttributeValueResolved(i);
                    break;
                }
                default: throw reader.unexpectedAttribute(i);
            }
        }
        while (reader.hasNext()) {
            final int tag = reader.nextTag();
            if (tag == START_ELEMENT) {
                checkElementNamespace(reader, xmlVersion);
                switch (reader.getLocalName()) {
                    case "credential-store-reference": {
                        final ExceptionSupplier supplier = parseCredentialStoreRefType(reader, credentialStoresMap);
                        try {
                            passphrase = supplier.get().getCredential(PasswordCredential.class);
                        } catch (IOException cause) {
                            throw xmlLog.couldNotObtainCredential();
                        }
                        break;
                    }
                    case "clear-password": {
                        ExceptionSupplier password = parseClearPassword(reader, providers);
                        passphrase = new PasswordCredential(password.get());
                        break;
                    }
                    case "masked-password": {
                        final XMLLocation location = reader.getLocation();
                        ExceptionSupplier password = parseMaskedPassword(reader, providers);
                        Password maskedPassword = password.get();
                        Password finalPassword;
                        try {
                            final PasswordFactory passwordFactory = PasswordFactory.getInstance(maskedPassword.getAlgorithm(), providers);
                            final ClearPasswordSpec spec = passwordFactory.getKeySpec(maskedPassword, ClearPasswordSpec.class);
                            final char[] clearPassword = spec.getEncodedPassword();
                            PasswordFactory clearPasswordFactory = PasswordFactory.getInstance(ClearPassword.ALGORITHM_CLEAR, providers);
                            finalPassword = clearPasswordFactory.generatePassword(new ClearPasswordSpec(clearPassword)).castAs(ClearPassword.class);
                        } catch (InvalidKeySpecException | NoSuchAlgorithmException cause) {
                            throw xmlLog.xmlFailedToCreateCredential(location, cause);
                        }
                        passphrase = new PasswordCredential(finalPassword);
                        break;
                    }
                    default: throw reader.unexpectedElement();
                }

            } else if (tag == END_ELEMENT) {
                SSHCredential.Builder builder = SSHCredential.builder();
                if (sshDirectory != null) builder.setSSHDirectory(sshDirectory);
                if (privateKeyIdentity != null) builder.setPrivateKeyIdentity(privateKeyIdentity);
                if (passphrase != null) builder.setPassphrase(passphrase);
                if (knownHostsFile != null) builder.setKnownHostsFile(knownHostsFile);
                return builder.build();
            } else {
                throw reader.unexpectedContent();
            }
        }
        throw reader.unexpectedDocumentEnd();
    }

    private static KeyPairCredential parseKeyPair(final ConfigurationXMLStreamReader reader, final Version xmlVersion, final Map> credentialStoresMap, Supplier providers) throws ConfigXMLParseException {
        PrivateKey privateKey = null;
        PublicKey publicKey = null;
        KeyPair keyPair = null;
        while (reader.hasNext()) {
            final int tag = reader.nextTag();
            if (tag == START_ELEMENT) {
                checkElementNamespace(reader, xmlVersion);
                switch (reader.getLocalName()) {
                    case "private-key-pem": {
                        if (privateKey != null || keyPair != null) throw reader.unexpectedElement();
                        privateKey = parsePem(reader, PrivateKey.class);
                        break;
                    }
                    case "public-key-pem": {
                        if (publicKey != null || keyPair != null) throw reader.unexpectedElement();
                        publicKey = parsePem(reader, PublicKey.class);
                        break;
                    }
                    case "openssh-private-key": {
                        if ( ! xmlVersion.isAtLeast(Version.VERSION_1_6)) {
                            throw reader.unexpectedElement();
                        }
                        if (keyPair != null || privateKey != null || publicKey != null) throw reader.unexpectedElement();
                        keyPair = parseOpenSSHKeyType(reader, xmlVersion, credentialStoresMap, providers);
                        break;
                    }
                    default: {
                        throw reader.unexpectedElement();
                    }
                }
            } else if (tag == END_ELEMENT) {
                if (keyPair == null) {
                    if (privateKey == null) throw reader.missingRequiredElement(xmlVersion.namespace, "private-key-pem");
                    if (publicKey == null) throw reader.missingRequiredElement(xmlVersion.namespace, "public-key-pem");
                    keyPair = new KeyPair(publicKey, privateKey);
                }
                return new KeyPairCredential(keyPair);
            } else {
                throw reader.unexpectedContent();
            }
        }
        throw reader.unexpectedDocumentEnd();
    }

    private static KeyPair parseOpenSSHKeyType(final ConfigurationXMLStreamReader reader, final Version xmlVersion, final Map> credentialStoresMap, Supplier providers) throws ConfigXMLParseException {
        final int attributeCount = reader.getAttributeCount();
        ExceptionUnaryOperator function = parent -> CredentialSource.NONE;
        String keyContent = null;
        for (int i = 0; i < attributeCount; i ++) {
            checkAttributeNamespace(reader, i);
            switch (reader.getAttributeLocalName(i)) {
                case "pem": {
                    if (keyContent != null) throw reader.unexpectedAttribute(i);
                    keyContent = reader.getAttributeValueResolved(i);
                    break;
                }
                default: throw reader.unexpectedAttribute(i);
            }
        }
        while (reader.hasNext()) {
            final int tag = reader.nextTag();
            if (tag == START_ELEMENT) {
                checkElementNamespace(reader, xmlVersion);
                switch (reader.getLocalName()) {
                    case "credential-store-reference": {
                        final ExceptionSupplier supplier = parseCredentialStoreRefType(reader, credentialStoresMap);
                        function = andThenOp(function, credentialSource -> credentialSource.with(supplier.get()));
                        break;
                    }
                    case "clear-password": {
                        ExceptionSupplier password = parseClearPassword(reader, providers);
                        function = andThenOp(function, credentialSource -> credentialSource.with(IdentityCredentials.NONE.withCredential(new PasswordCredential(password.get()))));
                        break;
                    }
                    case "masked-password": {
                        if ( ! xmlVersion.isAtLeast(Version.VERSION_1_4)) {
                            throw reader.unexpectedElement();
                        }
                        final XMLLocation location = reader.getLocation();
                        ExceptionSupplier password = parseMaskedPassword(reader, providers);
                        Password maskedPassword = password.get();
                        Password finalPassword;
                        try {
                            final PasswordFactory passwordFactory = PasswordFactory.getInstance(maskedPassword.getAlgorithm(), providers);
                            final ClearPasswordSpec spec = passwordFactory.getKeySpec(maskedPassword, ClearPasswordSpec.class);
                            final char[] clearPassword = spec.getEncodedPassword();
                            PasswordFactory clearPasswordFactory = PasswordFactory.getInstance(ClearPassword.ALGORITHM_CLEAR, providers);
                            finalPassword = clearPasswordFactory.generatePassword(new ClearPasswordSpec(clearPassword)).castAs(ClearPassword.class);
                        } catch (InvalidKeySpecException | NoSuchAlgorithmException cause) {
                            throw xmlLog.xmlFailedToCreateCredential(location, cause);
                        }
                        function = andThenOp(function, credentialSource -> credentialSource.with(IdentityCredentials.NONE.withCredential(new PasswordCredential(finalPassword))));
                        break;
                    }
                    default: throw reader.unexpectedElement();
                }

            } else if (tag == END_ELEMENT) {
                if (keyContent == null) throw reader.missingRequiredAttribute(reader.getNamespaceURI(), "openssh-private-key");
                final ExceptionUnaryOperator finalFunction = function;
                ElytronFilePasswordProvider passwordProvider = new ElytronFilePasswordProvider(()->finalFunction.apply(null));
                Iterator> pemContent = SshUtil.parsePemOpenSSHContent(CodePointIterator.ofString(keyContent), passwordProvider);
                final PemEntry pemEntry = pemContent.next();
                final KeyPair keyPair = pemEntry.tryCast(KeyPair.class);
                if (keyPair == null) throw xmlLog.xmlInvalidOpenSSHKey(reader);
                return keyPair;

            } else {
                throw reader.unexpectedContent();
            }
        }
        throw reader.unexpectedDocumentEnd();
    }

    private static X509CertificateChainPrivateCredential parseCertificateType(final ConfigurationXMLStreamReader reader, final Version xmlVersion) throws ConfigXMLParseException {
        requireNoAttributes(reader);
        PrivateKey privateKey = null;
        X509Certificate[] certificates = null;
        while (reader.hasNext()) {
            final int tag = reader.nextTag();
            if (tag == START_ELEMENT) {
                checkElementNamespace(reader, xmlVersion);
                switch (reader.getLocalName()) {
                    case "private-key-pem": {
                        if (privateKey != null) throw reader.unexpectedElement();
                        privateKey = parsePem(reader, PrivateKey.class);
                        break;
                    }
                    case "pem": {
                        if (certificates != null) throw reader.unexpectedElement();
                        certificates = parseMultiPem(reader, X509Certificate.class, X509Certificate[]::new);
                        break;
                    }
                    default: {
                        throw reader.unexpectedElement();
                    }
                }
            } else if (tag == END_ELEMENT) {
                if (privateKey == null) throw reader.missingRequiredElement(xmlVersion.namespace, "private-key-pem");
                if (certificates == null) throw reader.missingRequiredElement(xmlVersion.namespace, "pem");
                return new X509CertificateChainPrivateCredential(privateKey, certificates);
            } else {
                throw reader.unexpectedContent();
            }
        }
        throw reader.unexpectedDocumentEnd();
    }

    private static 

P[] parseMultiPem(final ConfigurationXMLStreamReader reader, final Class

pemType, final IntFunction ctor) throws ConfigXMLParseException { requireNoAttributes(reader); final Iterator> pemContent = Pem.parsePemContent(CodePointIterator.ofString(reader.getElementText())); if (! reader.hasNext()) throw reader.unexpectedDocumentEnd(); final ArrayList

arrayList = new ArrayList<>(); while (pemContent.hasNext()) { final PemEntry pemEntry = pemContent.next(); final P pem = pemEntry.tryCast(pemType); if (pem == null) throw xmlLog.xmlWrongPemType(reader, pemType, pemEntry.getEntry().getClass()); arrayList.add(pem); } if (arrayList.isEmpty()) throw xmlLog.xmlNoPemContent(reader); return arrayList.toArray(ctor.apply(arrayList.size())); } private static

P parsePem(final ConfigurationXMLStreamReader reader, final Class

pemType) throws ConfigXMLParseException { requireNoAttributes(reader); final Iterator> pemContent = Pem.parsePemContent(CodePointIterator.ofString(reader.getElementText())); if (! reader.hasNext()) throw reader.unexpectedDocumentEnd(); if (! pemContent.hasNext()) throw xmlLog.xmlNoPemContent(reader); final PemEntry pemEntry = pemContent.next(); final P pem = pemEntry.tryCast(pemType); if (pem == null) throw xmlLog.xmlWrongPemType(reader, pemType, pemEntry.getEntry().getClass()); return pem; } /** * Parse an XML element of type {@code key-stores-type} from an XML reader. * * @param reader the XML stream reader * @param xmlVersion the version of parsed XML * @param keyStoresMap the map of key stores to use * @throws ConfigXMLParseException if the resource failed to be parsed */ static void parseKeyStoresType(ConfigurationXMLStreamReader reader, final Version xmlVersion, final Map> keyStoresMap, final Map> credentialStoresMap, final Supplier providers) throws ConfigXMLParseException { requireNoAttributes(reader); while (reader.hasNext()) { final int tag = reader.nextTag(); if (tag == START_ELEMENT) { checkElementNamespace(reader, xmlVersion); switch (reader.getLocalName()) { case "key-store": { parseKeyStoreType(reader, xmlVersion, keyStoresMap, 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 key-store-type} from an XML reader. * * @param reader the XML stream reader * @param xmlVersion the version of parsed XML * @param keyStoresMap the map of key stores to use * @throws ConfigXMLParseException if the resource failed to be parsed */ static void parseKeyStoreType(ConfigurationXMLStreamReader reader, final Version xmlVersion, final Map> keyStoresMap, final Map> credentialStoresMap, final Supplier providers) throws ConfigXMLParseException { final int attributeCount = reader.getAttributeCount(); String name = null; String type = null; String provider = null; Boolean wrap = null; DeferredSupplier providersSupplier = new DeferredSupplier<>(providers); for (int i = 0; i < attributeCount; i ++) { checkAttributeNamespace(reader, 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; } case "wrap-passwords": { if (wrap != null) throw reader.unexpectedAttribute(i); wrap = Boolean.valueOf(Boolean.parseBoolean(reader.getAttributeValueResolved(i))); break; } default: throw reader.unexpectedAttribute(i); } } if (type == null && !xmlVersion.isAtLeast(Version.VERSION_1_3)) { throw missingAttribute(reader, "type"); } if (name == null) { throw missingAttribute(reader, "name"); } final XMLLocation location = reader.getLocation(); ExceptionSupplier passwordFactory = null; boolean gotSource = false; boolean gotCredential = false; boolean gotProviders = false; String fileSource = null; ExceptionSupplier resourceSource = null; URI uriSource = null; while (reader.hasNext()) { final int tag = reader.nextTag(); if (tag == START_ELEMENT) { checkElementNamespace(reader, xmlVersion); switch (reader.getLocalName()) { case "key-store-credential": { // group 2 if (gotCredential) { throw reader.unexpectedElement(); } gotCredential = true; final XMLLocation nestedLocation = reader.getLocation(); final ExceptionSupplier entryFactory = parseKeyStoreRefType(reader, xmlVersion, keyStoresMap, credentialStoresMap, providersSupplier); passwordFactory = () -> { final KeyStore.Entry entry = entryFactory.get(); if (entry instanceof PasswordEntry) try { final Password password = ((PasswordEntry) entry).getPassword(); final PasswordFactory passwordFactory1 = PasswordFactory.getInstance(password.getAlgorithm(), providersSupplier); final ClearPasswordSpec passwordSpec = passwordFactory1.getKeySpec(password, ClearPasswordSpec.class); return passwordSpec.getEncodedPassword(); } catch (GeneralSecurityException e) { throw xmlLog.xmlFailedToCreateCredential(nestedLocation, e); } return null; }; break; } case "credential-store-reference": { if (gotCredential || !xmlVersion.isAtLeast(Version.VERSION_1_0_1)) { throw reader.unexpectedElement(); } gotCredential = true; final XMLLocation nestedLocation = reader.getLocation(); ExceptionSupplier credentialSourceSupplier = parseCredentialStoreRefType(reader, credentialStoresMap); passwordFactory = () -> { try { return credentialSourceSupplier.get().applyToCredential(PasswordCredential.class, c -> c.getPassword().castAndApply(ClearPassword.class, ClearPassword::getPassword)); } catch (IOException e) { throw xmlLog.xmlFailedToCreateCredential(nestedLocation, e); } }; break; } case "key-store-clear-password": { // group 2 if (gotCredential) { throw reader.unexpectedElement(); } gotCredential = true; final ExceptionSupplier clearPassword = parseClearPassword(reader, providersSupplier); passwordFactory = () -> ((ClearPassword)clearPassword.get()).getPassword(); break; } case "key-store-masked-password": { // group 2 if (gotCredential || !xmlVersion.isAtLeast(Version.VERSION_1_4)) { throw reader.unexpectedElement(); } gotCredential = true; final XMLLocation nestedLocation = reader.getLocation(); final ExceptionSupplier maskedPassword = parseMaskedPassword(reader, providersSupplier); passwordFactory = () -> { try { Password password = maskedPassword.get(); PasswordFactory factory = PasswordFactory.getInstance(password.getAlgorithm(), providersSupplier); ClearPasswordSpec spec = factory.getKeySpec(password, ClearPasswordSpec.class); return spec.getEncodedPassword(); } catch (GeneralSecurityException e) { throw xmlLog.xmlFailedToCreateCredential(nestedLocation, e); } }; break; } case "file": { // group 1 if (gotSource || gotCredential) { throw reader.unexpectedElement(); } gotSource = true; fileSource = parseNameType(reader); break; } case "resource": { // group 1 if (gotSource || gotCredential) { throw reader.unexpectedElement(); } gotSource = true; resourceSource = parseResourceType(reader, xmlVersion); break; } case "uri": { // group 1 if (gotSource || gotCredential) { throw reader.unexpectedElement(); } gotSource = true; uriSource = parseUriType(reader); break; } case "providers": { if (gotProviders || !xmlVersion.isAtLeast(Version.VERSION_1_1)) { throw reader.unexpectedElement(); } gotProviders = true; Supplier supplier = parseProvidersType(reader, xmlVersion); if (supplier != null) { providersSupplier.setSupplier(supplier); } break; } default: throw reader.unexpectedElement(); } } else if (tag == END_ELEMENT) { ExceptionSupplier keyStoreFactory = null; if (type == null || type.equalsIgnoreCase("automatic")) { keyStoreFactory = new UnknownTypeFileKeyStoreFactory(providers, provider, passwordFactory, fileSource, resourceSource, uriSource, location); if (wrap == Boolean.TRUE) { keyStoreFactory = new PasswordKeyStoreFactory(keyStoreFactory); } } else { keyStoreFactory = new KeyStoreCreateFactory(providersSupplier, provider, type, location); if (wrap == Boolean.TRUE) { keyStoreFactory = new PasswordKeyStoreFactory(keyStoreFactory); } if (fileSource != null) { keyStoreFactory = new FileLoadingKeyStoreFactory(keyStoreFactory, passwordFactory, fileSource, location); } else if (resourceSource != null) { keyStoreFactory = new ResourceLoadingKeyStoreFactory(keyStoreFactory, passwordFactory, resourceSource, location); } else if (uriSource != null) { keyStoreFactory = new URILoadingKeyStoreFactory(keyStoreFactory, passwordFactory, uriSource, location); } else { keyStoreFactory = new NullLoadingKeyStoreFactory(keyStoreFactory, passwordFactory, location); } } keyStoresMap.put(name, keyStoreFactory); return; } else { throw reader.unexpectedContent(); } } throw reader.unexpectedDocumentEnd(); } /** * Parse an XML element of type {@code key-store-ref-type} from an XML reader. * * @param reader the XML stream reader * @param xmlVersion the version of parsed XML * @param keyStoresMap the map of key stores to use * @param credentialStoresMap the map of credential stores to use * @param providers supplier of providers for loading services * @return the key store entry factory * @throws ConfigXMLParseException if the resource failed to be parsed */ static ExceptionSupplier parseKeyStoreRefType(ConfigurationXMLStreamReader reader, final Version xmlVersion, final Map> keyStoresMap, final Map> credentialStoresMap, final Supplier providers) throws ConfigXMLParseException { final int attributeCount = reader.getAttributeCount(); final XMLLocation location = reader.getLocation(); String keyStoreName = null; String alias = null; for (int i = 0; i < attributeCount; i ++) { checkAttributeNamespace(reader, i); switch (reader.getAttributeLocalName(i)) { case "key-store-name": { if (keyStoreName != null) throw reader.unexpectedAttribute(i); keyStoreName = reader.getAttributeValueResolved(i); break; } case "alias": { if (alias != null) throw reader.unexpectedAttribute(i); alias = reader.getAttributeValueResolved(i); break; } default: throw reader.unexpectedElement(); } } if (keyStoreName == null) { throw missingAttribute(reader, "key-store-name"); } ExceptionSupplier keyStoreCredential = null; while (reader.hasNext()) { final int tag = reader.nextTag(); if (tag == START_ELEMENT) { checkElementNamespace(reader, xmlVersion); switch (reader.getLocalName()) { case "key-store-credential": { if (keyStoreCredential != null) throw reader.unexpectedElement(); keyStoreCredential = parseKeyStoreRefType(reader, xmlVersion, keyStoresMap, credentialStoresMap, providers); break; } case "key-store-clear-password": { if (keyStoreCredential != null) throw reader.unexpectedElement(); ExceptionSupplier credential = parseClearPassword(reader, providers); keyStoreCredential = () -> new PasswordEntry(credential.get()); break; } case "key-store-masked-password": { if (keyStoreCredential != null || !xmlVersion.isAtLeast(Version.VERSION_1_4)) throw reader.unexpectedElement(); ExceptionSupplier credential = parseMaskedPassword(reader, providers); keyStoreCredential = () -> new PasswordEntry(credential.get()); break; } case "credential-store-reference": { if (keyStoreCredential != null || !xmlVersion.isAtLeast(Version.VERSION_1_0_1)) { throw reader.unexpectedElement(); } final XMLLocation nestedLocation = reader.getLocation(); ExceptionSupplier credentialSourceSupplier = parseCredentialStoreRefType(reader, credentialStoresMap); keyStoreCredential = () -> { try { PasswordCredential passwordCredential = credentialSourceSupplier.get().getCredential(PasswordCredential.class); if (passwordCredential == null) { throw new ConfigXMLParseException(xmlLog.couldNotObtainCredential(), reader); } return new PasswordEntry(passwordCredential.getPassword()); } catch (IOException e) { throw xmlLog.xmlFailedToCreateCredential(nestedLocation, e); } }; break; } default: throw reader.unexpectedElement(); } } else if (tag == END_ELEMENT) { final ExceptionSupplier finalKeyStoreCredential = keyStoreCredential; final String finalKeyStoreName = keyStoreName; final String finalAlias = alias; return () -> { try { final ExceptionSupplier keyStoreSupplier = keyStoresMap.get(finalKeyStoreName); if (keyStoreSupplier == null) { throw xmlLog.xmlUnknownKeyStoreSpecified(location); } char[] password = keyStoreCredentialToPassword(finalKeyStoreCredential, providers); final KeyStore.ProtectionParameter protectionParameter = password != null ? new KeyStore.PasswordProtection(password) : null; if (finalAlias != null) { KeyStore.Entry finalEntry = keyStoreSupplier.get().getEntry(finalAlias, protectionParameter == null ? null : protectionParameter); if (finalEntry == null) { throw xmlLog.keyStoreEntryMissing(location, finalAlias); } return finalEntry; } else { // allow to retrieve entry without providing alias only if keystore includes one and only entry. if (keyStoreSupplier.get().size() > 1) { throw xmlLog.missingAlias(location); } else if (keyStoreSupplier.get().aliases().hasMoreElements()) { String firstAlias = keyStoreSupplier.get().aliases().nextElement(); KeyStore.Entry finalEntry = keyStoreSupplier.get().getEntry(firstAlias, protectionParameter == null ? null : protectionParameter); if (finalEntry == null) { throw xmlLog.keyStoreEntryMissing(location, firstAlias); } return finalEntry; } else { return null; } } } catch (GeneralSecurityException e) { throw xmlLog.xmlFailedToLoadKeyStoreData(location, e); } }; } else { throw reader.unexpectedContent(); } } throw reader.unexpectedDocumentEnd(); } private static char[] keyStoreCredentialToPassword(ExceptionSupplier keyStoreCredential, Supplier providers) throws GeneralSecurityException, ConfigXMLParseException { final KeyStore.Entry entry = keyStoreCredential == null ? null : keyStoreCredential.get(); if (entry instanceof PasswordEntry) { Password password = ((PasswordEntry) entry).getPassword(); final PasswordFactory passwordFactory = PasswordFactory.getInstance(password.getAlgorithm(), providers); password = passwordFactory.translate(password); final ClearPasswordSpec spec = passwordFactory.getKeySpec(password, ClearPasswordSpec.class); return spec.getEncodedPassword(); } else if (entry instanceof KeyStore.SecretKeyEntry) { final SecretKey secretKey = ((KeyStore.SecretKeyEntry) entry).getSecretKey(); final SecretKeyFactory instance = SecretKeyFactory.getInstance(secretKey.getAlgorithm()); final SecretKeySpec keySpec = (SecretKeySpec) instance.getKeySpec(secretKey, SecretKeySpec.class); final byte[] encoded = keySpec.getEncoded(); return encoded == null ? null : new String(encoded, StandardCharsets.UTF_8).toCharArray(); } else { return null; } } /** * Parse an XML element of type {@code trust-store-ref-type} from an XML reader. * * @param reader the XML stream reader * @param keyStoresMap the map of key stores to use * @return the key store entry factory * @throws ConfigXMLParseException if the resource failed to be parsed */ static ExceptionSupplier parseTrustStoreRefType(ConfigurationXMLStreamReader reader, final Map> keyStoresMap) throws ConfigXMLParseException { final int attributeCount = reader.getAttributeCount(); final XMLLocation location = reader.getLocation(); String keyStoreName = null; for (int i = 0; i < attributeCount; i ++) { checkAttributeNamespace(reader, i); switch (reader.getAttributeLocalName(i)) { case "key-store-name": { if (keyStoreName != null) throw reader.unexpectedAttribute(i); keyStoreName = reader.getAttributeValueResolved(i); break; } default: throw reader.unexpectedElement(); } } if (keyStoreName == null) { throw missingAttribute(reader, "key-store-name"); } while (reader.hasNext()) { final int tag = reader.nextTag(); if (tag == START_ELEMENT) { throw reader.unexpectedElement(); } else if (tag == END_ELEMENT) { final ExceptionSupplier keyStoreSupplier = keyStoresMap.get(keyStoreName); if (keyStoreSupplier == null) { throw xmlLog.xmlUnknownKeyStoreSpecified(location); } return keyStoreSupplier; } else { throw reader.unexpectedContent(); } } throw reader.unexpectedDocumentEnd(); } static ExceptionSupplier parseCredentialStoreRefType(ConfigurationXMLStreamReader reader, final Map> credentialStoresMap) throws ConfigXMLParseException { final int attributeCount = reader.getAttributeCount(); String storeName = null; String alias = null; String clearText = null; for (int i = 0; i < attributeCount; i ++) { checkAttributeNamespace(reader, i); switch (reader.getAttributeLocalName(i)) { case "store": { if (storeName != null) throw reader.unexpectedAttribute(i); storeName = reader.getAttributeValueResolved(i); break; } case "alias": { if (alias != null) throw reader.unexpectedAttribute(i); alias = reader.getAttributeValueResolved(i); break; } case "clear-text": { if (clearText != null) throw reader.unexpectedAttribute(i); clearText = reader.getAttributeValueResolved(i); break; } default: throw reader.unexpectedAttribute(i); } } if (! reader.hasNext()) throw reader.unexpectedDocumentEnd(); if (reader.nextTag() != END_ELEMENT) throw reader.unexpectedContent(); final XMLLocation finalLocation = reader.getLocation(); final String finalStoreName = storeName; final String finalClearText = clearText; final String finalAlias = alias; if (finalStoreName == null && finalClearText == null) throw xmlLog.xmlInvalidCredentialStoreRef(reader.getLocation()); if (finalStoreName != null && finalAlias == null) throw missingAttribute(reader, "alias"); return () -> { if (finalStoreName != null) { final ExceptionSupplier supplier = credentialStoresMap.get(finalStoreName); if (supplier == null) { throw xmlLog.xmlCredentialStoreNameNotDefined(finalLocation, finalStoreName); } final CredentialStore credentialStore = supplier.get(); return new CredentialStoreCredentialSource(credentialStore, finalAlias); } else { final PasswordCredential passwordCredential = new PasswordCredential(ClearPassword.createRaw(ClearPassword.ALGORITHM_CLEAR, finalClearText.toCharArray())); return IdentityCredentials.NONE.withCredential(passwordCredential); } }; } /** * Parse an XML element of type {@code credential-stores-type} from an XML reader. * * @param reader the XML stream reader * @param xmlVersion the version of parsed XML * @param keyStoresMap the key stores map * @param credentialStoresMap the map of credential stores to use @throws ConfigXMLParseException if the resource failed to be parsed */ private static void parseCredentialStoresType(ConfigurationXMLStreamReader reader, final Version xmlVersion, Map> keyStoresMap, 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, keyStoresMap, 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 keyStoresMap the key stores map * @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 Version xmlVersion, Map> keyStoresMap, 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; DeferredSupplier providersSupplier = new 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, keyStoresMap, 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 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); switch (reader.getAttributeLocalName(i)) { case "name": { name = reader.getAttributeValueResolved(i); break; } default: 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(); } /** * Parse an XML element of type {@code resource-type} from an XML reader. * * @param reader the XML stream reader * @param xmlVersion the version of the XML being parsed * @return An {@code ExceptionSupplier} for the referenced resource * @throws ConfigXMLParseException if the resource failed to be parsed */ static ExceptionSupplier parseResourceType(ConfigurationXMLStreamReader reader, Version xmlVersion) throws ConfigXMLParseException { final int attributeCount = reader.getAttributeCount(); String name = null; String module = null; for (int i = 0; i < attributeCount; i ++) { checkAttributeNamespace(reader, i); switch (reader.getAttributeLocalName(i)) { case "name": { name = reader.getAttributeValueResolved(i); break; } case "module-name": { if (xmlVersion.isAtLeast(Version.VERSION_1_1)) { module = reader.getAttributeValueResolved(i); break; } } default: throw reader.unexpectedAttribute(i); } } if (name == null) { throw missingAttribute(reader, "name"); } if (reader.hasNext()) { final int tag = reader.nextTag(); if (tag == START_ELEMENT) { throw reader.unexpectedElement(); } else if (tag == END_ELEMENT) { final String resourceName = name; final ClassLoader classLoader = module != null ? ModuleLoader.getClassLoaderFromModule(reader, module) : Thread.currentThread().getContextClassLoader(); return () -> { ClassLoader actualClassLoader = classLoader != null ? classLoader : ElytronXmlParser.class.getClassLoader(); final InputStream stream = actualClassLoader.getResourceAsStream(resourceName); if (stream == null) throw new FileNotFoundException(resourceName); return stream; }; } else { throw reader.unexpectedContent(); } } throw reader.unexpectedDocumentEnd(); } /** * Parse an XML element of type {@code port-type} from an XML reader. * * @param reader the XML stream reader * @return the port number (1-65535 inclusive) * @throws ConfigXMLParseException if the resource failed to be parsed */ static int parsePortType(ConfigurationXMLStreamReader reader) throws ConfigXMLParseException { final int attributeCount = reader.getAttributeCount(); int number = -1; for (int i = 0; i < attributeCount; i ++) { checkAttributeNamespace(reader, i); switch (reader.getAttributeLocalName(i)) { case "number": { String s = reader.getAttributeValueResolved(i); try { number = Integer.parseInt(s); } catch (NumberFormatException ignored) { throw invalidPortNumber(reader, i); } if (number < 1 || number > 65535) { throw invalidPortNumber(reader, i); } break; } default: throw reader.unexpectedAttribute(i); } } if (number == -1) { throw missingAttribute(reader, "number"); } if (reader.hasNext()) { final int tag = reader.nextTag(); if (tag == START_ELEMENT) { throw reader.unexpectedElement(); } else if (tag == END_ELEMENT) { return number; } else { throw reader.unexpectedContent(); } } throw reader.unexpectedDocumentEnd(); } /** * Parse an XML element of type {@code regex-substitution-type} from an XML reader. * * @param reader the XML stream reader * @return the regular expression based name rewriter * @throws ConfigXMLParseException if the resource failed to be parsed */ static NameRewriter parseRegexSubstitutionType(ConfigurationXMLStreamReader reader) throws ConfigXMLParseException { final int attributeCount = reader.getAttributeCount(); Pattern pattern = null; String replacement = null; for (int i = 0; i < attributeCount; i ++) { checkAttributeNamespace(reader, i); switch (reader.getAttributeLocalName(i)) { case "pattern": { pattern = Pattern.compile(reader.getAttributeValueResolved(i)); break; } case "replacement": { replacement = reader.getAttributeValueResolved(i); break; } default: throw reader.unexpectedAttribute(i); } } if (pattern == null) { throw missingAttribute(reader, "pattern"); } if (replacement == null) { throw missingAttribute(reader, "replacement"); } if (reader.hasNext()) { final int tag = reader.nextTag(); if (tag == START_ELEMENT) { throw reader.unexpectedElement(); } else if (tag == END_ELEMENT) { return new RegexNameRewriter(pattern, replacement, true); } else { throw reader.unexpectedContent(); } } throw reader.unexpectedDocumentEnd(); } /** * Parse an XML element of type {@code names-type} from an XML reader. * * @param reader the XML stream reader * @return the array of parsed names * @throws ConfigXMLParseException if the resource failed to be parsed */ static String[] parseNamesType(ConfigurationXMLStreamReader reader) throws ConfigXMLParseException { final int attributeCount = reader.getAttributeCount(); String[] names = null; for (int i = 0; i < attributeCount; i ++) { checkAttributeNamespace(reader, i); switch (reader.getAttributeLocalName(i)) { case "names": { String s = reader.getAttributeValueResolved(i); names = s.trim().split("\\s+"); break; } default: throw reader.unexpectedAttribute(i); } } if (names == null) { throw missingAttribute(reader, "names"); } if (reader.hasNext()) { final int tag = reader.nextTag(); if (tag == START_ELEMENT) { throw reader.unexpectedElement(); } else if (tag == END_ELEMENT) { return names; } else { throw reader.unexpectedContent(); } } throw reader.unexpectedDocumentEnd(); } /** * Parse an XML element of type {@code uri-type} from an XML reader. * * @param reader the XML stream reader * @return the parsed URI * @throws ConfigXMLParseException if the resource failed to be parsed */ static URI parseUriType(ConfigurationXMLStreamReader reader) throws ConfigXMLParseException { final int attributeCount = reader.getAttributeCount(); URI uri = null; for (int i = 0; i < attributeCount; i ++) { checkAttributeNamespace(reader, i); switch (reader.getAttributeLocalName(i)) { case "uri": { uri = reader.getURIAttributeValueResolved(i); break; } default: throw reader.unexpectedAttribute(i); } } if (uri == null) { throw missingAttribute(reader, "uri"); } if (reader.hasNext()) { final int tag = reader.nextTag(); if (tag == START_ELEMENT) { throw reader.unexpectedElement(); } else if (tag == END_ELEMENT) { return uri; } else { throw reader.unexpectedContent(); } } throw reader.unexpectedDocumentEnd(); } static SaslMechanismSelector parseSaslMechanismSelectorType(ConfigurationXMLStreamReader reader) throws ConfigXMLParseException { final int attributeCount = reader.getAttributeCount(); SaslMechanismSelector selector = null; for (int i = 0; i < attributeCount; i ++) { checkAttributeNamespace(reader, i); switch (reader.getAttributeLocalName(i)) { case "selector": { selector = SaslMechanismSelector.fromString(reader.getAttributeValueResolved(i)); break; } default: throw reader.unexpectedAttribute(i); } } if (selector == null) { throw missingAttribute(reader, "selector"); } if (reader.hasNext()) { final int tag = reader.nextTag(); if (tag == START_ELEMENT) { throw reader.unexpectedElement(); } else if (tag == END_ELEMENT) { return selector; } else { throw reader.unexpectedContent(); } } throw reader.unexpectedDocumentEnd(); } /** * Parse an XML element of type {@code ssl-cipher-selector-type} from an XML reader. * * @param reader the XML stream reader * @return the parsed cipher suite selector * @throws ConfigXMLParseException if the resource failed to be parsed */ static CipherSuiteSelector parseCipherSuiteSelectorType(ConfigurationXMLStreamReader reader, Version xmlVersion) throws ConfigXMLParseException { final int attributeCount = reader.getAttributeCount(); CipherSuiteSelector selector = null; CipherSuiteSelector names = null; for (int i = 0; i < attributeCount; i ++) { checkAttributeNamespace(reader, i); switch (reader.getAttributeLocalName(i)) { case "selector": { selector = CipherSuiteSelector.fromString(reader.getAttributeValueResolved(i)); break; } case "names": { if (xmlVersion.isAtLeast(Version.VERSION_1_5)) { names = CipherSuiteSelector.fromNamesString(reader.getAttributeValueResolved(i)); break; } } default: throw reader.unexpectedAttribute(i); } } if (selector == null && ! xmlVersion.isAtLeast(Version.VERSION_1_5)) { throw missingAttribute(reader, "selector"); } else if (selector == null && names == null && xmlVersion.isAtLeast(Version.VERSION_1_5)) { throw xmlLog.atLeastOneCipherSuiteAttributeMustBeProvided("selector", "names"); } if (selector == null) { selector = CipherSuiteSelector.openSslDefault(); // default cipher suites pre TLSv1.3 } // ELY-1917: The following lines should be added back when once we are ready to enable TLS 1.3 by default /*if ((names == null) && xmlVersion.isAtLeast(Version.VERSION_1_5)) { names = CipherSuiteSelector.openSslDefaultCipherSuites(); // default cipher suites for TLSv1.3 }*/ if (reader.hasNext()) { final int tag = reader.nextTag(); if (tag == START_ELEMENT) { throw reader.unexpectedElement(); } else if (tag == END_ELEMENT) { return CipherSuiteSelector.aggregate(names, selector); } else { throw reader.unexpectedContent(); } } throw reader.unexpectedDocumentEnd(); } /** * Parse an XML element of type {@code names} which yields a protocol selector from an XML reader. * * @param reader the XML stream reader * @return the parsed protocol selector * @throws ConfigXMLParseException if the resource failed to be parsed */ static ProtocolSelector parseProtocolSelectorNamesType(ConfigurationXMLStreamReader reader) throws ConfigXMLParseException { ProtocolSelector selector = ProtocolSelector.empty(); for (String name : parseNamesType(reader)) { selector = selector.add(name); } return selector; } /** * Parse an XML element of type {@code module-ref-type} from an XML reader. * * @param reader the XML stream reader * @return the corresponding module name * @throws ConfigXMLParseException if the resource failed to be parsed or the module is not found */ static String parseModuleRefType(ConfigurationXMLStreamReader reader) throws ConfigXMLParseException { final int attributeCount = reader.getAttributeCount(); String moduleName = null; for (int i = 0; i < attributeCount; i ++) { checkAttributeNamespace(reader, i); switch (reader.getAttributeLocalName(i)) { case "module-name": { moduleName = reader.getAttributeValueResolved(i); break; } default: throw reader.unexpectedAttribute(i); } } if (reader.hasNext()) { final int tag = reader.nextTag(); if (tag == START_ELEMENT) { throw reader.unexpectedElement(); } else if (tag == END_ELEMENT) { return moduleName; } 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); switch (reader.getAttributeLocalName(i)) { case "password": { password = reader.getAttributeValueResolved(i).toCharArray(); break; } default: 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(); } /** * Parse an XML element of type {@code masked-password-type} from an XML reader. * * @param reader the XML stream reader * @return a {@link MaskedPassword} supplier * @throws ConfigXMLParseException if the resource failed to be parsed or the module is not found */ static ExceptionSupplier parseMaskedPassword(ConfigurationXMLStreamReader reader, Supplier providers) throws ConfigXMLParseException { final int attributeCount = reader.getAttributeCount(); String algorithm = MaskedPassword.ALGORITHM_MASKED_MD5_DES; char[] initialKeyMaterial = "somearbitrarycrazystringthatdoesnotmatter".toCharArray(); int iterationCount = 0; byte[] salt = null; byte[] maskedPasswordBytes = null; byte[] initializationVector = null; for (int i = 0; i < attributeCount; i ++) { checkAttributeNamespace(reader, i); switch (reader.getAttributeLocalName(i)) { case "algorithm": algorithm = reader.getAttributeValueResolved(i); break; case "key-material": initialKeyMaterial = reader.getAttributeValueResolved(i).toCharArray(); break; case "iteration-count": iterationCount = reader.getIntAttributeValueResolved(i, 1, Integer.MAX_VALUE); break; case "salt": salt = CodePointIterator.ofString(reader.getAttributeValueResolved(i)).asUtf8().drain(); break; case "masked-password": maskedPasswordBytes = CodePointIterator.ofString(reader.getAttributeValueResolved(i)).base64Decode().drain(); break; case "initialization-vector": initializationVector = CodePointIterator.ofString(reader.getAttributeValueResolved(i)).base64Decode().drain(); break; default: throw reader.unexpectedAttribute(i); } } if (iterationCount == 0) throw missingAttribute(reader, "iteration-count"); if (salt == null) throw missingAttribute(reader, "salt"); if (maskedPasswordBytes == null) throw missingAttribute(reader, "masked-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(); if (!MaskedPassword.isMaskedAlgorithm(algorithm)) { throw xmlLog.xmlUnsupportedAlgorithmForType(location, algorithm, MaskedPassword.class.getSimpleName()); } final String finalAlgorithm = algorithm; final MaskedPasswordSpec spec = new MaskedPasswordSpec(initialKeyMaterial, iterationCount, salt, maskedPasswordBytes, initializationVector); return () -> { try { PasswordFactory factory = PasswordFactory.getInstance(finalAlgorithm, providers); return Assert.assertNotNull(factory.generatePassword(spec).castAs(MaskedPassword.class)); } catch (InvalidKeySpecException | NoSuchAlgorithmException cause) { throw xmlLog.xmlFailedToCreateCredential(location, cause); } }; } else { throw reader.unexpectedContent(); } } throw reader.unexpectedDocumentEnd(); } static Map parsePropertiesType(ConfigurationXMLStreamReader reader, final Version xmlVersion) throws ConfigXMLParseException { if (reader.getAttributeCount() > 0) { throw reader.unexpectedAttribute(0); } Map propertiesMap = new HashMap<>(); while (reader.hasNext()) { final int tag = reader.nextTag(); if (tag == START_ELEMENT) { checkElementNamespace(reader, xmlVersion); switch (reader.getLocalName()) { case "property": final int attributeCount = reader.getAttributeCount(); String key = null; String value = null; for (int i = 0; i < attributeCount; i++) { checkAttributeNamespace(reader, i); switch (reader.getAttributeLocalName(i)) { case "key": if (key != null) throw reader.unexpectedAttribute(i); key = reader.getAttributeValueResolved(i); break; case "value": if (value != null) throw reader.unexpectedAttribute(i); value = reader.getAttributeValueResolved(i); break; default: throw reader.unexpectedAttribute(i); } } if (key == null) { throw missingAttribute(reader, "key"); } if (value == null) { throw missingAttribute(reader, "value"); } propertiesMap.put(key, value); if (reader.hasNext()) { final int innerTag = reader.nextTag(); if (innerTag == START_ELEMENT) { throw reader.unexpectedElement(); } else if (innerTag == END_ELEMENT) { } else { throw reader.unexpectedContent(); } } else { throw reader.unexpectedDocumentEnd(); } break; default: throw reader.unexpectedElement(); } } else if (tag == END_ELEMENT) { return propertiesMap; } else { throw reader.unexpectedContent(); } } throw reader.unexpectedDocumentEnd(); } /** * Parse an XML element of type {@code bearer-token-type} from an XML reader. * * @param reader the XML stream reader * @throws ConfigXMLParseException if the resource failed to be parsed */ static BearerTokenCredential parseBearerTokenType(ConfigurationXMLStreamReader reader) throws ConfigXMLParseException { String value = requireSingleAttribute(reader, "value"); while (reader.hasNext()) { final int tag = reader.nextTag(); if (tag == START_ELEMENT) { throw reader.unexpectedElement(); } else if (tag == END_ELEMENT) { return new BearerTokenCredential(value); } else { throw reader.unexpectedContent(); } } throw reader.unexpectedDocumentEnd(); } /** * Parse an XML element of type {@code oauth2-bearer-token-type} from an XML reader. * * @param reader the XML stream reader * @param xmlVersion the version of parsed XML * @throws ConfigXMLParseException if the resource failed to be parsed */ static ExceptionSupplier parseOAuth2BearerTokenType(ConfigurationXMLStreamReader reader, final Map> credentialStoresMap, final Version xmlVersion) throws ConfigXMLParseException { URI tokenEndpointUri = requireSingleURIAttribute(reader, "token-endpoint-uri"); ExceptionSupplier builderSupplier = null; DeferredSupplier providersSupplier = new DeferredSupplier<>(ProviderFactory.getElytronProviderSupplier(WildFlyElytronPasswordProvider.class.getClassLoader())); builderSupplier = () -> { try { return OAuth2CredentialSource.builder(tokenEndpointUri.toURL()); } catch (MalformedURLException e) { throw xmlLog.xmlInvalidUrl(tokenEndpointUri.toString()); } }; while (reader.hasNext()) { final int tag = reader.nextTag(); if (tag == START_ELEMENT) { checkElementNamespace(reader, xmlVersion); switch (reader.getLocalName()) { case "resource-owner-credentials": { builderSupplier = parseOAuth2ResourceOwnerCredentials(reader, builderSupplier, credentialStoresMap, xmlVersion); break; } case "client-credentials": { builderSupplier = parseOAuth2ClientCredentials(reader, builderSupplier, credentialStoresMap, xmlVersion); break; } case "masked-resource-owner-credentials": { if (!xmlVersion.isAtLeast(Version.VERSION_1_4)) { throw reader.unexpectedElement(); } builderSupplier = parseOAuth2MaskedResourceOwnerCredentials(reader, builderSupplier, xmlVersion, providersSupplier); break; } case "masked-client-credentials": { if (!xmlVersion.isAtLeast(Version.VERSION_1_4)) { throw reader.unexpectedElement(); } builderSupplier = parseOAuth2MaskedClientCredentials(reader, builderSupplier, xmlVersion, providersSupplier); break; } default: throw reader.unexpectedElement(); } } else if (tag == END_ELEMENT) { final ExceptionSupplier finalBuilderSupplier = builderSupplier; return () -> finalBuilderSupplier.get().build(); } else { throw reader.unexpectedContent(); } } throw reader.unexpectedDocumentEnd(); } /** * Parse an XML element of type {@code oauth2-bearer-token-type} from an XML reader. * * @param reader the XML stream reader * @param builderSupplier the builder supplier * @throws ConfigXMLParseException if the resource failed to be parsed */ static ExceptionSupplier parseOAuth2ResourceOwnerCredentials(ConfigurationXMLStreamReader reader, final ExceptionSupplier builderSupplier, final Map> credentialStoresMap, Version xmlVersion) throws ConfigXMLParseException { final int attributeCount = reader.getAttributeCount(); ExceptionSupplier credentialSourceSupplier = null; XMLLocation nestedLocation = null; String userName = null; String password = null; for (int i = 0; i < attributeCount; i ++) { checkAttributeNamespace(reader, i); switch (reader.getAttributeLocalName(i)) { case "name": { if (userName != null) throw reader.unexpectedAttribute(i); userName = reader.getAttributeValueResolved(i); break; } case "password": { if (password != null) throw reader.unexpectedAttribute(i); password = reader.getAttributeValueResolved(i); break; } default: throw reader.unexpectedAttribute(i); } } if (userName == null) throw reader.missingRequiredAttribute(xmlVersion.namespace, "name"); while (reader.hasNext()) { final int tag = reader.nextTag(); if (tag == START_ELEMENT) { checkElementNamespace(reader, xmlVersion); if (!xmlVersion.isAtLeast(Version.VERSION_1_1)) { throw reader.unexpectedElement(); } if ("credential-store-reference".equals(reader.getLocalName())) { if (password != null) { throw reader.unexpectedElement(); } if (credentialSourceSupplier != null) { // must not throw because compatibility xmlLog.trace("Multiple credential-store-references in resource-owner-credentials - only the last one used!"); } nestedLocation = reader.getLocation(); credentialSourceSupplier = parseCredentialStoreRefType(reader, credentialStoresMap); } else { throw reader.unexpectedElement(); } } else if (tag == END_ELEMENT) { final String finalUserName = userName; if (password != null) { final String finalPassword = password; return () -> builderSupplier.get().useResourceOwnerPassword(finalUserName, finalPassword); } if (credentialSourceSupplier != null) { final XMLLocation finalLocation = nestedLocation; final ExceptionSupplier finalCredentialSourceSupplier = credentialSourceSupplier; return () -> { try { PasswordCredential passwordCredential = finalCredentialSourceSupplier.get().getCredential(PasswordCredential.class); if (passwordCredential == null) { throw new ConfigXMLParseException(xmlLog.couldNotObtainCredential(), reader); } char[] pass = passwordCredential.getPassword().castAndApply(ClearPassword.class, ClearPassword::getPassword); if (pass == null) { throw new ConfigXMLParseException(xmlLog.couldNotObtainCredential(), reader); } String finalPassword = String.valueOf(pass); return builderSupplier.get().useResourceOwnerPassword(finalUserName, finalPassword); } catch (IOException e) { throw xmlLog.xmlFailedToCreateCredential(finalLocation, e); } }; } throw reader.missingRequiredAttribute(xmlVersion.namespace, "password"); } else { throw reader.unexpectedContent(); } } throw reader.unexpectedDocumentEnd(); } /** * Parse an XML element of type {@code oauth2-client-credentials-type} from an XML reader. * * @param reader the XML stream reader * @param builderSupplier the builder supplier * @throws ConfigXMLParseException if the resource failed to be parsed */ static ExceptionSupplier parseOAuth2ClientCredentials(ConfigurationXMLStreamReader reader, final ExceptionSupplier builderSupplier, final Map> credentialStoresMap, Version xmlVersion) throws ConfigXMLParseException { ExceptionSupplier credentialSourceSupplier = null; XMLLocation nestedLocation = null; String id = null; String secret = null; for (int i = 0; i < reader.getAttributeCount(); i ++) { checkAttributeNamespace(reader, i); switch (reader.getAttributeLocalName(i)) { case "client-id": { if (id != null) throw reader.unexpectedAttribute(i); id = reader.getAttributeValueResolved(i); break; } case "client-secret": { if (secret != null) throw reader.unexpectedAttribute(i); secret = reader.getAttributeValueResolved(i); break; } default: throw reader.unexpectedAttribute(i); } } while (reader.hasNext()) { final int tag = reader.nextTag(); if (tag == START_ELEMENT) { checkElementNamespace(reader, xmlVersion); if (!xmlVersion.isAtLeast(Version.VERSION_1_1)) { throw reader.unexpectedElement(); } if ("credential-store-reference".equals(reader.getLocalName())) { if (secret != null) { throw reader.unexpectedElement(); } if (credentialSourceSupplier != null) { // must not throw because compatibility xmlLog.trace("Multiple credential-store-references in client-credentials - only the last one used!"); } nestedLocation = reader.getLocation(); credentialSourceSupplier = parseCredentialStoreRefType(reader, credentialStoresMap); } else { throw reader.unexpectedElement(); } } else if (tag == END_ELEMENT) { if (id == null) throw reader.unexpectedContent(); final String finalId = id; if (secret != null) { final String finalSecret = secret; return () -> builderSupplier.get().clientCredentials(finalId, finalSecret); } if (credentialSourceSupplier != null) { final XMLLocation finalLocation = nestedLocation; final ExceptionSupplier finalCredentialSourceSupplier = credentialSourceSupplier; return () -> { try { PasswordCredential passwordCredential = finalCredentialSourceSupplier.get().getCredential(PasswordCredential.class); if (passwordCredential == null) { throw new ConfigXMLParseException(xmlLog.couldNotObtainCredential(), reader); } char[] pass = passwordCredential.getPassword().castAndApply(ClearPassword.class, ClearPassword::getPassword); if (pass == null) { throw new ConfigXMLParseException(xmlLog.couldNotObtainCredential(), reader); } String finalPassword = String.valueOf(pass); return builderSupplier.get().clientCredentials(finalId, finalPassword); } catch (IOException e) { throw xmlLog.xmlFailedToCreateCredential(finalLocation, e); } }; } throw reader.unexpectedContent(); } else { throw reader.unexpectedContent(); } } throw reader.unexpectedDocumentEnd(); } /** * Parse an XML element of type {@code oauth2-bearer-token-type} from an XML reader. * * @param reader the XML stream reader * @param builderSupplier the builder supplier * @throws ConfigXMLParseException if the resource failed to be parsed */ static ExceptionSupplier parseOAuth2MaskedResourceOwnerCredentials(ConfigurationXMLStreamReader reader, final ExceptionSupplier builderSupplier, Version xmlVersion, Supplier providers) throws ConfigXMLParseException { final int attributeCount = reader.getAttributeCount(); XMLLocation nestedLocation = null; String userName = null; String password = null; for (int i = 0; i < attributeCount; i ++) { checkAttributeNamespace(reader, i); switch (reader.getAttributeLocalName(i)) { case "name": { if (userName != null) throw reader.unexpectedAttribute(i); userName = reader.getAttributeValueResolved(i); break; } default: throw reader.unexpectedAttribute(i); } } if (userName == null) throw reader.missingRequiredAttribute(xmlVersion.namespace, "name"); while (reader.hasNext()) { final int tag = reader.nextTag(); if (tag == START_ELEMENT) { checkElementNamespace(reader, xmlVersion); if ("masked-password".equals(reader.getLocalName())) { if (password != null) { throw reader.unexpectedElement(); } nestedLocation = reader.getLocation(); Password maskedPassword = parseMaskedPassword(reader, providers).get(); try { final PasswordFactory passwordFactory = PasswordFactory.getInstance(maskedPassword.getAlgorithm(), providers); final ClearPasswordSpec spec = passwordFactory.getKeySpec(maskedPassword, ClearPasswordSpec.class); password = String.valueOf(spec.getEncodedPassword()); } catch (InvalidKeySpecException | NoSuchAlgorithmException cause) { throw xmlLog.xmlFailedToCreateCredential(nestedLocation, cause); } } else { throw reader.unexpectedElement(); } } else if (tag == END_ELEMENT) { final String finalUserName = userName; if (password != null) { final String finalPassword = password; return () -> builderSupplier.get().useResourceOwnerPassword(finalUserName, finalPassword); } throw reader.missingRequiredAttribute(xmlVersion.namespace, "password"); } else { throw reader.unexpectedContent(); } } throw reader.unexpectedDocumentEnd(); } /** * Parse an XML element of type {@code oauth2-client-credentials-type} from an XML reader. * * @param reader the XML stream reader * @param builderSupplier the builder supplier * @throws ConfigXMLParseException if the resource failed to be parsed */ static ExceptionSupplier parseOAuth2MaskedClientCredentials(ConfigurationXMLStreamReader reader, final ExceptionSupplier builderSupplier, Version xmlVersion,Supplier providers) throws ConfigXMLParseException { XMLLocation nestedLocation = null; String id = null; String secret = null; for (int i = 0; i < reader.getAttributeCount(); i ++) { checkAttributeNamespace(reader, i); if ("client-id".equals(reader.getAttributeLocalName(i))) { if (id != null) throw reader.unexpectedAttribute(i); id = reader.getAttributeValueResolved(i); break; } else { throw reader.unexpectedAttribute(i); } } while (reader.hasNext()) { final int tag = reader.nextTag(); if (tag == START_ELEMENT) { checkElementNamespace(reader, xmlVersion); if ("masked-client-secret".equals(reader.getLocalName())) { if (secret != null) { throw reader.unexpectedElement(); } nestedLocation = reader.getLocation(); Password maskedPassword = parseMaskedPassword(reader, providers).get(); try { final PasswordFactory passwordFactory = PasswordFactory.getInstance(maskedPassword.getAlgorithm(), providers); final ClearPasswordSpec spec = passwordFactory.getKeySpec(maskedPassword, ClearPasswordSpec.class); secret = String.valueOf(spec.getEncodedPassword()); } catch (InvalidKeySpecException | NoSuchAlgorithmException cause) { throw xmlLog.xmlFailedToCreateCredential(nestedLocation, cause); } } else { throw reader.unexpectedElement(); } } else if (tag == END_ELEMENT) { if (id == null) throw reader.unexpectedContent(); final String finalId = id; if (secret != null) { final String finalSecret = secret; return () -> builderSupplier.get().clientCredentials(finalId, finalSecret); } throw reader.unexpectedContent(); } else { throw reader.unexpectedContent(); } } throw reader.unexpectedDocumentEnd(); } /** * Parse an XML element of type {@code local-kerberos-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 CredentialSource parseLocalKerberos(ConfigurationXMLStreamReader reader) throws ConfigXMLParseException { final int attributeCount = reader.getAttributeCount(); List mechanismOids = new LinkedList<>(); for (int i = 0; i < attributeCount; i ++) { checkAttributeNamespace(reader, i); switch (reader.getAttributeLocalName(i)) { case "mechanism-names": { for (String name : reader.getListAttributeValueAsArrayResolved(i)) { String oid = OidsUtil.attributeNameToOid(OidsUtil.Category.GSS, name); if (oid == null) { throw xmlLog.xmlInvalidGssMechanismName(reader, name); } try { mechanismOids.add(new Oid(oid)); } catch (GSSException e) { throw xmlLog.xmlGssMechanismOidConversionFailed(reader, oid, e); } } break; } case "mechanism-oids": { for (String oid : reader.getListAttributeValueAsArrayResolved(i)) { try { mechanismOids.add(new Oid(oid)); } catch (GSSException e) { throw xmlLog.xmlGssMechanismOidConversionFailed(reader, oid, e); } } break; } default: throw reader.unexpectedAttribute(i); } } if (mechanismOids.size() == 0) { mechanismOids.add(GSSCredentialSecurityFactory.KERBEROS_V5); mechanismOids.add(GSSCredentialSecurityFactory.SPNEGO); } if (reader.hasNext()) { final int tag = reader.nextTag(); if (tag == START_ELEMENT) { throw reader.unexpectedElement(); } else if (tag == END_ELEMENT) { return LocalKerberosCredentialSource.builder().setMechanismOids(mechanismOids.toArray(new Oid[mechanismOids.size()])).build(); } else { throw reader.unexpectedContent(); } } throw reader.unexpectedDocumentEnd(); } // util 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 void checkElementNamespace(final ConfigurationXMLStreamReader reader, final Version xmlVersion) throws ConfigXMLParseException { if (! xmlVersion.namespace.equals(reader.getNamespaceURI())) { throw reader.unexpectedElement(); } } 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; private final String type; private final XMLLocation location; KeyStoreCreateFactory(final Supplier providers, final String providerName, final String type, final XMLLocation location) { this.providerName = providerName; this.providers = providers; this.type = type; this.location = location; } public KeyStore get() throws ConfigXMLParseException { Provider provider = findProvider(providers, providerName, KeyStore.class, type); if (provider == null) { throw xmlLog.xmlUnableToIdentifyProvider(location, providerName, "KeyStore", type); } try { return KeyStore.getInstance(type, provider); } catch (GeneralSecurityException e) { throw xmlLog.xmlFailedToCreateKeyStore(location, e); } } } static final class UnknownTypeFileKeyStoreFactory implements ExceptionSupplier { private final String providerName; private final Supplier providers; private final XMLLocation location; protected final ExceptionSupplier passwordFactory; private final String fileName; private final ExceptionSupplier resourceSupplier; private final URI uri; UnknownTypeFileKeyStoreFactory(final Supplier providers, final String providerName, final ExceptionSupplier passwordFactory, final String fileName, final ExceptionSupplier resourceSupplier, final URI uri, final XMLLocation location) { this.providerName = providerName; this.providers = providers; this.location = location; this.passwordFactory = passwordFactory; this.fileName = fileName; this.resourceSupplier = resourceSupplier; this.uri = uri; } @Override public KeyStore get() throws ConfigXMLParseException { KeyStore keyStore = null; FileInputStream fin = null; try { if (fileName != null) { fin = new FileInputStream(fileName); } else if (resourceSupplier != null) { InputStream is = resourceSupplier.get(); if (is instanceof FileInputStream) { fin = (FileInputStream) is; } } else { fin = new FileInputStream(uri.toURL().getFile()); } keyStore = KeyStoreUtil.loadKeyStore(providers, providerName, fin, fileName, passwordFactory.get()); } catch (Exception e) { throw xmlLog.xmlFailedToCreateKeyStore(location, e); } finally { safeClose(fin); } return keyStore; } private static void safeClose(Closeable c) { if (c != null) { try { c.close(); } catch (Throwable ignored) {} } } } static final class PasswordKeyStoreFactory implements ExceptionSupplier { private final ExceptionSupplier delegateFactory; PasswordKeyStoreFactory(final ExceptionSupplier delegateFactory) { this.delegateFactory = delegateFactory; } public KeyStore get() throws ConfigXMLParseException { return new WrappingPasswordKeyStore(delegateFactory.get()); } } abstract static class AbstractLoadingKeyStoreFactory implements ExceptionSupplier { protected final ExceptionSupplier delegateFactory; protected final ExceptionSupplier passwordFactory; protected final XMLLocation location; protected AbstractLoadingKeyStoreFactory(final ExceptionSupplier delegateFactory, final ExceptionSupplier passwordFactory, final XMLLocation location) { this.delegateFactory = delegateFactory; this.passwordFactory = passwordFactory; this.location = location; } public KeyStore get() throws ConfigXMLParseException { try { KeyStore keyStore = delegateFactory.get(); if (passwordFactory == null || passwordFactory.get() == null) { xmlLog.noKeystorePasswordSpecified(location); } try (InputStream fis = createStream()) { keyStore.load(fis, passwordFactory == null ? null : passwordFactory.get()); } return keyStore; } catch (GeneralSecurityException | IOException e) { throw xmlLog.xmlFailedToLoadKeyStoreData(location, e); } } abstract InputStream createStream() throws IOException; } static final class FileLoadingKeyStoreFactory extends AbstractLoadingKeyStoreFactory { private final String fileName; FileLoadingKeyStoreFactory(final ExceptionSupplier delegateFactory, final ExceptionSupplier passwordFactory, final String fileName, final XMLLocation location) { super(delegateFactory, passwordFactory, location); this.fileName = fileName; } InputStream createStream() throws FileNotFoundException { return new FileInputStream(fileName); } } static final class ResourceLoadingKeyStoreFactory extends AbstractLoadingKeyStoreFactory { private final ExceptionSupplier resourceSupplier; ResourceLoadingKeyStoreFactory(final ExceptionSupplier delegateFactory, final ExceptionSupplier passwordFactory, final ExceptionSupplier resourceSupplier, final XMLLocation location) { super(delegateFactory, passwordFactory, location); this.resourceSupplier = resourceSupplier; } InputStream createStream() throws IOException { return resourceSupplier.get(); } } static final class URILoadingKeyStoreFactory extends AbstractLoadingKeyStoreFactory { private final URI uri; URILoadingKeyStoreFactory(final ExceptionSupplier delegateFactory, final ExceptionSupplier passwordFactory, final URI uri, final XMLLocation location) { super(delegateFactory, passwordFactory, location); this.uri = uri; } InputStream createStream() throws IOException { return uri.toURL().openStream(); } } static final class NullLoadingKeyStoreFactory extends AbstractLoadingKeyStoreFactory { NullLoadingKeyStoreFactory(final ExceptionSupplier delegateFactory, final ExceptionSupplier passwordFactory, final XMLLocation location) { super(delegateFactory, passwordFactory, location); } @Override InputStream createStream() throws IOException { return null; } } static final class DeferredSupplier implements Supplier { private volatile Supplier supplier; private T value; DeferredSupplier(Supplier supplier) { checkNotNullParam("supplier", supplier); this.supplier = supplier; } void setSupplier(Supplier supplier) { checkNotNullParam("supplier", supplier); this.supplier = supplier; } @Override public T get() { return supplier.get(); } } }





© 2015 - 2025 Weber Informatics LLC | Privacy Policy