From 72e86431b59c466673214d330bbd9baa295449cf Mon Sep 17 00:00:00 2001 From: Thomas Lenz Date: Fri, 4 Nov 2016 09:51:26 +0100 Subject: add hostname validation to httpclient 3.1, which is assumed by openSAML 2.x --- .../config/ConfigurationProvider.java | 11 +- .../validation/oa/OAPVP2ConfigValidation.java | 3 +- .../task/impl/ServicesProtocolPVP2XTask.java | 3 +- .../moa-id-configtool.properties | 1 + .../data/deploy/conf/moa-id/moa-id.properties | 1 + id/server/doc/handbook/config/config.html | 12 ++ .../pvp2x/metadata/SimpleMOAMetadataProvider.java | 5 +- .../protocols/pvp2x/utils/MOASAMLSOAPClient.java | 6 +- .../utils/MOAHttpProtocolSocketFactory.java | 140 +++++++++++++++++---- .../engine/MOAeIDASChainingMetadataProvider.java | 5 +- 10 files changed, 156 insertions(+), 31 deletions(-) (limited to 'id') diff --git a/id/ConfigWebTool/src/main/java/at/gv/egovernment/moa/id/configuration/config/ConfigurationProvider.java b/id/ConfigWebTool/src/main/java/at/gv/egovernment/moa/id/configuration/config/ConfigurationProvider.java index c0cd971cf..05ce3344b 100644 --- a/id/ConfigWebTool/src/main/java/at/gv/egovernment/moa/id/configuration/config/ConfigurationProvider.java +++ b/id/ConfigWebTool/src/main/java/at/gv/egovernment/moa/id/configuration/config/ConfigurationProvider.java @@ -523,6 +523,14 @@ public class ConfigurationProvider { } + /** + * @return + */ + private boolean isHostNameValidationEnabled() { + return Boolean.parseBoolean(props.getProperty("general.ssl.hostnamevalidation", "true")); + + } + /** * @return the context */ @@ -580,7 +588,8 @@ public class ConfigurationProvider { null, "pkix", true, - new String[]{"crl"}); + new String[]{"crl"}, + ConfigurationProvider.getInstance().isHostNameValidationEnabled()); httpClient.setCustomSSLTrustStore(metadataurl, protoSocketFactory); diff --git a/id/ConfigWebTool/src/main/java/at/gv/egovernment/moa/id/configuration/validation/oa/OAPVP2ConfigValidation.java b/id/ConfigWebTool/src/main/java/at/gv/egovernment/moa/id/configuration/validation/oa/OAPVP2ConfigValidation.java index 970785bdb..61a380188 100644 --- a/id/ConfigWebTool/src/main/java/at/gv/egovernment/moa/id/configuration/validation/oa/OAPVP2ConfigValidation.java +++ b/id/ConfigWebTool/src/main/java/at/gv/egovernment/moa/id/configuration/validation/oa/OAPVP2ConfigValidation.java @@ -135,7 +135,8 @@ public class OAPVP2ConfigValidation { null, "pkix", true, - new String[]{"crl"}); + new String[]{"crl"}, + false); httpClient.setCustomSSLTrustStore( form.getMetaDataURL(), diff --git a/id/moa-id-webgui/src/main/java/at/gv/egovernment/moa/id/config/webgui/validation/task/impl/ServicesProtocolPVP2XTask.java b/id/moa-id-webgui/src/main/java/at/gv/egovernment/moa/id/config/webgui/validation/task/impl/ServicesProtocolPVP2XTask.java index dac5ae1ee..2d6f7c9a9 100644 --- a/id/moa-id-webgui/src/main/java/at/gv/egovernment/moa/id/config/webgui/validation/task/impl/ServicesProtocolPVP2XTask.java +++ b/id/moa-id-webgui/src/main/java/at/gv/egovernment/moa/id/config/webgui/validation/task/impl/ServicesProtocolPVP2XTask.java @@ -197,7 +197,8 @@ public class ServicesProtocolPVP2XTask extends AbstractTaskValidator implements null, "pkix", true, - new String[]{"crl"}); + new String[]{"crl"}, + false); httpClient.setCustomSSLTrustStore( metadataURL, diff --git a/id/server/data/deploy/conf/moa-id-configuration/moa-id-configtool.properties b/id/server/data/deploy/conf/moa-id-configuration/moa-id-configtool.properties index 8f7e0efaf..63b053228 100644 --- a/id/server/data/deploy/conf/moa-id-configuration/moa-id-configtool.properties +++ b/id/server/data/deploy/conf/moa-id-configuration/moa-id-configtool.properties @@ -12,6 +12,7 @@ general.moaid.instance.url=https://localhost:8443/moa-id-auth general.defaultlanguage=de general.ssl.certstore=certs/certstore general.ssl.truststore=certs/truststore +general.ssl.hostnamevalidation=true general.moaconfig.key=ConfigurationEncryptionKey general.pvp.schemavalidation=true diff --git a/id/server/data/deploy/conf/moa-id/moa-id.properties b/id/server/data/deploy/conf/moa-id/moa-id.properties index 41ca6c008..ea4a820e7 100644 --- a/id/server/data/deploy/conf/moa-id/moa-id.properties +++ b/id/server/data/deploy/conf/moa-id/moa-id.properties @@ -17,6 +17,7 @@ protocols.pvp2.schemavalidation=true configuration.moasession.key=SessionEncryptionKey configuration.moaconfig.key=ConfigurationEncryptionKey configuration.ssl.validation.revocation.method.order=ocsp,crl +configuration.ssl.validation.hostname=false #MOA-ID 3.x Monitoring Servlet configuration.monitoring.active=false diff --git a/id/server/doc/handbook/config/config.html b/id/server/doc/handbook/config/config.html index 1e1a7d398..0361442ac 100644 --- a/id/server/doc/handbook/config/config.html +++ b/id/server/doc/handbook/config/config.html @@ -214,6 +214,12 @@ UNIX: -Duser.properties=file:C:/Programme/apache/tomcat-8.x.x/conf/moa-id-config certs/truststore TrustedCACertificates enthält das Verzeichnis (relativ zur MOA-ID-Auth Basiskonfigurationsdatei), das jene Zertifikate enthält, die als vertrauenswürdig betrachtet werden. Im Zuge der Überprüfung der TLS-Serverzertifikate wird die Zertifikatspfaderstellung an einem dieser Zertifikate beendet. Dieses Verzeichnis wird zur Prüfung der SSL Serverzertifikate beim Download von PVP 2.1 Metadaten verwendet. + + general.ssl.hostnamevalidation + true / false +

Hiermit kann die SSL Hostname validation für das abholen von PVP Metadaten deaktiviert werden.

+

Hinweis: Workaround, da der httpClient der openSAML2 Implementierung kein SNI (Server Name Indication) unterstützt.

+ general.moaconfig.key ConfigurationEncryptionKey @@ -408,6 +414,12 @@ UNIX: moa.id.configuration=file:C:/Programme/apache/tomcat-8.x.x/conf/moa-id/moa

Definiert die Reihenfolge des Zertifikatsrevokierungschecks bei SSL Verbindungen. Die Defaultreihenfolge ist OCSP, CRL.

Hinweis: Die Angabe erfolgt als CSV, wobei die Schlüsselwörter 'ocsp' und 'crl' lauten

+ + configuration.ssl.validation.hostname + true / false +

Hiermit kann die SSL Hostname validation für das abholen von PVP Metadaten deaktiviert werden.

+

Hinweis: Workaround, da der httpClient der openSAML2 Implementierung kein SNI (Server Name Indication) unterstützt.

+ configuration.monitoring.active true / false diff --git a/id/server/idserverlib/src/main/java/at/gv/egovernment/moa/id/protocols/pvp2x/metadata/SimpleMOAMetadataProvider.java b/id/server/idserverlib/src/main/java/at/gv/egovernment/moa/id/protocols/pvp2x/metadata/SimpleMOAMetadataProvider.java index c0ba1d96d..d5c7d9100 100644 --- a/id/server/idserverlib/src/main/java/at/gv/egovernment/moa/id/protocols/pvp2x/metadata/SimpleMOAMetadataProvider.java +++ b/id/server/idserverlib/src/main/java/at/gv/egovernment/moa/id/protocols/pvp2x/metadata/SimpleMOAMetadataProvider.java @@ -66,13 +66,16 @@ public abstract class SimpleMOAMetadataProvider implements MetadataProvider{ if (metadataURL.startsWith("https:")) { try { + //FIX: change hostname validation default flag to true when httpClient is updated to > 4.4 MOAHttpProtocolSocketFactory protoSocketFactory = new MOAHttpProtocolSocketFactory( PVPConstants.SSLSOCKETFACTORYNAME, AuthConfigurationProviderFactory.getInstance().getTrustedCACertificates(), null, AuthConfiguration.DEFAULT_X509_CHAININGMODE, AuthConfigurationProviderFactory.getInstance().isTrustmanagerrevoationchecking(), - AuthConfigurationProviderFactory.getInstance().getRevocationMethodOrder()); + AuthConfigurationProviderFactory.getInstance().getRevocationMethodOrder(), + AuthConfigurationProviderFactory.getInstance().getBasicMOAIDConfigurationBoolean( + AuthConfiguration.PROP_KEY_SSL_HOSTNAME_VALIDATION, false)); httpClient.setCustomSSLTrustStore(metadataURL, protoSocketFactory); diff --git a/id/server/idserverlib/src/main/java/at/gv/egovernment/moa/id/protocols/pvp2x/utils/MOASAMLSOAPClient.java b/id/server/idserverlib/src/main/java/at/gv/egovernment/moa/id/protocols/pvp2x/utils/MOASAMLSOAPClient.java index 0d1f54249..e02ecb662 100644 --- a/id/server/idserverlib/src/main/java/at/gv/egovernment/moa/id/protocols/pvp2x/utils/MOASAMLSOAPClient.java +++ b/id/server/idserverlib/src/main/java/at/gv/egovernment/moa/id/protocols/pvp2x/utils/MOASAMLSOAPClient.java @@ -35,6 +35,7 @@ import org.opensaml.xml.XMLObject; import org.opensaml.xml.parse.BasicParserPool; import org.opensaml.xml.security.SecurityException; +import at.gv.egovernment.moa.id.commons.api.AuthConfiguration; import at.gv.egovernment.moa.id.commons.api.exceptions.ConfigurationException; import at.gv.egovernment.moa.id.commons.ex.MOAHttpProtocolSocketFactoryException; import at.gv.egovernment.moa.id.commons.utils.MOAHttpProtocolSocketFactory; @@ -70,6 +71,7 @@ public class MOASAMLSOAPClient { HttpClientBuilder clientBuilder = new HttpClientBuilder(); if (destination.startsWith("https")) { try { + //FIX: change hostname validation default flag to true when httpClient is updated to > 4.4 SecureProtocolSocketFactory sslprotocolsocketfactory = new MOAHttpProtocolSocketFactory( PVPConstants.SSLSOCKETFACTORYNAME, @@ -77,7 +79,9 @@ public class MOASAMLSOAPClient { null, AuthConfigurationProviderFactory.getInstance().getDefaultChainingMode(), AuthConfigurationProviderFactory.getInstance().isTrustmanagerrevoationchecking(), - AuthConfigurationProviderFactory.getInstance().getRevocationMethodOrder()); + AuthConfigurationProviderFactory.getInstance().getRevocationMethodOrder(), + AuthConfigurationProviderFactory.getInstance().getBasicMOAIDConfigurationBoolean( + AuthConfiguration.PROP_KEY_SSL_HOSTNAME_VALIDATION, false)); clientBuilder.setHttpsProtocolSocketFactory(sslprotocolsocketfactory ); } catch (MOAHttpProtocolSocketFactoryException e) { diff --git a/id/server/moa-id-commons/src/main/java/at/gv/egovernment/moa/id/commons/utils/MOAHttpProtocolSocketFactory.java b/id/server/moa-id-commons/src/main/java/at/gv/egovernment/moa/id/commons/utils/MOAHttpProtocolSocketFactory.java index 84743b8c7..5bcf915e8 100644 --- a/id/server/moa-id-commons/src/main/java/at/gv/egovernment/moa/id/commons/utils/MOAHttpProtocolSocketFactory.java +++ b/id/server/moa-id-commons/src/main/java/at/gv/egovernment/moa/id/commons/utils/MOAHttpProtocolSocketFactory.java @@ -27,7 +27,12 @@ import java.net.InetAddress; import java.net.Socket; import java.net.UnknownHostException; import java.security.GeneralSecurityException; +import java.security.cert.Certificate; +import java.security.cert.X509Certificate; +import javax.net.ssl.SSLException; +import javax.net.ssl.SSLPeerUnverifiedException; +import javax.net.ssl.SSLSession; import javax.net.ssl.SSLSocket; import javax.net.ssl.SSLSocketFactory; @@ -35,6 +40,7 @@ import org.apache.commons.httpclient.ConnectTimeoutException; import org.apache.commons.httpclient.params.HttpConnectionParams; import org.apache.commons.httpclient.protocol.SecureProtocolSocketFactory; import org.apache.commons.lang3.StringUtils; +import org.apache.http.conn.ssl.DefaultHostnameVerifier; import at.gv.egovernment.moa.id.commons.ex.MOAHttpProtocolSocketFactoryException; import at.gv.egovernment.moa.id.commons.utils.ssl.SSLConfigurationException; @@ -51,34 +57,57 @@ public class MOAHttpProtocolSocketFactory implements SecureProtocolSocketFactory private SSLSocketFactory sslfactory = null; + private boolean verifyHostName = true; + /** + * SSL Protocol socket factory + * + * @param url + * @param trustStoreURL + * @param acceptedServerCertURL + * @param chainingMode Specifies the ChainingMode for SSL certificate trust validation + * @param checkRevocation Enables / Disables certificate revocation checks + * @param revocationMethodOrder + * @param verifyHostName Enables / Disables hostName verfication + * @throws MOAHttpProtocolSocketFactoryException + */ public MOAHttpProtocolSocketFactory ( String url, String trustStoreURL, String acceptedServerCertURL, String chainingMode, boolean checkRevocation, - String[] revocationMethodOrder) throws MOAHttpProtocolSocketFactoryException { + String[] revocationMethodOrder, + boolean verifyHostName) throws MOAHttpProtocolSocketFactoryException { internalInitialize(url, null, trustStoreURL, acceptedServerCertURL, chainingMode, checkRevocation, revocationMethodOrder); + this.verifyHostName = verifyHostName; + } /** - * @param string - * @param certStoreDirectory - * @param trustStoreDirectory - * @param object - * @param string2 - * @param b - * @param strings + * SSL Protocol socket factory + * + * @param url + * @param certStoreDirectory + * @param trustStoreURL + * @param acceptedServerCertURL + * @param chainingMode Specifies the ChainingMode for SSL certificate trust validation + * @param checkRevocation Enables / Disables certificate revocation checks + * @param revocationMethodOrder + * @param verifyHostName Enables / Disables hostName verfication + * @throws MOAHttpProtocolSocketFactoryException */ public MOAHttpProtocolSocketFactory(String url, String certStoreDirectory, String trustStoreURL, String acceptedServerCertURL, String chainingMode, boolean checkRevocation, - String[] revocationMethodOrder) throws MOAHttpProtocolSocketFactoryException { + String[] revocationMethodOrder, + boolean verifyHostName) throws MOAHttpProtocolSocketFactoryException { internalInitialize(url, certStoreDirectory, trustStoreURL, acceptedServerCertURL, chainingMode, checkRevocation, revocationMethodOrder); + this.verifyHostName = verifyHostName; + } private void internalInitialize(String url, String certStoreDirectory, String trustStoreURL, @@ -120,7 +149,7 @@ public class MOAHttpProtocolSocketFactory implements SecureProtocolSocketFactory */ public Socket createSocket(String host, int port, InetAddress localAddress, int localPort) throws IOException, UnknownHostException { - return setEnabledSslCiphers(this.sslfactory.createSocket(host, port, + return setSecurityRequirements(this.sslfactory.createSocket(host, port, localAddress, localPort)); } @@ -130,7 +159,7 @@ public class MOAHttpProtocolSocketFactory implements SecureProtocolSocketFactory public Socket createSocket(String host, int port, InetAddress localAddress, int localPort, HttpConnectionParams params) throws IOException, UnknownHostException, ConnectTimeoutException { - return setEnabledSslCiphers(this.sslfactory.createSocket(host, port, + return setSecurityRequirements(this.sslfactory.createSocket(host, port, localAddress, localPort)); } @@ -139,7 +168,7 @@ public class MOAHttpProtocolSocketFactory implements SecureProtocolSocketFactory */ public Socket createSocket(String host, int port) throws IOException, UnknownHostException { - return setEnabledSslCiphers(this.sslfactory.createSocket(host, port)); + return setSecurityRequirements(this.sslfactory.createSocket(host, port)); } /* (non-Javadoc) @@ -147,10 +176,73 @@ public class MOAHttpProtocolSocketFactory implements SecureProtocolSocketFactory */ public Socket createSocket(Socket socket, String host, int port, boolean autoClose) throws IOException, UnknownHostException { - return setEnabledSslCiphers(this.sslfactory.createSocket(socket, host, + return setSecurityRequirements(this.sslfactory.createSocket(socket, host, port, autoClose)); } + + private Socket setSecurityRequirements(Socket socket) throws SSLException { + if (socket instanceof SSLSocket) { + SSLSocket sslSocket = (SSLSocket)socket; + + //verify Hostname + verifyHostName(sslSocket); + + //set allowed SSL ciphers + sslSocket = setEnabledSslCiphers(sslSocket); + + return sslSocket; + } + + return socket; + } + + /** + * Verify hostname against SSL server certificate + * + * @param sslSocket + * @throws SSLPeerUnverifiedException + */ + private void verifyHostName(SSLSocket sslSocket) throws SSLException{ + if (verifyHostName) { + SSLSession session = sslSocket.getSession(); + String hostName = session.getPeerHost(); + Certificate[] certs = null; + + try { + certs = session.getPeerCertificates(); + if (certs == null || certs.length < 1) + throw new SSLPeerUnverifiedException("No server certificates found!"); + + X509Certificate x509 = (X509Certificate)certs[0]; + DefaultHostnameVerifier hostNameVerifier = new DefaultHostnameVerifier(); + hostNameVerifier.verify(hostName, x509); + + } catch (SSLPeerUnverifiedException e) { + Logger.error("Host:" + hostName + " sends no certificates for validation.", e); + throw e; + + } catch (SSLException e) { + Logger.error("Hostname validation FAILED:" + hostName + " validation ", e); + + //log certificates in case of Debug + if (Logger.isDebugEnabled() && certs != null) { + Logger.debug("Server certificate chain:"); + for (int i = 0; i < certs.length; i++) { + Logger.debug("X509Certificate[" + i + "]=" + certs[i]); + } + + } + + throw e; + + } + + } + + } + + /** * Enable only a specific subset of TLS cipher suites * This subset can be set by 'https.cipherSuites' SystemProperty (z.B. -Dhttps.cipherSuites=...) @@ -158,19 +250,17 @@ public class MOAHttpProtocolSocketFactory implements SecureProtocolSocketFactory * @param sslSocket {@link SSLSocket} * @return {@link SSLSocket} with Ciphersuites */ - private Socket setEnabledSslCiphers(Socket sslSocket) { - if (sslSocket instanceof SSLSocket) { - String systemProp = System.getProperty("https.cipherSuites"); - if (MiscUtil.isNotEmpty(systemProp)) { - ((SSLSocket) sslSocket).setEnabledCipherSuites(systemProp.split(",")); - - } + private SSLSocket setEnabledSslCiphers(SSLSocket sslSocket) { + String systemProp = System.getProperty("https.cipherSuites"); + if (MiscUtil.isNotEmpty(systemProp)) { + sslSocket.setEnabledCipherSuites(systemProp.split(",")); + + } - try { - Logger.trace("Enabled SSL-Cipher: " + StringUtils.join(((SSLSocket) sslSocket).getEnabledCipherSuites(), ",")); - } catch (Exception e) { - Logger.error(e); - } + try { + Logger.trace("Enabled SSL-Cipher: " + StringUtils.join(((SSLSocket) sslSocket).getEnabledCipherSuites(), ",")); + } catch (Exception e) { + Logger.error(e); } return sslSocket; diff --git a/id/server/modules/moa-id-module-eIDAS/src/main/java/at/gv/egovernment/moa/id/auth/modules/eidas/engine/MOAeIDASChainingMetadataProvider.java b/id/server/modules/moa-id-module-eIDAS/src/main/java/at/gv/egovernment/moa/id/auth/modules/eidas/engine/MOAeIDASChainingMetadataProvider.java index 0cb6228a7..ffa74b92b 100644 --- a/id/server/modules/moa-id-module-eIDAS/src/main/java/at/gv/egovernment/moa/id/auth/modules/eidas/engine/MOAeIDASChainingMetadataProvider.java +++ b/id/server/modules/moa-id-module-eIDAS/src/main/java/at/gv/egovernment/moa/id/auth/modules/eidas/engine/MOAeIDASChainingMetadataProvider.java @@ -204,13 +204,16 @@ public class MOAeIDASChainingMetadataProvider implements ObservableMetadataProvi if (metadataURL.startsWith("https:")) { try { + //FIX: change hostname validation default flag to true when httpClient is updated to > 4.4 MOAHttpProtocolSocketFactory protoSocketFactory = new MOAHttpProtocolSocketFactory( Constants.SSLSOCKETFACTORYNAME, authConfig.getTrustedCACertificates(), null, AuthConfiguration.DEFAULT_X509_CHAININGMODE, authConfig.isTrustmanagerrevoationchecking(), - authConfig.getRevocationMethodOrder()); + authConfig.getRevocationMethodOrder(), + authConfig.getBasicMOAIDConfigurationBoolean( + AuthConfiguration.PROP_KEY_SSL_HOSTNAME_VALIDATION, false)); httpClient.setCustomSSLTrustStore(metadataURL, protoSocketFactory); -- cgit v1.2.3