Java tutorial
/* * 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; } }