eu.eidas.node.auth.metadata.NodeMetadataFetcher.java Source code

Java tutorial

Introduction

Here is the source code for eu.eidas.node.auth.metadata.NodeMetadataFetcher.java

Source

/*
 * This work is Open Source and licensed by the European Commission under the
 * conditions of the European Public License v1.1
 *
 * (http://www.osor.eu/eupl/european-union-public-licence-eupl-v.1.1);
 *
 * any use of this file implies acceptance of the conditions of this license.
 * 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 eu.eidas.node.auth.metadata;

import eu.eidas.auth.commons.EIDASUtil;
import eu.eidas.auth.engine.metadata.EntityDescriptorContainer;
import eu.eidas.auth.engine.metadata.MetadataFetcherI;
import eu.eidas.auth.engine.metadata.impl.AbstractCachingMetadataFetcher;
import eu.eidas.auth.engine.xml.opensaml.CertificateUtil;
import eu.eidas.engine.exceptions.EIDASSAMLEngineException;
import org.apache.commons.httpclient.params.HttpConnectionParams;
import org.apache.commons.httpclient.protocol.Protocol;
import org.apache.commons.httpclient.protocol.SecureProtocolSocketFactory;
import org.opensaml.DefaultBootstrap;
import org.opensaml.saml2.metadata.EntityDescriptor;
import org.opensaml.ws.soap.client.http.TLSProtocolSocketFactory;
import org.opensaml.xml.security.x509.tls.StrictHostnameVerifier;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.annotation.Nonnull;
import javax.net.ssl.HostnameVerifier;
import javax.net.ssl.SSLException;
import javax.net.ssl.SSLSession;
import javax.net.ssl.SSLSocket;
import javax.net.ssl.X509TrustManager;
import java.io.IOException;
import java.net.InetAddress;
import java.net.Socket;
import java.security.cert.X509Certificate;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

/**
 * The implementation of the {@link MetadataFetcherI} interface for the Node.
 *
 * @since 1.1
 */
public class NodeMetadataFetcher extends AbstractCachingMetadataFetcher implements IStaticMetadataChangeListener {

    private static final Logger LOG = LoggerFactory.getLogger(NodeMetadataFetcher.class);

    private boolean httpRetrievalEnabled = false;
    private String hubSslCertificateString = "LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tDQpNSUlDVmpDQ0FiK2dBd0lCQWdJQkFUQU5CZ2txaGtpRzl3MEJBUVVGQURBVU1SSXdFQVlEVlFRRERBbHNiMk5oDQpiR2h2YzNRd0hoY05NVGN3TmpFek1USTFPVE16V2hjTk1UZ3dOakV6TVRJMU9UTXpXakFVTVJJd0VBWURWUVFEDQpEQWxzYjJOaGJHaHZjM1F3Z1o4d0RRWUpLb1pJaHZjTkFRRUJCUUFEZ1kwQU1JR0pBb0dCQUx0czl0U3JWK0loDQo1QW5MNE1FOXFTVExJQjFPVHFrcTFSQytQNXJGbmJzR01mS052Q0FOdThSNWYweUEva0Y1dXZBb1A3TDV4OTFODQp6TVVYMHJ3TEhpZGt1QVM5WlJlZDE0R25qMTJyL1lGM3RPdFVzQTdMbjZMODN2U05rdUdtb3pLNHd5S0ZkOWlWDQpGSVBnczVheG5YQkhxNkNaRDhmNzBpa1A2aDRoZlQ4dEFnTUJBQUdqZ2Jjd2diUXdDUVlEVlIwVEJBSXdBREFMDQpCZ05WSFE4RUJBTUNCU0F3SFFZRFZSME9CQllFRk81YzRySEdZaFdDOHRobjA0N2owazViRXpydk1CTUdBMVVkDQpKUVFNTUFvR0NDc0dBUVVGQndNQk1DZ0dDV0NHU0FHRytFSUJEUVFiRmhsSFpXNWxjbUYwWldRZ1lua2dVblZpDQplUzlQY0dWdVUxTk1NRHdHQTFVZEl3UTFNRE9BRk81YzRySEdZaFdDOHRobjA0N2owazViRXpydm9SaWtGakFVDQpNUkl3RUFZRFZRUUREQWxzYjJOaGJHaHZjM1NDQVFFd0RRWUpLb1pJaHZjTkFRRUZCUUFEZ1lFQUtQd01oY3poDQpVVHpRVnNaU1p2RW1WUWQ4TUt4R01Ha2dhRDdJZ0RNZldhUHpDOWRlMW45a1lVeE5vTHY3QmhsTWxydHlrMG4zDQpIcTZzMUEvU3VHR2lXQVZCb3BHNzd5TWlaZGFVUWJaVmtUOStsN3pLT290aFZNQkR5clFpV01KdURRbjRxRDNDDQo1QXB4ZC9oOC9GUlhQNVA5VGQrK2RUSnk3N2pXM2pEV2QvST0NCi0tLS0tRU5EIENFUlRJRklDQVRFLS0tLS0NCg==";
    /**
     * when restrictHttp is true, remote metadata is accepted only through https otherwise, metadata retrieved using
     * http protocol is also accepted
     */
    private boolean restrictHttp = false;

