From f95a1fb3982395ccbc7e139cb5bd8a1c106bbb48 Mon Sep 17 00:00:00 2001 From: Thomas Lenz Date: Wed, 11 Mar 2020 12:46:45 +0100 Subject: refactor HttpClientFactory.java to build HTTP clients with different authentication mechanisms --- .../services/ProtocolAuthenticationService.java | 2 +- .../impl/idp/controller/protocols/RequestImpl.java | 2 +- .../core/impl/idp/auth/DummyHttpClientFactory.java | 12 +- eaaf_core_utils/pom.xml | 10 + .../impl/http/EaafSslKeySelectionStrategy.java | 50 +++ .../core/impl/http/HttpClientConfiguration.java | 191 +++++++++++ .../eaaf/core/impl/http/HttpClientFactory.java | 360 ++++++++++++++++++++ .../at/gv/egiz/eaaf/core/impl/http/HttpUtils.java | 194 +++++++++++ .../eaaf/core/impl/http/IHttpClientFactory.java | 43 +++ .../eaaf/core/impl/utils/HttpClientFactory.java | 370 --------------------- .../at/gv/egiz/eaaf/core/impl/utils/HttpUtils.java | 118 ------- .../eaaf/core/impl/utils/IHttpClientFactory.java | 24 -- .../messages/eaaf_utils_message.properties | 5 +- .../src/main/resources/spring/eaaf_utils.beans.xml | 4 +- .../eaaf/core/test/http/HttpClientFactoryTest.java | 328 ++++++++++++++++++ .../tasks/AbstractCreateQualEidRequestTask.java | 2 +- .../modules/pvp2/test/binding/PostBindingTest.java | 2 +- .../pvp2/test/binding/RedirectBindingTest.java | 2 +- .../pvp2/test/dummy/DummyMetadataProvider.java | 2 +- .../pvp2/test/metadata/ChainingMetadataTest.java | 2 +- .../pvp2/test/metadata/MetadataResolverTest.java | 2 +- .../test/resources/spring/test_eaaf_core.beans.xml | 2 +- pom.xml | 8 +- 23 files changed, 1208 insertions(+), 527 deletions(-) create mode 100644 eaaf_core_utils/src/main/java/at/gv/egiz/eaaf/core/impl/http/EaafSslKeySelectionStrategy.java create mode 100644 eaaf_core_utils/src/main/java/at/gv/egiz/eaaf/core/impl/http/HttpClientConfiguration.java create mode 100644 eaaf_core_utils/src/main/java/at/gv/egiz/eaaf/core/impl/http/HttpClientFactory.java create mode 100644 eaaf_core_utils/src/main/java/at/gv/egiz/eaaf/core/impl/http/HttpUtils.java create mode 100644 eaaf_core_utils/src/main/java/at/gv/egiz/eaaf/core/impl/http/IHttpClientFactory.java delete mode 100644 eaaf_core_utils/src/main/java/at/gv/egiz/eaaf/core/impl/utils/HttpClientFactory.java delete mode 100644 eaaf_core_utils/src/main/java/at/gv/egiz/eaaf/core/impl/utils/HttpUtils.java delete mode 100644 eaaf_core_utils/src/main/java/at/gv/egiz/eaaf/core/impl/utils/IHttpClientFactory.java create mode 100644 eaaf_core_utils/src/test/java/at/gv/egiz/eaaf/core/test/http/HttpClientFactoryTest.java diff --git a/eaaf_core/src/main/java/at/gv/egiz/eaaf/core/impl/idp/auth/services/ProtocolAuthenticationService.java b/eaaf_core/src/main/java/at/gv/egiz/eaaf/core/impl/idp/auth/services/ProtocolAuthenticationService.java index 817c7aa2..4c82adac 100644 --- a/eaaf_core/src/main/java/at/gv/egiz/eaaf/core/impl/idp/auth/services/ProtocolAuthenticationService.java +++ b/eaaf_core/src/main/java/at/gv/egiz/eaaf/core/impl/idp/auth/services/ProtocolAuthenticationService.java @@ -70,8 +70,8 @@ import at.gv.egiz.eaaf.core.exceptions.InvalidProtocolRequestException; import at.gv.egiz.eaaf.core.exceptions.ProcessExecutionException; import at.gv.egiz.eaaf.core.exceptions.ProtocolNotActiveException; import at.gv.egiz.eaaf.core.impl.gui.AbstractGuiFormBuilderConfiguration; +import at.gv.egiz.eaaf.core.impl.http.HttpUtils; import at.gv.egiz.eaaf.core.impl.idp.controller.protocols.RequestImpl; -import at.gv.egiz.eaaf.core.impl.utils.HttpUtils; @Service public class ProtocolAuthenticationService implements IProtocolAuthenticationService { diff --git a/eaaf_core/src/main/java/at/gv/egiz/eaaf/core/impl/idp/controller/protocols/RequestImpl.java b/eaaf_core/src/main/java/at/gv/egiz/eaaf/core/impl/idp/controller/protocols/RequestImpl.java index adc8774a..f4494106 100644 --- a/eaaf_core/src/main/java/at/gv/egiz/eaaf/core/impl/idp/controller/protocols/RequestImpl.java +++ b/eaaf_core/src/main/java/at/gv/egiz/eaaf/core/impl/idp/controller/protocols/RequestImpl.java @@ -40,8 +40,8 @@ import at.gv.egiz.eaaf.core.api.idp.ISpConfiguration; import at.gv.egiz.eaaf.core.exceptions.EaafAuthenticationException; import at.gv.egiz.eaaf.core.exceptions.EaafException; import at.gv.egiz.eaaf.core.exceptions.EaafStorageException; +import at.gv.egiz.eaaf.core.impl.http.HttpUtils; import at.gv.egiz.eaaf.core.impl.idp.auth.data.AuthProcessDataWrapper; -import at.gv.egiz.eaaf.core.impl.utils.HttpUtils; import at.gv.egiz.eaaf.core.impl.utils.TransactionIdUtils; import org.apache.commons.lang3.StringUtils; diff --git a/eaaf_core/src/test/java/at/gv/egiz/eaaf/core/impl/idp/auth/DummyHttpClientFactory.java b/eaaf_core/src/test/java/at/gv/egiz/eaaf/core/impl/idp/auth/DummyHttpClientFactory.java index 9a924f83..6aea52ac 100644 --- a/eaaf_core/src/test/java/at/gv/egiz/eaaf/core/impl/idp/auth/DummyHttpClientFactory.java +++ b/eaaf_core/src/test/java/at/gv/egiz/eaaf/core/impl/idp/auth/DummyHttpClientFactory.java @@ -1,8 +1,10 @@ package at.gv.egiz.eaaf.core.impl.idp.auth; -import org.apache.http.impl.client.CloseableHttpClient; +import at.gv.egiz.eaaf.core.exceptions.EaafException; +import at.gv.egiz.eaaf.core.impl.http.HttpClientConfiguration; +import at.gv.egiz.eaaf.core.impl.http.IHttpClientFactory; -import at.gv.egiz.eaaf.core.impl.utils.IHttpClientFactory; +import org.apache.http.impl.client.CloseableHttpClient; public class DummyHttpClientFactory implements IHttpClientFactory { @@ -18,4 +20,10 @@ public class DummyHttpClientFactory implements IHttpClientFactory { return null; } + @Override + public CloseableHttpClient getHttpClient(HttpClientConfiguration config) throws EaafException { + // TODO Auto-generated method stub + return null; + } + } diff --git a/eaaf_core_utils/pom.xml b/eaaf_core_utils/pom.xml index 1f5467ab..681152f7 100644 --- a/eaaf_core_utils/pom.xml +++ b/eaaf_core_utils/pom.xml @@ -100,6 +100,16 @@ guava test + + com.squareup.okhttp3 + mockwebserver + test + + + com.squareup.okhttp3 + okhttp-tls + test + diff --git a/eaaf_core_utils/src/main/java/at/gv/egiz/eaaf/core/impl/http/EaafSslKeySelectionStrategy.java b/eaaf_core_utils/src/main/java/at/gv/egiz/eaaf/core/impl/http/EaafSslKeySelectionStrategy.java new file mode 100644 index 00000000..1e1e2137 --- /dev/null +++ b/eaaf_core_utils/src/main/java/at/gv/egiz/eaaf/core/impl/http/EaafSslKeySelectionStrategy.java @@ -0,0 +1,50 @@ +package at.gv.egiz.eaaf.core.impl.http; + +import java.net.Socket; +import java.util.Map; + +import org.apache.commons.lang3.StringUtils; +import org.apache.http.ssl.PrivateKeyDetails; +import org.apache.http.ssl.PrivateKeyStrategy; + +import lombok.extern.slf4j.Slf4j; + +/** + * Private Key selection implementation for Apache HTTP clients. + * + * @author tlenz + * + */ +@Slf4j +public class EaafSslKeySelectionStrategy implements PrivateKeyStrategy { + + private final String keyAlias; + + /** + * Private Key selection implementation for Apache HTTP clients. + * + * @param alias Alias of the Key that should be used for SSL client authentication. + */ + public EaafSslKeySelectionStrategy(String alias) { + this.keyAlias = alias; + + } + + @Override + public String chooseAlias(Map aliases, Socket socket) { + log.trace("Selection SSL client-auth key for alias: {}", keyAlias); + final PrivateKeyDetails selected = aliases.get(keyAlias); + if (selected != null) { + log.trace("Select SL client-auth key with type:", selected.getType()); + return keyAlias; + + } else { + log.warn("KeyStore contains NO key with alias: {}. Using first key from keystore", keyAlias); + log.info("Available aliases: {}", StringUtils.join(aliases.keySet(), ", ")); + return aliases.keySet().iterator().next(); + + } + + } + +} diff --git a/eaaf_core_utils/src/main/java/at/gv/egiz/eaaf/core/impl/http/HttpClientConfiguration.java b/eaaf_core_utils/src/main/java/at/gv/egiz/eaaf/core/impl/http/HttpClientConfiguration.java new file mode 100644 index 00000000..582ad545 --- /dev/null +++ b/eaaf_core_utils/src/main/java/at/gv/egiz/eaaf/core/impl/http/HttpClientConfiguration.java @@ -0,0 +1,191 @@ +package at.gv.egiz.eaaf.core.impl.http; + +import java.text.MessageFormat; +import java.util.UUID; + +import javax.annotation.Nonnull; + +import at.gv.egiz.eaaf.core.exceptions.EaafConfigurationException; +import at.gv.egiz.eaaf.core.impl.credential.KeyStoreConfiguration; + +import org.apache.commons.lang3.StringUtils; + +import lombok.Getter; +import lombok.Setter; +import lombok.extern.slf4j.Slf4j; + +@Slf4j +@Getter +public class HttpClientConfiguration { + + private static final String MSG_KEYSTORE_NAME = "KeyStore for httpClient: {0}"; + + private static final String ERROR_00 = "internal.httpclient.00"; + private static final String ERROR_01 = "internal.httpclient.01"; + private static final String ERROR_02 = "internal.httpclient.02"; + + @Nonnull + private final String friendlyName; + + @Nonnull + private final String uuid; + + @Nonnull + private ClientAuthMode authMode = ClientAuthMode.NONE; + + @Setter + private String username; + + @Setter + private String password; + + @Setter + boolean disableHostnameValidation = false; + + @Setter + boolean disableTlsHostCertificateValidation = false; + + + private KeyStoreConfiguration keyStoreConfig; + + @Setter + private String sslKeyAlias; + + @Setter + private String sslKeyPassword; + + @Setter + private boolean followHttpRedirects = true; + + /** + * Get a new HTTP-client configuration object. + * + * @param name FriendlyName of this http client for logging purposes. + */ + public HttpClientConfiguration(String name) { + this.friendlyName = name; + this.uuid = UUID.randomUUID().toString(); + + } + + /** + * Set Client authentication-mode from configuration property. + * + *

If the mode is unknown than the {@link ClientAuthMode} is set to NONE

+ * + * @param authModeString Modes from {@link ClientAuthMode} + */ + public void setAuthMode(String authModeString) { + final ClientAuthMode configAuthMode = HttpClientConfiguration.ClientAuthMode.fromString(authModeString); + if (configAuthMode != null) { + authMode = configAuthMode; + + } else { + log.warn("Can Not parse ClientAuthMode for client: {}! Set mode to default value", + friendlyName); + + } + } + + + /** + * Validate the internal state of this configuration object. + * + * @throws EaafConfigurationException In case of a configuration error + */ + public void validate() throws EaafConfigurationException { + log.trace("Validating http-client: {}", this.friendlyName); + if (this.authMode.equals(ClientAuthMode.PASSWORD)) { + if (StringUtils.isEmpty(this.username)) { + throw new EaafConfigurationException(ERROR_00, new Object[] {this.friendlyName}); + + } + + if (StringUtils.isEmpty(this.password)) { + log.warn("Http basic authentication was activated but NOT username was set!"); + + } + + } else if (this.authMode.equals(ClientAuthMode.SSL)) { + if (this.keyStoreConfig == null) { + throw new EaafConfigurationException(ERROR_01, new Object[] {this.friendlyName}); + + } else { + log.trace("Validating KeyStore: {} for http-client: {} ...", + this.keyStoreConfig.getFriendlyName(), this.friendlyName); + this.keyStoreConfig.validate(); + + } + + if (StringUtils.isEmpty(this.sslKeyPassword)) { + throw new EaafConfigurationException(ERROR_02, new Object[] { + this.friendlyName, this.keyStoreConfig.getFriendlyName()}); + + } + } + + } + + /** + * Build a {@link KeyStoreConfiguration} object from configuration parameters. + * + * @param keyStoreType String based KeyStore type + * @param keyStorePath Path to KeyStore in case of a software based KeyStore + * @param keyStorePassword Password in case of a software based KeyStore + * @param keyStoreName Name of the KeyStore in case of a named KeyStore like HSM-Facade + * @throws EaafConfigurationException In case of a configuration error + */ + public void buildKeyStoreConfig(String keyStoreType, String keyStorePath, + String keyStorePassword, String keyStoreName) throws EaafConfigurationException { + final KeyStoreConfiguration config = new KeyStoreConfiguration(); + config.setKeyStoreType(keyStoreType); + config.setFriendlyName(MessageFormat.format(MSG_KEYSTORE_NAME, friendlyName)); + config.setSoftKeyStoreFilePath(keyStorePath); + config.setSoftKeyStorePassword(keyStorePassword); + config.setKeyStoreName(keyStoreName); + this.keyStoreConfig = config; + + } + + public enum ClientAuthMode { + NONE("none"), PASSWORD("password"), SSL("ssl"); + + private final String mode; + + ClientAuthMode(final String mode) { + this.mode = mode; + } + + /** + * Get the PVP mode. + * + * @return + */ + public String getMode() { + return this.mode; + } + + /** + * Get http-client authentication mode from String representation. + * + * @param s Config parameter + * @return + */ + public static ClientAuthMode fromString(final String s) { + try { + return ClientAuthMode.valueOf(s.toUpperCase()); + + } catch (IllegalArgumentException | NullPointerException e) { + return null; + } + } + + @Override + public String toString() { + return getMode(); + + } + + } + +} diff --git a/eaaf_core_utils/src/main/java/at/gv/egiz/eaaf/core/impl/http/HttpClientFactory.java b/eaaf_core_utils/src/main/java/at/gv/egiz/eaaf/core/impl/http/HttpClientFactory.java new file mode 100644 index 00000000..b6e660da --- /dev/null +++ b/eaaf_core_utils/src/main/java/at/gv/egiz/eaaf/core/impl/http/HttpClientFactory.java @@ -0,0 +1,360 @@ +package at.gv.egiz.eaaf.core.impl.http; + +import java.security.KeyStore; +import java.security.NoSuchAlgorithmException; +import java.util.HashMap; +import java.util.Map; + +import javax.annotation.Nonnull; +import javax.annotation.PostConstruct; +import javax.net.ssl.HostnameVerifier; +import javax.net.ssl.SSLContext; + +import at.gv.egiz.eaaf.core.api.idp.IConfiguration; +import at.gv.egiz.eaaf.core.exceptions.EaafConfigurationException; +import at.gv.egiz.eaaf.core.exceptions.EaafException; +import at.gv.egiz.eaaf.core.exceptions.EaafFactoryException; +import at.gv.egiz.eaaf.core.impl.credential.EaafKeyStoreFactory; +import at.gv.egiz.eaaf.core.impl.credential.KeyStoreConfiguration.KeyStoreType; + +import org.apache.commons.lang3.StringUtils; +import org.apache.http.HttpRequest; +import org.apache.http.HttpResponse; +import org.apache.http.ProtocolException; +import org.apache.http.auth.AuthScope; +import org.apache.http.auth.UsernamePasswordCredentials; +import org.apache.http.client.CredentialsProvider; +import org.apache.http.client.RedirectStrategy; +import org.apache.http.client.config.RequestConfig; +import org.apache.http.client.methods.HttpUriRequest; +import org.apache.http.config.Registry; +import org.apache.http.config.RegistryBuilder; +import org.apache.http.conn.socket.ConnectionSocketFactory; +import org.apache.http.conn.socket.LayeredConnectionSocketFactory; +import org.apache.http.conn.socket.PlainConnectionSocketFactory; +import org.apache.http.conn.ssl.NoopHostnameVerifier; +import org.apache.http.conn.ssl.SSLConnectionSocketFactory; +import org.apache.http.impl.client.BasicCredentialsProvider; +import org.apache.http.impl.client.CloseableHttpClient; +import org.apache.http.impl.client.DefaultRedirectStrategy; +import org.apache.http.impl.client.HttpClientBuilder; +import org.apache.http.impl.client.HttpClients; +import org.apache.http.impl.conn.PoolingHttpClientConnectionManager; +import org.apache.http.protocol.HttpContext; +import org.springframework.beans.factory.annotation.Autowired; + +import lombok.extern.slf4j.Slf4j; + +@Slf4j +public class HttpClientFactory implements IHttpClientFactory { + + @Autowired + private IConfiguration basicConfig; + @Autowired + private EaafKeyStoreFactory keyStoreFactory; + + private static final String ERROR_03 = "internal.httpclient.03"; + + public static final String PROP_CONFIG_CLIENT_HTTP_CONNECTION_POOL_USE = + "client.http.connection.pool.use"; + public static final String PROP_CONFIG_CLIENT_HTTP_CONNECTION_POOL_MAXTOTAL = + "client.http.connection.pool.maxtotal"; + public static final String PROP_CONFIG_CLIENT_HTTP_CONNECTION_POOL_MAXPERROUTE = + "client.http.connection.pool.maxperroute"; + public static final String PROP_CONFIG_CLIENT_HTTP_CONNECTION_TIMEOUT_SOCKET = + "client.http.connection.timeout.socket"; + public static final String PROP_CONFIG_CLIENT_HTTP_CONNECTION_TIMEOUT_CONNECTION = + "client.http.connection.timeout.connection"; + public static final String PROP_CONFIG_CLIENT_HTTP_CONNECTION_TIMEOUT_REQUEST = + "client.http.connection.timeout.request"; + public static final String PROP_CONFIG_CLIENT_HTTP_SSL_HOSTNAMEVERIFIER_TRUSTALL = + "client.http.ssl.hostnameverifier.trustall"; + + public static final String PROP_CONFIG_CLIENT_MODE = "client.authmode"; + public static final String PROP_CONFIG_CLIENT_AUTH_HTTP_USERNAME = "client.auth.http.username"; + public static final String PROP_CONFIG_CLIENT_AUTH_HTTP_PASSORD = "client.auth.http.password"; + public static final String PROP_CONFIG_CLIENT_AUTH_SSL_KEYSTORE_PATH = + "client.auth.ssl.keystore.path"; + public static final String PROP_CONFIG_CLIENT_AUTH_SSL_KEYSTORE_PASSORD = + "client.auth.ssl.keystore.password"; + private static final String PROP_CONFIG_CLIENT_AUTH_SSL_KEYSTORE_NAME = + "client.auth.ssl.keystore.name"; + public static final String PROP_CONFIG_CLIENT_AUTH_SSL_KEYSTORE_TYPE = + "client.auth.ssl.keystore.type"; + public static final String PROP_CONFIG_CLIENT_AUTH_SSL_KEY_ALIAS = + "client.auth.ssl.key.alias"; + public static final String PROP_CONFIG_CLIENT_AUTH_SSL_KEY_PASSWORD = + "client.auth.ssl.key.password"; + + // default configuration values + public static final String DEFAULT_CONFIG_CLIENT_HTTP_CONNECTION_TIMEOUT_SOCKET = "15"; + public static final String DEFAULT_CONFIG_CLIENT_HTTP_CONNECTION_TIMEOUT_CONNECTION = "15"; + public static final String DEFAULT_CONFIG_CLIENT_HTTP_CONNECTION_TIMEOUT_REQUEST = "30"; + public static final String DEFAULT_CONFIG_CLIENT_HTTP_CONNECTION_POOL_MAXTOTAL = "500"; + public static final String DEFAULT_CONFIG_CLIENT_HTTP_CONNECTION_POOL_MAXPERROUTE = "100"; + + private String defaultConfigurationId = null; + private final Map availableBuilders = new HashMap<>(); + + /* + * (non-Javadoc) + * + * @see at.gv.egiz.eaaf.core.impl.utils.IHttpClientFactory#getHttpClient() + */ + @Override + public CloseableHttpClient getHttpClient() { + return getHttpClient(true); + + } + + @Override + public CloseableHttpClient getHttpClient(final boolean followRedirects) { + return availableBuilders.get(defaultConfigurationId).setRedirectStrategy( + buildRedirectStrategy(followRedirects)).build(); + + } + + @Override + public CloseableHttpClient getHttpClient(@Nonnull HttpClientConfiguration config) throws EaafException { + log.trace("Build http client for: {}", config.getFriendlyName()); + HttpClientBuilder builder = null; + if (availableBuilders.containsKey(config.getUuid())) { + builder = availableBuilders.get(config.getUuid()); + + } else { + log.debug("Initialize new http-client builder for: {}", config.getFriendlyName()); + + //validate configuration object + config.validate(); + + builder = HttpClients.custom(); + builder.setDefaultRequestConfig(buildDefaultRequestConfig()); + + //inject basic authentication infos + injectBasicAuthenticationIfRequired(builder, config); + + //inject authentication if required + final LayeredConnectionSocketFactory sslConnectionFactory = getSslContext(config); + + // set pool connection if required + injectDefaultConnectionPoolIfRequired(builder, sslConnectionFactory); + + availableBuilders.put(config.getUuid(), builder); + + } + + return builder.setRedirectStrategy( + buildRedirectStrategy(config.isFollowHttpRedirects())).build(); + + } + + @PostConstruct + private void initalize() throws EaafException { + final HttpClientConfiguration defaultHttpClientConfig = buildDefaultHttpClientConfiguration(); + + // initialize http client + log.trace("Initializing default HTTP-Client builder ... "); + final HttpClientBuilder defaultHttpClientBuilder = HttpClients.custom(); + + // set default request configuration + defaultHttpClientBuilder.setDefaultRequestConfig(buildDefaultRequestConfig()); + + //inject http basic authentication + injectBasicAuthenticationIfRequired(defaultHttpClientBuilder, defaultHttpClientConfig); + + // inject authentication if required + final LayeredConnectionSocketFactory sslConnectionFactory = + getSslContext(defaultHttpClientConfig); + + // set pool connection if required + injectDefaultConnectionPoolIfRequired(defaultHttpClientBuilder, sslConnectionFactory); + + //set default http client builder + defaultConfigurationId = defaultHttpClientConfig.getUuid(); + availableBuilders.put(defaultConfigurationId, defaultHttpClientBuilder); + + } + + private HttpClientConfiguration buildDefaultHttpClientConfiguration() throws EaafConfigurationException { + final HttpClientConfiguration config = new HttpClientConfiguration("Default"); + + // inject basic http authentication if required + config.setAuthMode(basicConfig.getBasicConfiguration(PROP_CONFIG_CLIENT_MODE, + HttpClientConfiguration.ClientAuthMode.NONE.getMode())); + log.info("Default client authentication-mode is set to: {}", config.getAuthMode()); + + // set Username and Password if available + config.setUsername(basicConfig.getBasicConfiguration(PROP_CONFIG_CLIENT_AUTH_HTTP_USERNAME)); + config.setPassword(basicConfig.getBasicConfiguration(PROP_CONFIG_CLIENT_AUTH_HTTP_PASSORD)); + + // set SSL Client auth. informations if available + config.buildKeyStoreConfig( + basicConfig.getBasicConfiguration( + PROP_CONFIG_CLIENT_AUTH_SSL_KEYSTORE_TYPE, KeyStoreType.PKCS12.getKeyStoreType()), + basicConfig.getBasicConfiguration( + PROP_CONFIG_CLIENT_AUTH_SSL_KEYSTORE_PATH, StringUtils.EMPTY), + basicConfig.getBasicConfiguration( + PROP_CONFIG_CLIENT_AUTH_SSL_KEYSTORE_PASSORD, StringUtils.EMPTY), + basicConfig.getBasicConfiguration( + PROP_CONFIG_CLIENT_AUTH_SSL_KEYSTORE_NAME, StringUtils.EMPTY)); + + config.setSslKeyAlias( + basicConfig.getBasicConfiguration(PROP_CONFIG_CLIENT_AUTH_SSL_KEY_ALIAS)); + config.setSslKeyPassword( + basicConfig.getBasicConfiguration(PROP_CONFIG_CLIENT_AUTH_SSL_KEY_PASSWORD)); + + config.setDisableHostnameValidation(basicConfig.getBasicConfigurationBoolean( + PROP_CONFIG_CLIENT_HTTP_SSL_HOSTNAMEVERIFIER_TRUSTALL, false)); + + // validate configuration object + config.validate(); + + return config; + } + + private void injectBasicAuthenticationIfRequired(HttpClientBuilder builder, + final HttpClientConfiguration httpClientConfig) { + if (httpClientConfig.getAuthMode().equals(HttpClientConfiguration.ClientAuthMode.PASSWORD)) { + final CredentialsProvider provider = new BasicCredentialsProvider(); + log.trace("Injecting basic authentication with username: {} and password: {}", + httpClientConfig.getUsername(), httpClientConfig.getPassword()); + final UsernamePasswordCredentials credentials = new UsernamePasswordCredentials( + httpClientConfig.getUsername(), httpClientConfig.getPassword()); + + final AuthScope scope = new AuthScope(AuthScope.ANY_HOST, AuthScope.ANY_PORT, AuthScope.ANY_REALM); + provider.setCredentials(scope, credentials); + builder.setDefaultCredentialsProvider(provider); + log.info("Basic http authentication was injected with username: {}", + httpClientConfig.getUsername()); + + } else { + log.trace("Injection of Http Basic authentication was skipped"); + + } + + } + + @Nonnull + private LayeredConnectionSocketFactory getSslContext(final HttpClientConfiguration httpClientConfig) + throws EaafException { + SSLContext sslContext = null; + try { + if (httpClientConfig.getAuthMode().equals(HttpClientConfiguration.ClientAuthMode.SSL)) { + log.debug("Open keyStore with type: {}", httpClientConfig.getKeyStoreConfig().getKeyStoreType()); + final KeyStore keyStore = keyStoreFactory.buildNewKeyStore(httpClientConfig.getKeyStoreConfig()) + .getFirst(); + + log.trace("Injecting SSL client-authentication into http client ... "); + sslContext = HttpUtils.buildSslContextWithSslClientAuthentication(keyStore, + httpClientConfig.getSslKeyAlias(), httpClientConfig.getSslKeyPassword(), + httpClientConfig.isDisableTlsHostCertificateValidation(), httpClientConfig.getFriendlyName()); + + } else { + log.trace("Initializing default SSL Context ... "); + sslContext = SSLContext.getDefault(); + + } + + // set hostname verifier + HostnameVerifier hostnameVerifier = null; + if (httpClientConfig.isDisableHostnameValidation()) { + hostnameVerifier = new NoopHostnameVerifier(); + log.warn("HTTP client-builder deactivates SSL Host-name verification!"); + + } + + final LayeredConnectionSocketFactory sslSocketFactory = + new SSLConnectionSocketFactory(sslContext, hostnameVerifier); + log.debug("HTTP client-builder successfuly initialized"); + return sslSocketFactory; + + } catch (final NoSuchAlgorithmException e) { + log.warn("HTTP client-builder can NOT initialze SSL-Context", e); + throw new EaafFactoryException(ERROR_03, new Object[] { + httpClientConfig.getFriendlyName(), e.getMessage()}, e); + + } + + } + + private void injectDefaultConnectionPoolIfRequired( + HttpClientBuilder builder, final LayeredConnectionSocketFactory sslConnectionFactory) { + if (basicConfig.getBasicConfigurationBoolean(PROP_CONFIG_CLIENT_HTTP_CONNECTION_POOL_USE, + true)) { + PoolingHttpClientConnectionManager pool; + + // set socketFactoryRegistry if SSLConnectionFactory is Set + if (sslConnectionFactory != null) { + final Registry socketFactoryRegistry = + RegistryBuilder.create() + .register("http", PlainConnectionSocketFactory.getSocketFactory()) + .register("https", sslConnectionFactory).build(); + log.trace("Inject SSLSocketFactory into pooled connection"); + pool = new PoolingHttpClientConnectionManager(socketFactoryRegistry); + + } else { + pool = new PoolingHttpClientConnectionManager(); + + } + + pool.setDefaultMaxPerRoute(Integer.parseInt( + basicConfig.getBasicConfiguration(PROP_CONFIG_CLIENT_HTTP_CONNECTION_POOL_MAXPERROUTE, + DEFAULT_CONFIG_CLIENT_HTTP_CONNECTION_POOL_MAXPERROUTE))); + pool.setMaxTotal(Integer.parseInt( + basicConfig.getBasicConfiguration(PROP_CONFIG_CLIENT_HTTP_CONNECTION_POOL_MAXTOTAL, + DEFAULT_CONFIG_CLIENT_HTTP_CONNECTION_POOL_MAXTOTAL))); + + builder.setConnectionManager(pool); + log.debug("Initalize http-client pool with, maxTotal: {} maxPerRoute: {}", pool.getMaxTotal(), + pool.getDefaultMaxPerRoute()); + + } else if (sslConnectionFactory != null) { + log.trace("Inject SSLSocketFactory without connection pool"); + builder.setSSLSocketFactory(sslConnectionFactory); + + } + + } + + private RequestConfig buildDefaultRequestConfig() { + final RequestConfig requestConfig = + RequestConfig.custom() + .setConnectTimeout( + Integer.parseInt(basicConfig.getBasicConfiguration( + PROP_CONFIG_CLIENT_HTTP_CONNECTION_TIMEOUT_CONNECTION, + DEFAULT_CONFIG_CLIENT_HTTP_CONNECTION_TIMEOUT_CONNECTION)) * 1000) + .setConnectionRequestTimeout(Integer.parseInt(basicConfig.getBasicConfiguration( + PROP_CONFIG_CLIENT_HTTP_CONNECTION_TIMEOUT_REQUEST, + DEFAULT_CONFIG_CLIENT_HTTP_CONNECTION_TIMEOUT_REQUEST)) * 1000) + .setSocketTimeout(Integer.parseInt( + basicConfig.getBasicConfiguration(PROP_CONFIG_CLIENT_HTTP_CONNECTION_TIMEOUT_SOCKET, + DEFAULT_CONFIG_CLIENT_HTTP_CONNECTION_TIMEOUT_SOCKET)) + * 1000) + .build(); + return requestConfig; + + } + + private static RedirectStrategy buildRedirectStrategy(final boolean followRedirects) { + RedirectStrategy redirectStrategy = new DefaultRedirectStrategy(); + if (!followRedirects) { + redirectStrategy = new RedirectStrategy() { + + @Override + public boolean isRedirected(final HttpRequest request, final HttpResponse response, + final HttpContext context) throws ProtocolException { + return false; + } + + @Override + public HttpUriRequest getRedirect(final HttpRequest request, final HttpResponse response, + final HttpContext context) throws ProtocolException { + return null; + } + }; + } + return redirectStrategy; + + } + +} diff --git a/eaaf_core_utils/src/main/java/at/gv/egiz/eaaf/core/impl/http/HttpUtils.java b/eaaf_core_utils/src/main/java/at/gv/egiz/eaaf/core/impl/http/HttpUtils.java new file mode 100644 index 00000000..2d514912 --- /dev/null +++ b/eaaf_core_utils/src/main/java/at/gv/egiz/eaaf/core/impl/http/HttpUtils.java @@ -0,0 +1,194 @@ +/* + * Copyright 2014 Federal Chancellery Austria MOA-ID has been developed in a cooperation between + * BRZ, the Federal Chancellery Austria - ICT staff unit, and Graz University of Technology. + * + * Licensed under the EUPL, Version 1.1 or - as soon they will be approved by the European + * Commission - subsequent versions of the EUPL (the "Licence"); You may not use this work except in + * compliance with the Licence. You may obtain a copy of the Licence at: http://www.osor.eu/eupl/ + * + * Unless required by applicable law or agreed to in writing, software distributed under the Licence + * is distributed on an "AS IS" basis, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express + * or implied. See the Licence for the specific language governing permissions and limitations under + * the Licence. + * + * This product combines work with different licenses. See the "NOTICE" text file for details on the + * various modules and licenses. The "NOTICE" text file is part of the distribution. Any derivative + * works that you distribute must include a readable copy of the "NOTICE" text file. +*/ + +package at.gv.egiz.eaaf.core.impl.http; + +import java.security.KeyManagementException; +import java.security.KeyStore; +import java.security.KeyStoreException; +import java.security.NoSuchAlgorithmException; +import java.security.UnrecoverableKeyException; + +import javax.annotation.Nonnull; +import javax.annotation.Nullable; +import javax.net.ssl.SSLContext; +import javax.servlet.http.HttpServletRequest; + +import at.gv.egiz.eaaf.core.exceptions.EaafConfigurationException; +import at.gv.egiz.eaaf.core.exceptions.EaafFactoryException; + +import org.apache.commons.lang3.StringUtils; +import org.apache.http.conn.ssl.TrustAllStrategy; +import org.apache.http.ssl.SSLContextBuilder; +import org.apache.http.ssl.SSLContexts; +import org.apache.http.ssl.TrustStrategy; + +import lombok.extern.slf4j.Slf4j; + +@Slf4j +public class HttpUtils { + + private static final String ERROR_03 = "internal.httpclient.03"; + + /** + * Helper method to retrieve server URL including context path. + * + * @param request HttpServletRequest + * @return Server URL including context path (e.g. + * http://localhost:8443/moa-id-auth + */ + public static String getBaseUrl(final HttpServletRequest request) { + final StringBuffer buffer = new StringBuffer(getServerUrl(request)); + + // add context path if available + final String contextPath = request.getContextPath(); + if (!StringUtils.isEmpty(contextPath)) { + buffer.append(contextPath); + } + + return buffer.toString(); + } + + /** + * Helper method to retrieve server URL. + * + * @param request HttpServletRequest + * @return Server URL (e.g. http://localhost:8443) + */ + public static String getServerUrl(final HttpServletRequest request) { + final StringBuffer buffer = new StringBuffer(); + + // get protocol + final String protocol = request.getScheme(); + buffer.append(protocol).append("://"); + + // server name + buffer.append(request.getServerName()); + + // add port if necessary + final int port = request.getServerPort(); + if (protocol.equals("http") && port != 80 || protocol.equals("https") && port != 443) { + buffer.append(':'); + buffer.append(port); + } + + return buffer.toString(); + } + + /** + * Extract the IDP PublicURLPrefix from authrequest. + * + * @param req HttpServletRequest + * @return PublicURLPrefix which ends always without / + */ + public static String extractAuthUrlFromRequest(final HttpServletRequest req) { + String authUrl = req.getScheme() + "://" + req.getServerName(); + if (req.getScheme().equalsIgnoreCase("https") && req.getServerPort() != 443 + || req.getScheme().equalsIgnoreCase("http") && req.getServerPort() != 80) { + authUrl = authUrl.concat(":" + req.getServerPort()); + } + authUrl = authUrl.concat(req.getContextPath()); + return authUrl; + + } + + /** + * Extract the IDP requested URL from authrequest. + * + * @param req HttpServletRequest + * @return RequestURL which ends always without / + */ + public static String extractAuthServletPathFromRequest(final HttpServletRequest req) { + return extractAuthUrlFromRequest(req).concat(req.getServletPath()); + + } + + /** + * Add a http GET parameter to URL. + * + * @param url URL + * @param paramname Name of the parameter. + * @param paramvalue Value of the parameter. + * @return + */ + public static String addUrlParameter(final String url, final String paramname, + final String paramvalue) { + final String param = paramname + "=" + paramvalue; + if (url.indexOf("?") < 0) { + return url + "?" + param; + } else { + return url + "&" + param; + } + } + + /** + * Initialize a {@link SSLContext} with a {@link KeyStore} that uses X509 Client + * authentication. + * + * @param keyStore KeyStore with private keys that should be + * used + * @param keyAlias Alias of the key that should be used. If + * the alias is null, than the first key that + * is found will be selected. + * @param keyPasswordString Password of the Key in this keystore + * @param trustAllServerCertificates Deactivate SSL server-certificate + * validation + * @param friendlyName FriendlyName of the http client for logging + * purposes + * @return {@link SSLContext} with X509 client authentication + * @throws EaafConfigurationException In case of a configuration error + * @throws EaafFactoryException In case of a {@link SSLContext} + * initialization error + */ + public static SSLContext buildSslContextWithSslClientAuthentication(@Nonnull final KeyStore keyStore, + @Nullable String keyAlias, @Nullable String keyPasswordString, + boolean trustAllServerCertificates, @Nonnull String friendlyName) + throws EaafConfigurationException, EaafFactoryException { + try { + log.trace("Open SSL Client-Auth keystore with password: {}", keyPasswordString); + final char[] keyPassword = keyPasswordString == null ? StringUtils.EMPTY.toCharArray() + : keyPasswordString.toCharArray(); + + SSLContextBuilder sslContextBuilder = SSLContexts.custom(); + if (StringUtils.isNotEmpty(keyAlias)) { + sslContextBuilder = sslContextBuilder + .loadKeyMaterial(keyStore, keyPassword, new EaafSslKeySelectionStrategy(keyAlias)); + + } else { + sslContextBuilder = sslContextBuilder + .loadKeyMaterial(keyStore, keyPassword); + + } + + if (trustAllServerCertificates) { + log.warn("Http-client:{} trusts ALL TLS server-certificates!"); + final TrustStrategy trustStrategy = new TrustAllStrategy(); + sslContextBuilder = sslContextBuilder.loadTrustMaterial(trustStrategy); + + } + + return sslContextBuilder.build(); + + } catch (NoSuchAlgorithmException | KeyManagementException | UnrecoverableKeyException + | KeyStoreException e) { + throw new EaafFactoryException(ERROR_03, new Object[] { friendlyName, e.getMessage() }, e); + + } + } + +} diff --git a/eaaf_core_utils/src/main/java/at/gv/egiz/eaaf/core/impl/http/IHttpClientFactory.java b/eaaf_core_utils/src/main/java/at/gv/egiz/eaaf/core/impl/http/IHttpClientFactory.java new file mode 100644 index 00000000..7ec58d46 --- /dev/null +++ b/eaaf_core_utils/src/main/java/at/gv/egiz/eaaf/core/impl/http/IHttpClientFactory.java @@ -0,0 +1,43 @@ +package at.gv.egiz.eaaf.core.impl.http; + +import javax.annotation.Nonnull; + +import at.gv.egiz.eaaf.core.exceptions.EaafException; + +import org.apache.http.impl.client.CloseableHttpClient; + +public interface IHttpClientFactory { + + /** + * Return an instance of a Apache HTTP client that uses + * default configuration properties from {@link IHttpClientFactory} implementation + * and follows http redirects automatically. + * + * @return http client + */ + @Nonnull + CloseableHttpClient getHttpClient(); + + /** + * Return an instance of a Apache HTTP client that uses + * default configuration properties from {@link IHttpClientFactory} implementation. + * + * @param followRedirects if false, the client does not flow 30x + * http redirects + * @return http client + */ + @Nonnull + CloseableHttpClient getHttpClient(boolean followRedirects); + + /** + * Return an instance of a Apache HTTP client based in {@link HttpClientConfiguration}. + * + * @param config Configuration object for this http client + * @return http client + * @throws EaafException In case of a http-client initialization problem + */ + @Nonnull + CloseableHttpClient getHttpClient(@Nonnull HttpClientConfiguration config) + throws EaafException; + +} diff --git a/eaaf_core_utils/src/main/java/at/gv/egiz/eaaf/core/impl/utils/HttpClientFactory.java b/eaaf_core_utils/src/main/java/at/gv/egiz/eaaf/core/impl/utils/HttpClientFactory.java deleted file mode 100644 index e681e705..00000000 --- a/eaaf_core_utils/src/main/java/at/gv/egiz/eaaf/core/impl/utils/HttpClientFactory.java +++ /dev/null @@ -1,370 +0,0 @@ -package at.gv.egiz.eaaf.core.impl.utils; - -import java.security.KeyManagementException; -import java.security.KeyStore; -import java.security.KeyStoreException; -import java.security.NoSuchAlgorithmException; -import java.security.UnrecoverableKeyException; - -import javax.annotation.PostConstruct; -import javax.net.ssl.HostnameVerifier; -import javax.net.ssl.SSLContext; - -import at.gv.egiz.eaaf.core.api.idp.IConfiguration; -import at.gv.egiz.eaaf.core.exceptions.EaafConfigurationException; -import at.gv.egiz.eaaf.core.exceptions.EaafException; -import at.gv.egiz.eaaf.core.impl.credential.EaafKeyStoreFactory; -import at.gv.egiz.eaaf.core.impl.credential.KeyStoreConfiguration; -import at.gv.egiz.eaaf.core.impl.credential.KeyStoreConfiguration.KeyStoreType; - -import org.apache.commons.lang3.StringUtils; -import org.apache.http.HttpRequest; -import org.apache.http.HttpResponse; -import org.apache.http.ProtocolException; -import org.apache.http.auth.AuthScope; -import org.apache.http.auth.UsernamePasswordCredentials; -import org.apache.http.client.CredentialsProvider; -import org.apache.http.client.RedirectStrategy; -import org.apache.http.client.config.RequestConfig; -import org.apache.http.client.methods.HttpUriRequest; -import org.apache.http.config.Registry; -import org.apache.http.config.RegistryBuilder; -import org.apache.http.conn.socket.ConnectionSocketFactory; -import org.apache.http.conn.socket.LayeredConnectionSocketFactory; -import org.apache.http.conn.socket.PlainConnectionSocketFactory; -import org.apache.http.conn.ssl.NoopHostnameVerifier; -import org.apache.http.conn.ssl.SSLConnectionSocketFactory; -import org.apache.http.impl.client.BasicCredentialsProvider; -import org.apache.http.impl.client.CloseableHttpClient; -import org.apache.http.impl.client.DefaultRedirectStrategy; -import org.apache.http.impl.client.HttpClientBuilder; -import org.apache.http.impl.client.HttpClients; -import org.apache.http.impl.conn.PoolingHttpClientConnectionManager; -import org.apache.http.protocol.HttpContext; -import org.apache.http.ssl.SSLContexts; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.core.io.ResourceLoader; - -import lombok.extern.slf4j.Slf4j; - -@Slf4j -public class HttpClientFactory implements IHttpClientFactory { - - @Autowired(required = true) - private IConfiguration basicConfig; - - @Autowired(required = true) - ResourceLoader resourceLoader; - - @Autowired private EaafKeyStoreFactory keyStoreFactory; - - public static final String PROP_CONFIG_CLIENT_HTTP_CONNECTION_POOL_USE = - "client.http.connection.pool.use"; - public static final String PROP_CONFIG_CLIENT_HTTP_CONNECTION_POOL_MAXTOTAL = - "client.http.connection.pool.maxtotal"; - public static final String PROP_CONFIG_CLIENT_HTTP_CONNECTION_POOL_MAXPERROUTE = - "client.http.connection.pool.maxperroute"; - public static final String PROP_CONFIG_CLIENT_HTTP_CONNECTION_TIMEOUT_SOCKET = - "client.http.connection.timeout.socket"; - public static final String PROP_CONFIG_CLIENT_HTTP_CONNECTION_TIMEOUT_CONNECTION = - "client.http.connection.timeout.connection"; - public static final String PROP_CONFIG_CLIENT_HTTP_CONNECTION_TIMEOUT_REQUEST = - "client.http.connection.timeout.request"; - public static final String PROP_CONFIG_CLIENT_HTTP_SSL_HOSTNAMEVERIFIER_TRUSTALL = - "client.http.ssl.hostnameverifier.trustall"; - - public static final String PROP_CONFIG_CLIENT_MODE = "client.authmode"; - public static final String PROP_CONFIG_CLIENT_AUTH_HTTP_USERNAME = "client.auth.http.username"; - public static final String PROP_CONFIG_CLIENT_AUTH_HTTP_PASSORD = "client.auth.http.password"; - public static final String PROP_CONFIG_CLIENT_AUTH_SSL_KEYSTORE_PATH = - "client.auth.ssl.keystore.path"; - public static final String PROP_CONFIG_CLIENT_AUTH_SSL_KEYSTORE_PASSORD = - "client.auth.ssl.keystore.password"; - private static final String PROP_CONFIG_CLIENT_AUTH_SSL_KEYSTORE_NAME = - "client.auth.ssl.keystore.name"; - public static final String PROP_CONFIG_CLIENT_AUTH_SSL_KEYSTORE_TYPE = - "client.auth.ssl.keystore.type"; - public static final String PROP_CONFIG_CLIENT_AUTH_SSL_KEY_PASSWORD = - "client.auth.ssl.key.password"; - public static final String PROP_CONFIG_CLIENT_AUTH_SSL_KEY_ALIAS = "client.auth.ssl.key.alias"; - - // default configuration values - public static final String DEFAULT_CONFIG_CLIENT_HTTP_CONNECTION_TIMEOUT_SOCKET = "15"; - public static final String DEFAULT_CONFIG_CLIENT_HTTP_CONNECTION_TIMEOUT_CONNECTION = "15"; - public static final String DEFAULT_CONFIG_CLIENT_HTTP_CONNECTION_TIMEOUT_REQUEST = "30"; - public static final String DEFAULT_CONFIG_CLIENT_HTTP_CONNECTION_POOL_MAXTOTAL = "500"; - public static final String DEFAULT_CONFIG_CLIENT_HTTP_CONNECTION_POOL_MAXPERROUTE = "100"; - - public enum ClientAuthMode { - NONE("none"), PASSWORD("password"), SSL("ssl"); - - private final String mode; - - ClientAuthMode(final String mode) { - this.mode = mode; - } - - /** - * Get the PVP mode. - * - * @return - */ - public String getMode() { - return this.mode; - } - - /** - * Get http-client authentication mode from String representation. - * - * @param s Config parameter - * @return - */ - public static ClientAuthMode fromString(final String s) { - try { - return ClientAuthMode.valueOf(s.toUpperCase()); - - } catch (IllegalArgumentException | NullPointerException e) { - return null; - } - } - - @Override - public String toString() { - return getMode(); - - } - - } - - private HttpClientBuilder httpClientBuilder = null; - - /* - * (non-Javadoc) - * - * @see at.gv.egiz.eaaf.core.impl.utils.IHttpClientFactory#getHttpClient() - */ - @Override - public CloseableHttpClient getHttpClient() { - return getHttpClient(true); - - } - - @Override - public CloseableHttpClient getHttpClient(final boolean followRedirects) { - RedirectStrategy redirectStrategy = new DefaultRedirectStrategy(); - if (!followRedirects) { - redirectStrategy = new RedirectStrategy() { - - @Override - public boolean isRedirected(final HttpRequest request, final HttpResponse response, - final HttpContext context) throws ProtocolException { - return false; - } - - @Override - public HttpUriRequest getRedirect(final HttpRequest request, final HttpResponse response, - final HttpContext context) throws ProtocolException { - return null; - } - }; - } - - return httpClientBuilder.setRedirectStrategy(redirectStrategy).build(); - - } - - @PostConstruct - private void initalize() { - // initialize http client - log.trace("Initializing HTTP Client-builder ... "); - httpClientBuilder = HttpClients.custom(); - - // set default request configuration - final RequestConfig requestConfig = - RequestConfig.custom() - .setConnectTimeout( - Integer.parseInt(basicConfig.getBasicConfiguration( - PROP_CONFIG_CLIENT_HTTP_CONNECTION_TIMEOUT_CONNECTION, - DEFAULT_CONFIG_CLIENT_HTTP_CONNECTION_TIMEOUT_CONNECTION)) * 1000) - .setConnectionRequestTimeout(Integer.parseInt(basicConfig.getBasicConfiguration( - PROP_CONFIG_CLIENT_HTTP_CONNECTION_TIMEOUT_REQUEST, - DEFAULT_CONFIG_CLIENT_HTTP_CONNECTION_TIMEOUT_REQUEST)) * 1000) - .setSocketTimeout(Integer.parseInt( - basicConfig.getBasicConfiguration(PROP_CONFIG_CLIENT_HTTP_CONNECTION_TIMEOUT_SOCKET, - DEFAULT_CONFIG_CLIENT_HTTP_CONNECTION_TIMEOUT_SOCKET)) - * 1000) - .build(); - httpClientBuilder.setDefaultRequestConfig(requestConfig); - - ClientAuthMode clientAuthMode = ClientAuthMode.fromString( - basicConfig.getBasicConfiguration(PROP_CONFIG_CLIENT_MODE, ClientAuthMode.NONE.getMode())); - if (clientAuthMode == null) { - log.warn("Can Not parse ClientAuthMode! Set mode to default value"); - clientAuthMode = ClientAuthMode.NONE; - - } - - // inject basic http authentication if required - log.info("Client authentication-mode is set to: {}", clientAuthMode); - injectBasicAuthenticationIfRequired(clientAuthMode); - - // inject authentication if required - final LayeredConnectionSocketFactory sslConnectionFactory = getSslContext(clientAuthMode); - - // set pool connection if required - injectConnectionPoolIfRequired(sslConnectionFactory); - - } - - private void injectBasicAuthenticationIfRequired(final ClientAuthMode clientAuthMode) { - if (clientAuthMode.equals(ClientAuthMode.PASSWORD)) { - final CredentialsProvider provider = new BasicCredentialsProvider(); - - final String username = - basicConfig.getBasicConfiguration(PROP_CONFIG_CLIENT_AUTH_HTTP_USERNAME); - final String password = - basicConfig.getBasicConfiguration(PROP_CONFIG_CLIENT_AUTH_HTTP_PASSORD); - - if (StringUtils.isEmpty(username)) { - log.warn("Http basic authentication was activated but NOT username was set!"); - - } - - log.trace("Injecting basic authentication with username: {} and password: {}", username, - password); - final UsernamePasswordCredentials credentials = - new UsernamePasswordCredentials(username, password); - provider.setCredentials(AuthScope.ANY, credentials); - httpClientBuilder.setDefaultCredentialsProvider(provider); - log.info("Basic http authentication was injected with username: {}", username); - - } else { - log.trace("Injection of Http Basic authentication was skipped"); - - } - - } - - private SSLContext buildSslContextWithSslClientAuthentication() - throws KeyManagementException, UnrecoverableKeyException, NoSuchAlgorithmException, - KeyStoreException, EaafConfigurationException { - log.trace("Injecting SSL client-authentication into http client ... "); - final KeyStore keystore = getSslAuthKeyStore(); - final String keyPasswordString = - basicConfig.getBasicConfiguration(PROP_CONFIG_CLIENT_AUTH_SSL_KEY_PASSWORD); - log.trace("Open SSL Client-Auth keystore with password: {}", keyPasswordString); - final char[] keyPassword = keyPasswordString == null ? StringUtils.EMPTY.toCharArray() - : keyPasswordString.toCharArray(); - return SSLContexts.custom().loadKeyMaterial(keystore, keyPassword).build(); - - } - - private KeyStore getSslAuthKeyStore() throws EaafConfigurationException { - final String keyStoreType = basicConfig.getBasicConfiguration( - PROP_CONFIG_CLIENT_AUTH_SSL_KEYSTORE_TYPE, KeyStoreType.PKCS12.getKeyStoreType()); - final String localKeyStorePath = basicConfig - .getBasicConfiguration(PROP_CONFIG_CLIENT_AUTH_SSL_KEYSTORE_PATH, StringUtils.EMPTY); - final String keyStorePassword = basicConfig - .getBasicConfiguration(PROP_CONFIG_CLIENT_AUTH_SSL_KEYSTORE_PASSORD, StringUtils.EMPTY); - final String keyStoreName = basicConfig - .getBasicConfiguration(PROP_CONFIG_CLIENT_AUTH_SSL_KEYSTORE_NAME, StringUtils.EMPTY); - - try { - final KeyStoreConfiguration keyStoreConfig = new KeyStoreConfiguration(); - keyStoreConfig.setKeyStoreType(keyStoreType); - keyStoreConfig.setFriendlyName("HttpClient Keystore"); - keyStoreConfig.setSoftKeyStoreFilePath(localKeyStorePath); - keyStoreConfig.setSoftKeyStorePassword(keyStorePassword); - keyStoreConfig.setKeyStoreName(keyStoreName); - - log.debug("Open keyStore with type: {}", keyStoreType); - final KeyStore keyStore = keyStoreFactory.buildNewKeyStore(keyStoreConfig).getFirst(); - - return keyStore; - - } catch (final EaafException e) { - log.warn("Can NOT read keyStore: {} from filesystem", localKeyStorePath, null, e); - throw new EaafConfigurationException("Can NOT read keyStore: {} from filesystem", - new Object[] { localKeyStorePath }, e); - - } - - } - - private LayeredConnectionSocketFactory getSslContext(final ClientAuthMode clientAuthMode) { - SSLContext sslContext = null; - try { - if (clientAuthMode.equals(ClientAuthMode.SSL)) { - sslContext = buildSslContextWithSslClientAuthentication(); - - } else { - log.trace("Initializing default SSL Context ... "); - sslContext = SSLContext.getDefault(); - - } - - // set hostname verifier - HostnameVerifier hostnameVerifier = null; - if (basicConfig.getBasicConfigurationBoolean( - PROP_CONFIG_CLIENT_HTTP_SSL_HOSTNAMEVERIFIER_TRUSTALL, false)) { - hostnameVerifier = new NoopHostnameVerifier(); - log.warn("HTTP client-builder deactivates SSL Host-name verification!"); - - } - - final LayeredConnectionSocketFactory sslSocketFactory = - new SSLConnectionSocketFactory(sslContext, hostnameVerifier); - - return sslSocketFactory; - - } catch (final NoSuchAlgorithmException | KeyManagementException | UnrecoverableKeyException - | KeyStoreException | EaafConfigurationException e) { - log.warn("HTTP client-builder can NOT initialze SSL-Context", e); - - } - - log.info("HTTP client-builder successfuly initialized"); - return null; - - } - - private void injectConnectionPoolIfRequired( - final LayeredConnectionSocketFactory sslConnectionFactory) { - if (basicConfig.getBasicConfigurationBoolean(PROP_CONFIG_CLIENT_HTTP_CONNECTION_POOL_USE, - true)) { - PoolingHttpClientConnectionManager pool; - - // set socketFactoryRegistry if SSLConnectionFactory is Set - if (sslConnectionFactory != null) { - final Registry socketFactoryRegistry = - RegistryBuilder.create() - .register("http", PlainConnectionSocketFactory.getSocketFactory()) - .register("https", sslConnectionFactory).build(); - log.trace("Inject SSLSocketFactory into pooled connection"); - pool = new PoolingHttpClientConnectionManager(socketFactoryRegistry); - - } else { - pool = new PoolingHttpClientConnectionManager(); - - } - - pool.setDefaultMaxPerRoute(Integer.parseInt( - basicConfig.getBasicConfiguration(PROP_CONFIG_CLIENT_HTTP_CONNECTION_POOL_MAXPERROUTE, - DEFAULT_CONFIG_CLIENT_HTTP_CONNECTION_POOL_MAXPERROUTE))); - pool.setMaxTotal(Integer.parseInt( - basicConfig.getBasicConfiguration(PROP_CONFIG_CLIENT_HTTP_CONNECTION_POOL_MAXTOTAL, - DEFAULT_CONFIG_CLIENT_HTTP_CONNECTION_POOL_MAXTOTAL))); - - httpClientBuilder.setConnectionManager(pool); - log.debug("Initalize http-client pool with, maxTotal: {} maxPerRoute: {}", pool.getMaxTotal(), - pool.getDefaultMaxPerRoute()); - - } else if (sslConnectionFactory != null) { - log.trace("Inject SSLSocketFactory without connection pool"); - httpClientBuilder.setSSLSocketFactory(sslConnectionFactory); - - } - - } - -} diff --git a/eaaf_core_utils/src/main/java/at/gv/egiz/eaaf/core/impl/utils/HttpUtils.java b/eaaf_core_utils/src/main/java/at/gv/egiz/eaaf/core/impl/utils/HttpUtils.java deleted file mode 100644 index 66356ba0..00000000 --- a/eaaf_core_utils/src/main/java/at/gv/egiz/eaaf/core/impl/utils/HttpUtils.java +++ /dev/null @@ -1,118 +0,0 @@ -/* - * Copyright 2014 Federal Chancellery Austria MOA-ID has been developed in a cooperation between - * BRZ, the Federal Chancellery Austria - ICT staff unit, and Graz University of Technology. - * - * Licensed under the EUPL, Version 1.1 or - as soon they will be approved by the European - * Commission - subsequent versions of the EUPL (the "Licence"); You may not use this work except in - * compliance with the Licence. You may obtain a copy of the Licence at: http://www.osor.eu/eupl/ - * - * Unless required by applicable law or agreed to in writing, software distributed under the Licence - * is distributed on an "AS IS" basis, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express - * or implied. See the Licence for the specific language governing permissions and limitations under - * the Licence. - * - * This product combines work with different licenses. See the "NOTICE" text file for details on the - * various modules and licenses. The "NOTICE" text file is part of the distribution. Any derivative - * works that you distribute must include a readable copy of the "NOTICE" text file. -*/ - -package at.gv.egiz.eaaf.core.impl.utils; - -import javax.servlet.http.HttpServletRequest; - -import org.apache.commons.lang3.StringUtils; - -public class HttpUtils { - - /** - * Helper method to retrieve server URL including context path. - * - * @param request HttpServletRequest - * @return Server URL including context path (e.g. - * http://localhost:8443/moa-id-auth - */ - public static String getBaseUrl(final HttpServletRequest request) { - final StringBuffer buffer = new StringBuffer(getServerUrl(request)); - - // add context path if available - final String contextPath = request.getContextPath(); - if (!StringUtils.isEmpty(contextPath)) { - buffer.append(contextPath); - } - - return buffer.toString(); - } - - /** - * Helper method to retrieve server URL. - * - * @param request HttpServletRequest - * @return Server URL (e.g. http://localhost:8443) - */ - public static String getServerUrl(final HttpServletRequest request) { - final StringBuffer buffer = new StringBuffer(); - - // get protocol - final String protocol = request.getScheme(); - buffer.append(protocol).append("://"); - - // server name - buffer.append(request.getServerName()); - - // add port if necessary - final int port = request.getServerPort(); - if (protocol.equals("http") && port != 80 || protocol.equals("https") && port != 443) { - buffer.append(':'); - buffer.append(port); - } - - return buffer.toString(); - } - - /** - * Extract the IDP PublicURLPrefix from authrequest. - * - * @param req HttpServletRequest - * @return PublicURLPrefix which ends always without / - */ - public static String extractAuthUrlFromRequest(final HttpServletRequest req) { - String authUrl = req.getScheme() + "://" + req.getServerName(); - if (req.getScheme().equalsIgnoreCase("https") && req.getServerPort() != 443 - || req.getScheme().equalsIgnoreCase("http") && req.getServerPort() != 80) { - authUrl = authUrl.concat(":" + req.getServerPort()); - } - authUrl = authUrl.concat(req.getContextPath()); - return authUrl; - - } - - /** - * Extract the IDP requested URL from authrequest. - * - * @param req HttpServletRequest - * @return RequestURL which ends always without / - */ - public static String extractAuthServletPathFromRequest(final HttpServletRequest req) { - return extractAuthUrlFromRequest(req).concat(req.getServletPath()); - - } - - /** - * Add a http GET parameter to URL. - * - * @param url URL - * @param paramname Name of the parameter. - * @param paramvalue Value of the parameter. - * @return - */ - public static String addUrlParameter(final String url, final String paramname, - final String paramvalue) { - final String param = paramname + "=" + paramvalue; - if (url.indexOf("?") < 0) { - return url + "?" + param; - } else { - return url + "&" + param; - } - } - -} diff --git a/eaaf_core_utils/src/main/java/at/gv/egiz/eaaf/core/impl/utils/IHttpClientFactory.java b/eaaf_core_utils/src/main/java/at/gv/egiz/eaaf/core/impl/utils/IHttpClientFactory.java deleted file mode 100644 index f922e1ac..00000000 --- a/eaaf_core_utils/src/main/java/at/gv/egiz/eaaf/core/impl/utils/IHttpClientFactory.java +++ /dev/null @@ -1,24 +0,0 @@ -package at.gv.egiz.eaaf.core.impl.utils; - -import org.apache.http.impl.client.CloseableHttpClient; - -public interface IHttpClientFactory { - - /** - * Return an instance of a Apache HTTP client that follows http redirects - * automatically. - * - * @return - */ - CloseableHttpClient getHttpClient(); - - /** - * Return an instance of a Apache HTTP client. - * - * @param followRedirects if false, the client does not flow 30x - * http redirects - * @return - */ - CloseableHttpClient getHttpClient(boolean followRedirects); - -} diff --git a/eaaf_core_utils/src/main/resources/messages/eaaf_utils_message.properties b/eaaf_core_utils/src/main/resources/messages/eaaf_utils_message.properties index f531e02d..b20c5f63 100644 --- a/eaaf_core_utils/src/main/resources/messages/eaaf_utils_message.properties +++ b/eaaf_core_utils/src/main/resources/messages/eaaf_utils_message.properties @@ -12,4 +12,7 @@ internal.keystore.07=Validation of KeyStore: {0} failed. Reason: {1} internal.keystore.08=Can not access Key: {1} in KeyStore: {0} internal.keystore.09=Can not access Key: {1} in KeyStore: {0} Reason: {2} - +internal.httpclient.00=HttpClient:{0} uses http Basic-Auth, but 'Username' is NOT set +internal.httpclient.01=HttpClient:{0} uses X509 client-auth, but 'KeyStoreConfig' is NOT set +internal.httpclient.02=HttpClient:{0} uses KeyStore:{1}, but 'keyPassword' is NOT set +internal.httpclient.03=Can not initialize SSLContext for HttpClient:{0} Reason:{1} \ No newline at end of file diff --git a/eaaf_core_utils/src/main/resources/spring/eaaf_utils.beans.xml b/eaaf_core_utils/src/main/resources/spring/eaaf_utils.beans.xml index ab631e34..aa5a50de 100644 --- a/eaaf_core_utils/src/main/resources/spring/eaaf_utils.beans.xml +++ b/eaaf_core_utils/src/main/resources/spring/eaaf_utils.beans.xml @@ -11,8 +11,8 @@ http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-3.0.xsd" > - + class="at.gv.egiz.eaaf.core.impl.http.HttpClientFactory" /> + diff --git a/eaaf_core_utils/src/test/java/at/gv/egiz/eaaf/core/test/http/HttpClientFactoryTest.java b/eaaf_core_utils/src/test/java/at/gv/egiz/eaaf/core/test/http/HttpClientFactoryTest.java new file mode 100644 index 00000000..b2f0f80e --- /dev/null +++ b/eaaf_core_utils/src/test/java/at/gv/egiz/eaaf/core/test/http/HttpClientFactoryTest.java @@ -0,0 +1,328 @@ +package at.gv.egiz.eaaf.core.test.http; + +import java.io.IOException; +import java.net.HttpURLConnection; +import java.net.InetAddress; +import java.security.KeyStore; +import java.security.KeyStoreException; +import java.security.Provider; +import java.security.cert.X509Certificate; + +import at.gv.egiz.eaaf.core.exceptions.EaafException; +import at.gv.egiz.eaaf.core.impl.credential.EaafKeyStoreFactory; +import at.gv.egiz.eaaf.core.impl.data.Pair; +import at.gv.egiz.eaaf.core.impl.http.HttpClientConfiguration; +import at.gv.egiz.eaaf.core.impl.http.IHttpClientFactory; + +import org.apache.commons.lang3.RandomStringUtils; +import org.apache.http.client.ClientProtocolException; +import org.apache.http.client.methods.CloseableHttpResponse; +import org.apache.http.client.methods.HttpGet; +import org.apache.http.client.methods.HttpUriRequest; +import org.apache.http.impl.client.CloseableHttpClient; +import org.junit.After; +import org.junit.Assert; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.test.context.ContextConfiguration; +import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; + +import okhttp3.HttpUrl; +import okhttp3.mockwebserver.MockResponse; +import okhttp3.mockwebserver.MockWebServer; +import okhttp3.mockwebserver.RecordedRequest; +import okhttp3.tls.HandshakeCertificates; +import okhttp3.tls.HeldCertificate; + +@RunWith(SpringJUnit4ClassRunner.class) +@ContextConfiguration("/spring/test_eaaf_pvp_not_lazy.beans.xml") +public class HttpClientFactoryTest { + + @Autowired private EaafKeyStoreFactory keyStoreFactory; + @Autowired private IHttpClientFactory httpClientFactory; + + private MockWebServer mockWebServer = null; + private HttpUrl mockServerUrl; + + /** + * JUnit test set-up. + * + */ + @Before + public void setup() { + + } + + /** + * jUnit test shutdown. + * + * @throws IOException In case of an mockWebServer error + */ + @After + public void shutdown() throws IOException { + if (mockWebServer != null) { + mockWebServer.shutdown(); + mockWebServer = null; + + } + + } + + @Test + public void getDefaultClient() { + final CloseableHttpClient client = httpClientFactory.getHttpClient(); + Assert.assertNotNull("httpClient", client); + + } + + @Test + public void getDefaultClientNoRedirect() { + final CloseableHttpClient client = httpClientFactory.getHttpClient(false); + Assert.assertNotNull("httpClient", client); + + } + + @Test + public void getCustomClientsDefault() throws EaafException { + final HttpClientConfiguration config = new HttpClientConfiguration("jUnit"); + Assert.assertFalse("Wrong default config - Hostnamevalidation", + config.isDisableHostnameValidation()); + Assert.assertFalse("Wrong default config - TLS Server-certs", + config.isDisableTlsHostCertificateValidation()); + + final CloseableHttpClient client1 = httpClientFactory.getHttpClient(config); + Assert.assertNotNull("first http client", client1); + + final CloseableHttpClient client2 = httpClientFactory.getHttpClient(config); + Assert.assertNotNull("second http client", client2); + + } + + @Test + public void getCustomClientUnknownAuthMethod() throws EaafException { + final HttpClientConfiguration config = new HttpClientConfiguration("jUnit"); + config.setAuthMode(RandomStringUtils.randomAlphabetic(5)); + final CloseableHttpClient client = httpClientFactory.getHttpClient(config); + Assert.assertNotNull("httpClient", client); + + } + + @Test + public void getCustomClientBasicAuth() throws EaafException, ClientProtocolException, IOException, InterruptedException { + final HttpClientConfiguration config = new HttpClientConfiguration("jUnit"); + config.setAuthMode("password"); + config.setUsername("jUnit"); + config.setPassword("password"); + + final CloseableHttpClient client = httpClientFactory.getHttpClient(config); + Assert.assertNotNull("httpClient", client); + + //setup test webserver that requestes http Basic authentication + mockWebServer = new MockWebServer(); + mockServerUrl = mockWebServer.url("/sp/junit"); + mockWebServer.enqueue(new MockResponse() + .setResponseCode(HttpURLConnection.HTTP_UNAUTHORIZED) + .addHeader("www-authenticate: Basic realm=\"protected area\"") + .setBody("Please authenticate.")); + mockWebServer.enqueue(new MockResponse().setResponseCode(200) + .setBody("Successful auth!")); + + //request webservice + final HttpUriRequest httpGet2 = new HttpGet(mockServerUrl.url().toString()); + final CloseableHttpResponse httpResp2 = client.execute(httpGet2); + Assert.assertEquals("http statusCode", 200, httpResp2.getStatusLine().getStatusCode()); + + //check request contains basic authentication after authentication was requested + final RecordedRequest httpReq1 = mockWebServer.takeRequest(); + final RecordedRequest httpReq2 = mockWebServer.takeRequest(); + Assert.assertNull("wrong BasicAuthHeader", httpReq1.getHeader("Authorization")); + Assert.assertNotNull("missing BasicAuthHeader", httpReq2.getHeader("Authorization")); + + } + + @Test + public void getCustomClientBasicAuthNoUsername(){ + final HttpClientConfiguration config = new HttpClientConfiguration("jUnit"); + config.setAuthMode("password"); + try { + httpClientFactory.getHttpClient(config); + Assert.fail("Wrong config not detected"); + + } catch (final EaafException e) { + Assert.assertEquals("Wrong errorCode", "internal.httpclient.00", e.getErrorId()); + + } + } + + @Test + public void getCustomClientBasicAuthNoPassword() throws EaafException { + final HttpClientConfiguration config = new HttpClientConfiguration("jUnit"); + config.setAuthMode("password"); + config.setUsername(RandomStringUtils.randomAlphabetic(5)); + + final CloseableHttpClient client = httpClientFactory.getHttpClient(config); + Assert.assertNotNull("httpClient", client); + + } + + @Test + public void getCustomClientX509AuthNoKeyStoreConfig(){ + final HttpClientConfiguration config = new HttpClientConfiguration("jUnit"); + config.setAuthMode("ssl"); + try { + httpClientFactory.getHttpClient(config); + Assert.fail("Wrong config not detected"); + + } catch (final EaafException e) { + Assert.assertEquals("Wrong errorCode", "internal.httpclient.01", e.getErrorId()); + + } + } + + @Test + public void getCustomClientX509AuthNoKeyPassword() throws EaafException{ + final HttpClientConfiguration config = new HttpClientConfiguration("jUnit"); + config.setAuthMode("ssl"); + config.buildKeyStoreConfig( + "jks", + "src/test/resources/data/junit.jks", + "password", + null); + + try { + httpClientFactory.getHttpClient(config); + Assert.fail("Wrong config not detected"); + + } catch (final EaafException e) { + Assert.assertEquals("Wrong errorCode", "internal.httpclient.02", e.getErrorId()); + + } + } + + @Test + public void getCustomClientX509Auth() throws EaafException { + final HttpClientConfiguration config = new HttpClientConfiguration("jUnit"); + config.setAuthMode("ssl"); + config.buildKeyStoreConfig( + "jks", + "src/test/resources/data/junit.jks", + "password", + null); + config.setSslKeyPassword("password"); + + final CloseableHttpClient client = httpClientFactory.getHttpClient(config); + Assert.assertNotNull("httpClient", client); + + } + + @Test + public void getCustomClientX509AuthWithAlias() throws EaafException, ClientProtocolException, + IOException, KeyStoreException { + final HttpClientConfiguration config = new HttpClientConfiguration("jUnit"); + config.setAuthMode("ssl"); + config.buildKeyStoreConfig( + "jks", + "src/test/resources/data/junit.jks", + "password", + null); + config.setSslKeyPassword("password"); + config.setSslKeyAlias("sig"); + config.setDisableTlsHostCertificateValidation(true); + + final CloseableHttpClient client = httpClientFactory.getHttpClient(config); + Assert.assertNotNull("httpClient", client); + + //set-up mock-up web-server with SSL client authentication + final Pair sslClientKeyStore = + keyStoreFactory.buildNewKeyStore(config.getKeyStoreConfig()); + final String localhost = InetAddress.getByName("localhost").getCanonicalHostName(); + final HeldCertificate localhostCertificate = new HeldCertificate.Builder() + .addSubjectAlternativeName(localhost) + .build(); + final HandshakeCertificates serverCertificates = new HandshakeCertificates.Builder() + .addTrustedCertificate( + (X509Certificate) sslClientKeyStore.getFirst().getCertificate(config.getSslKeyAlias())) + .heldCertificate(localhostCertificate) + .build(); + mockWebServer = new MockWebServer(); + mockWebServer.useHttps(serverCertificates.sslSocketFactory(), false); + mockWebServer.requireClientAuth(); + mockWebServer.enqueue(new MockResponse().setResponseCode(200) + .setBody("Successful auth!")); + mockServerUrl = mockWebServer.url("/sp/junit"); + + //perform test request + final HttpUriRequest httpGet2 = new HttpGet(mockServerUrl.url().toString()); + final CloseableHttpResponse httpResp2 = client.execute(httpGet2); + Assert.assertEquals("http statusCode", 200, httpResp2.getStatusLine().getStatusCode()); + + } + + @Test + public void getCustomClientX509AuthWrongKeyPassword() throws EaafException{ + final HttpClientConfiguration config = new HttpClientConfiguration("jUnit"); + config.setAuthMode("ssl"); + config.buildKeyStoreConfig( + "jks", + "src/test/resources/data/junit.jks", + "password", + null); + config.setSslKeyPassword(RandomStringUtils.randomAlphanumeric(5)); + config.setSslKeyAlias("sig"); + + try { + httpClientFactory.getHttpClient(config); + Assert.fail("Wrong key password not detected"); + + } catch (final EaafException e) { + Assert.assertEquals("Wrong errorCode", "internal.httpclient.03", e.getErrorId()); + + } + } + + @Test + public void getCustomClientX509AuthWithWrongAlias() throws EaafException, KeyStoreException, ClientProtocolException, IOException { + final HttpClientConfiguration config = new HttpClientConfiguration("jUnit"); + config.setAuthMode("ssl"); + config.buildKeyStoreConfig( + "jks", + "src/test/resources/data/junit.jks", + "password", + null); + config.setSslKeyPassword("password"); + config.setSslKeyAlias(RandomStringUtils.randomAlphabetic(5)); + config.setDisableHostnameValidation(true); + config.setFollowHttpRedirects(false); + config.setDisableTlsHostCertificateValidation(true); + + final CloseableHttpClient client = httpClientFactory.getHttpClient(config); + Assert.assertNotNull("httpClient", client); + + //set-up mock-up web-server with SSL client authentication + final Pair sslClientKeyStore = + keyStoreFactory.buildNewKeyStore(config.getKeyStoreConfig()); + final String localhost = InetAddress.getByName("localhost").getCanonicalHostName(); + final HeldCertificate localhostCertificate = new HeldCertificate.Builder() + .addSubjectAlternativeName(localhost) + .build(); + final HandshakeCertificates serverCertificates = new HandshakeCertificates.Builder() + .addTrustedCertificate( + (X509Certificate) sslClientKeyStore.getFirst().getCertificate("meta")) + .heldCertificate(localhostCertificate) + .build(); + mockWebServer = new MockWebServer(); + mockWebServer.useHttps(serverCertificates.sslSocketFactory(), false); + mockWebServer.requireClientAuth(); + mockWebServer.enqueue(new MockResponse().setResponseCode(200) + .setBody("Successful auth!")); + mockServerUrl = mockWebServer.url("/sp/junit"); + + //perform test request + final HttpUriRequest httpGet2 = new HttpGet(mockServerUrl.url().toString()); + final CloseableHttpResponse httpResp2 = client.execute(httpGet2); + Assert.assertEquals("http statusCode", 200, httpResp2.getStatusLine().getStatusCode()); + + } +} diff --git a/eaaf_modules/eaaf_module_auth_sl20/src/main/java/at/gv/egiz/eaaf/modules/auth/sl20/tasks/AbstractCreateQualEidRequestTask.java b/eaaf_modules/eaaf_module_auth_sl20/src/main/java/at/gv/egiz/eaaf/modules/auth/sl20/tasks/AbstractCreateQualEidRequestTask.java index d1887d5c..1d97b167 100644 --- a/eaaf_modules/eaaf_module_auth_sl20/src/main/java/at/gv/egiz/eaaf/modules/auth/sl20/tasks/AbstractCreateQualEidRequestTask.java +++ b/eaaf_modules/eaaf_module_auth_sl20/src/main/java/at/gv/egiz/eaaf/modules/auth/sl20/tasks/AbstractCreateQualEidRequestTask.java @@ -28,8 +28,8 @@ import at.gv.egiz.eaaf.core.api.idp.ISpConfiguration; import at.gv.egiz.eaaf.core.api.idp.process.ExecutionContext; import at.gv.egiz.eaaf.core.exceptions.EaafAuthenticationException; import at.gv.egiz.eaaf.core.exceptions.TaskExecutionException; +import at.gv.egiz.eaaf.core.impl.http.IHttpClientFactory; import at.gv.egiz.eaaf.core.impl.idp.auth.modules.AbstractAuthServletTask; -import at.gv.egiz.eaaf.core.impl.utils.IHttpClientFactory; import at.gv.egiz.eaaf.core.impl.utils.KeyValueUtils; import at.gv.egiz.eaaf.core.impl.utils.Random; import at.gv.egiz.eaaf.core.impl.utils.TransactionIdUtils; diff --git a/eaaf_modules/eaaf_module_pvp2_core/src/test/java/at/gv/egiz/eaaf/modules/pvp2/test/binding/PostBindingTest.java b/eaaf_modules/eaaf_module_pvp2_core/src/test/java/at/gv/egiz/eaaf/modules/pvp2/test/binding/PostBindingTest.java index 147199a5..ee601c73 100644 --- a/eaaf_modules/eaaf_module_pvp2_core/src/test/java/at/gv/egiz/eaaf/modules/pvp2/test/binding/PostBindingTest.java +++ b/eaaf_modules/eaaf_module_pvp2_core/src/test/java/at/gv/egiz/eaaf/modules/pvp2/test/binding/PostBindingTest.java @@ -11,10 +11,10 @@ import javax.xml.parsers.ParserConfigurationException; import at.gv.egiz.eaaf.core.api.IRequest; import at.gv.egiz.eaaf.core.api.gui.IVelocityGuiBuilderConfiguration; +import at.gv.egiz.eaaf.core.impl.http.IHttpClientFactory; import at.gv.egiz.eaaf.core.impl.idp.module.gui.DummyGuiBuilderConfigurationFactory; import at.gv.egiz.eaaf.core.impl.idp.module.test.TestRequestImpl; import at.gv.egiz.eaaf.core.impl.utils.DomUtils; -import at.gv.egiz.eaaf.core.impl.utils.IHttpClientFactory; import at.gv.egiz.eaaf.modules.pvp2.api.credential.EaafX509Credential; import at.gv.egiz.eaaf.modules.pvp2.api.message.InboundMessageInterface; import at.gv.egiz.eaaf.modules.pvp2.api.metadata.IPvp2MetadataProvider; diff --git a/eaaf_modules/eaaf_module_pvp2_core/src/test/java/at/gv/egiz/eaaf/modules/pvp2/test/binding/RedirectBindingTest.java b/eaaf_modules/eaaf_module_pvp2_core/src/test/java/at/gv/egiz/eaaf/modules/pvp2/test/binding/RedirectBindingTest.java index 37e4acd1..cbeca4c3 100644 --- a/eaaf_modules/eaaf_module_pvp2_core/src/test/java/at/gv/egiz/eaaf/modules/pvp2/test/binding/RedirectBindingTest.java +++ b/eaaf_modules/eaaf_module_pvp2_core/src/test/java/at/gv/egiz/eaaf/modules/pvp2/test/binding/RedirectBindingTest.java @@ -6,8 +6,8 @@ import java.net.URLDecoder; import javax.xml.parsers.ParserConfigurationException; import at.gv.egiz.eaaf.core.api.IRequest; +import at.gv.egiz.eaaf.core.impl.http.IHttpClientFactory; import at.gv.egiz.eaaf.core.impl.idp.module.test.TestRequestImpl; -import at.gv.egiz.eaaf.core.impl.utils.IHttpClientFactory; import at.gv.egiz.eaaf.modules.pvp2.PvpConstants; import at.gv.egiz.eaaf.modules.pvp2.api.credential.EaafX509Credential; import at.gv.egiz.eaaf.modules.pvp2.api.message.InboundMessageInterface; diff --git a/eaaf_modules/eaaf_module_pvp2_core/src/test/java/at/gv/egiz/eaaf/modules/pvp2/test/dummy/DummyMetadataProvider.java b/eaaf_modules/eaaf_module_pvp2_core/src/test/java/at/gv/egiz/eaaf/modules/pvp2/test/dummy/DummyMetadataProvider.java index 64ebe00c..3673859a 100644 --- a/eaaf_modules/eaaf_module_pvp2_core/src/test/java/at/gv/egiz/eaaf/modules/pvp2/test/dummy/DummyMetadataProvider.java +++ b/eaaf_modules/eaaf_module_pvp2_core/src/test/java/at/gv/egiz/eaaf/modules/pvp2/test/dummy/DummyMetadataProvider.java @@ -6,7 +6,7 @@ import java.util.ArrayList; import java.util.List; import at.gv.egiz.eaaf.core.exceptions.EaafConfigurationException; -import at.gv.egiz.eaaf.core.impl.utils.IHttpClientFactory; +import at.gv.egiz.eaaf.core.impl.http.IHttpClientFactory; import at.gv.egiz.eaaf.modules.pvp2.exception.Pvp2MetadataException; import at.gv.egiz.eaaf.modules.pvp2.impl.metadata.AbstractChainingMetadataProvider; import at.gv.egiz.eaaf.modules.pvp2.impl.metadata.PvpMetadataResolverFactory; diff --git a/eaaf_modules/eaaf_module_pvp2_core/src/test/java/at/gv/egiz/eaaf/modules/pvp2/test/metadata/ChainingMetadataTest.java b/eaaf_modules/eaaf_module_pvp2_core/src/test/java/at/gv/egiz/eaaf/modules/pvp2/test/metadata/ChainingMetadataTest.java index 6abe52dc..27c42c57 100644 --- a/eaaf_modules/eaaf_module_pvp2_core/src/test/java/at/gv/egiz/eaaf/modules/pvp2/test/metadata/ChainingMetadataTest.java +++ b/eaaf_modules/eaaf_module_pvp2_core/src/test/java/at/gv/egiz/eaaf/modules/pvp2/test/metadata/ChainingMetadataTest.java @@ -5,8 +5,8 @@ import java.io.UnsupportedEncodingException; import java.util.Arrays; import at.gv.egiz.eaaf.core.api.idp.IConfiguration; +import at.gv.egiz.eaaf.core.impl.http.IHttpClientFactory; import at.gv.egiz.eaaf.core.impl.utils.FileUtils; -import at.gv.egiz.eaaf.core.impl.utils.IHttpClientFactory; import at.gv.egiz.eaaf.modules.pvp2.api.metadata.IPvp2MetadataProvider; import at.gv.egiz.eaaf.modules.pvp2.exception.Pvp2MetadataException; import at.gv.egiz.eaaf.modules.pvp2.impl.metadata.PvpMetadataResolverFactory; diff --git a/eaaf_modules/eaaf_module_pvp2_core/src/test/java/at/gv/egiz/eaaf/modules/pvp2/test/metadata/MetadataResolverTest.java b/eaaf_modules/eaaf_module_pvp2_core/src/test/java/at/gv/egiz/eaaf/modules/pvp2/test/metadata/MetadataResolverTest.java index accdd8b0..da417ec7 100644 --- a/eaaf_modules/eaaf_module_pvp2_core/src/test/java/at/gv/egiz/eaaf/modules/pvp2/test/metadata/MetadataResolverTest.java +++ b/eaaf_modules/eaaf_module_pvp2_core/src/test/java/at/gv/egiz/eaaf/modules/pvp2/test/metadata/MetadataResolverTest.java @@ -10,7 +10,7 @@ import java.util.List; import javax.xml.transform.TransformerException; -import at.gv.egiz.eaaf.core.impl.utils.IHttpClientFactory; +import at.gv.egiz.eaaf.core.impl.http.IHttpClientFactory; import at.gv.egiz.eaaf.modules.pvp2.api.metadata.IPvp2MetadataProvider; import at.gv.egiz.eaaf.modules.pvp2.exception.CredentialsNotAvailableException; import at.gv.egiz.eaaf.modules.pvp2.exception.Pvp2InternalErrorException; diff --git a/eaaf_modules/eaaf_module_pvp2_idp/src/test/resources/spring/test_eaaf_core.beans.xml b/eaaf_modules/eaaf_module_pvp2_idp/src/test/resources/spring/test_eaaf_core.beans.xml index 99552053..fa7b4eae 100644 --- a/eaaf_modules/eaaf_module_pvp2_idp/src/test/resources/spring/test_eaaf_core.beans.xml +++ b/eaaf_modules/eaaf_module_pvp2_idp/src/test/resources/spring/test_eaaf_core.beans.xml @@ -20,7 +20,7 @@ class="at.gv.egiz.eaaf.core.impl.idp.module.gui.DummyGuiBuilderConfigurationFactory" /> + class="at.gv.egiz.eaaf.core.impl.http.HttpClientFactory" /> diff --git a/pom.xml b/pom.xml index 0ce897ec..773e0937 100644 --- a/pom.xml +++ b/pom.xml @@ -78,7 +78,7 @@ 2.22.1 4.12 - 4.0.0 + 4.4.1 1.18.10 @@ -483,6 +483,12 @@ ${com.squareup.okhttp3.version} test + + com.squareup.okhttp3 + okhttp-tls + ${com.squareup.okhttp3.version} + test + at.gv.egiz.eaaf eaaf_core_utils -- cgit v1.2.3