    /**
     * whether to enable the signature validation for EntityDescriptors
     */
    private boolean validateEntityDescriptorSignature = true;

    /**
     * initialized with a list of urls corresponding to EntityDescriptor not needing signature validation
     */
    private String trustedEntityDescriptors;

    private NODEFileMetadataProcessor fileMetadataLoader;

    private Set<String> trustedEntityDescriptorsSet = new HashSet<String>();

    private IMetadataCachingService cache;

    @Override
    public void add(EntityDescriptor ed) {
        if (null != cache) {
            cache.putDescriptor(ed.getEntityID(), ed, EntityDescriptorType.STATIC);
        }
    }

    public IMetadataCachingService getCache() {
        return cache;
    }

    public NODEFileMetadataProcessor getFileMetadataLoader() {
        return fileMetadataLoader;
    }

    @Override
    protected EntityDescriptor getFromCache(String url) {
        if (null != cache) {
            EntityDescriptor entityDescriptor = cache.getDescriptor(url);
            if (entityDescriptor != null && !entityDescriptor.isValid()) {
                LOG.error("Invalid cached metadata information associated with " + url
                        + ", removing it from the cache");
                removeFromCache(url);
            }
        }
        return null;
    }

    public String getTrustedEntityDescriptors() {
        return trustedEntityDescriptors;
    }

    public Set<String> getTrustedEntityDescriptorsSet() {
        return trustedEntityDescriptorsSet;
    }

    /**
     * perform post construct task, eg populating the cache with file based metadata
     */
    public void initProcessor() {
        if (getFileMetadataLoader() != null) {
            List<EntityDescriptorContainer> fileStoredDescriptors = getFileMetadataLoader().getEntityDescriptors();
            if (getCache() != null) {
                for (EntityDescriptorContainer edc : fileStoredDescriptors) {
                    for (EntityDescriptor ed : edc.getEntityDescriptors()) {
                        getCache().putDescriptor(ed.getEntityID(), ed, EntityDescriptorType.STATIC);
                        if (edc.getEntitiesDescriptor() != null && ed.getSignature() == null) {
                            getCache().putDescriptorSignatureHolder(ed.getEntityID(), edc);
                        }
                    }
                }
            }
            getFileMetadataLoader().addListenerContentChanged(this);
        }
    }

    @Override
    public boolean isHttpRetrievalEnabled() {
        return httpRetrievalEnabled;
    }

    public boolean isRestrictHttp() {
        return restrictHttp;
    }

    public boolean isValidateEntityDescriptorSignature() {
        return validateEntityDescriptorSignature;
    }

    @Override
    protected boolean mustUseHttps() {
        return restrictHttp;
    }

    @Override
    protected boolean mustValidateSignature(@Nonnull String url) {
        return validateEntityDescriptorSignature && !trustedEntityDescriptors.contains(url);
    }

    @Override
    protected void putInCache(String url, EntityDescriptor entityDescriptor) {
        if (null != cache && null != entityDescriptor && entityDescriptor.isValid()) {
            cache.putDescriptor(url, entityDescriptor, EntityDescriptorType.DYNAMIC);
        }
    }

    @Override
    public void remove(String entityID) {
        removeFromCache(entityID);
    }

    @Override
    protected void removeFromCache(String url) {
        if (null != cache) {
            cache.putDescriptor(url, null, null);
        }
    }

    public void setCache(IMetadataCachingService cache) {
        this.cache = cache;
    }

    public void setFileMetadataLoader(NODEFileMetadataProcessor fileMetadataLoader) {
        this.fileMetadataLoader = fileMetadataLoader;
    }

    public void setHttpRetrievalEnabled(boolean httpRetrievalEnabled) {
        this.httpRetrievalEnabled = httpRetrievalEnabled;
    }

    public void setRestrictHttp(boolean restrictHttp) {
        this.restrictHttp = restrictHttp;
    }

    public void setTrustedEntityDescriptors(String trustedEntityDescriptors) {
        this.trustedEntityDescriptors = trustedEntityDescriptors;
        setTrustedEntityDescriptorsSet(EIDASUtil.parseSemicolonSeparatedList(trustedEntityDescriptors));
    }

    public void setTrustedEntityDescriptorsSet(Set<String> trustedEntityDescriptorsSet) {
        this.trustedEntityDescriptorsSet = trustedEntityDescriptorsSet;
    }

    public void setValidateEntityDescriptorSignature(boolean validateEntityDescriptorSignature) {
        this.validateEntityDescriptorSignature = validateEntityDescriptorSignature;
    }

    /**
     * Override this method to plug your own SSLSocketFactory.
     * <p>
     * This default implementation relies on the default one from the JVM, i.e. using the default trustStore
     * ($JRE/lib/security/cacerts).
     *
     * @return the SecureProtocolSocketFactory instance to be used to connect to https metadata URLs.
     */
    @Nonnull
    @Override
    protected SecureProtocolSocketFactory newSslSocketFactory() {
        final SecureProtocolSocketFactory hubLocalSslCertificateProtocolSocketFactory = hubLocalSslSocketFactory();

        final SecureProtocolSocketFactory parentSslFactory = super.newSslSocketFactory();

        return new SecureProtocolSocketFactory() {
            @Override
            public Socket createSocket(Socket socket, String s, int i, boolean b) throws IOException {
                try {
                    return parentSslFactory.createSocket(socket, s, i, b);
                } catch (IOException e) {
                    return hubLocalSslCertificateProtocolSocketFactory.createSocket(socket, s, i, b);
                }
            }

            @Override
            public Socket createSocket(String s, int i, InetAddress inetAddress, int i1) throws IOException {
                try {
                    return parentSslFactory.createSocket(s, i, inetAddress, i1);
                } catch (IOException e) {
                    return hubLocalSslCertificateProtocolSocketFactory.createSocket(s, i, inetAddress, i1);
                }
            }

            @Override
            public Socket createSocket(String s, int i, InetAddress inetAddress, int i1,
                    HttpConnectionParams httpConnectionParams) throws IOException {
                try {
                    return parentSslFactory.createSocket(s, i, inetAddress, i1, httpConnectionParams);
                } catch (IOException e) {
                    return hubLocalSslCertificateProtocolSocketFactory.createSocket(s, i, inetAddress, i1,
                            httpConnectionParams);
                }
            }

            @Override
            public Socket createSocket(String s, int i) throws IOException {
                try {
                    return parentSslFactory.createSocket(s, i);
                } catch (IOException e) {
                    return hubLocalSslCertificateProtocolSocketFactory.createSocket(s, i);
                }
            }
        };

    }

    protected SecureProtocolSocketFactory hubLocalSslSocketFactory() {
        HostnameVerifier hostnameVerifier;

        if (!Boolean.getBoolean(DefaultBootstrap.SYSPROP_HTTPCLIENT_HTTPS_DISABLE_HOSTNAME_VERIFICATION)) {
            hostnameVerifier = new StrictHostnameVerifier();
        } else {
            hostnameVerifier = org.apache.commons.ssl.HostnameVerifier.ALLOW_ALL;
        }

        X509TrustManager trustedCertManager = new X509TrustManager() {
            @Override
            public java.security.cert.X509Certificate[] getAcceptedIssuers() {
                try {
                    return new X509Certificate[] { CertificateUtil.toCertificate(hubSslCertificateString) };
                } catch (EIDASSAMLEngineException e) {
                    throw new RuntimeException("Unable to load trusted certificate: ", e);
                }
            }

            @Override
            public void checkClientTrusted(java.security.cert.X509Certificate[] certs, String authType) {
            }

            @Override
            public void checkServerTrusted(java.security.cert.X509Certificate[] certs, String authType) {
            }
        };

        TLSProtocolSocketFactory tlsProtocolSocketFactory = new TLSProtocolSocketFactory(null, trustedCertManager,
                hostnameVerifier) {
            @Override
            protected void verifyHostname(Socket socket) throws SSLException {
                if (socket instanceof SSLSocket) {
                    SSLSocket sslSocket = (SSLSocket) socket;
                    try {
                        sslSocket.startHandshake();
                    } catch (IOException e) {
                        throw new SSLException(e);
                    }
                    SSLSession sslSession = sslSocket.getSession();
                    if (!sslSession.isValid()) {
                        throw new SSLException("SSLSession was invalid: Likely implicit handshake failure: "
                                + "Set system property javax.net.debug=all for details");
                    }
                    super.verifyHostname(sslSocket);
                }
            }
        };

        Protocol.registerProtocol("https", new Protocol("https", tlsProtocolSocketFactory, 443));

        return tlsProtocolSocketFactory;
    }
}