summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--eaaf_core_utils/src/main/java/at/gv/egiz/eaaf/core/impl/http/HttpClientConfiguration.java41
-rw-r--r--eaaf_core_utils/src/main/java/at/gv/egiz/eaaf/core/impl/http/HttpClientFactory.java54
-rw-r--r--eaaf_core_utils/src/main/java/at/gv/egiz/eaaf/core/impl/http/HttpUtils.java51
-rw-r--r--eaaf_core_utils/src/test/java/at/gv/egiz/eaaf/core/test/http/HttpClientFactoryTest.java97
4 files changed, 212 insertions, 31 deletions
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
index f978fa4c..5ecbe1ec 100644
--- 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
@@ -22,6 +22,7 @@ import lombok.extern.slf4j.Slf4j;
public class HttpClientConfiguration {
private static final String MSG_KEYSTORE_NAME = "KeyStore for httpClient: {0}";
+ private static final String MSG_TRUSTSTORE_NAME = "TrustStore for httpClient: {0}";
private static final String ERROR_00 = "internal.httpclient.00";
private static final String ERROR_01 = "internal.httpclient.01";
@@ -64,6 +65,9 @@ public class HttpClientConfiguration {
private String sslKeyPassword;
@Setter
+ private KeyStoreConfiguration trustStoreConfig;
+
+ @Setter
private boolean followHttpRedirects = true;
@Setter
@@ -160,6 +164,14 @@ public class HttpClientConfiguration {
}
}
+ // validate SSL TrustStore if it was set
+ if (this.trustStoreConfig != null) {
+ log.trace("Validating TrustStore: {} for http-client: {} ...",
+ this.trustStoreConfig.getFriendlyName(), this.friendlyName);
+ this.trustStoreConfig.validate();
+
+ }
+
}
/**
@@ -183,6 +195,35 @@ public class HttpClientConfiguration {
}
+ /**
+ * Build a {@link KeyStoreConfiguration} object from trustStore 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 buildTrustStoreConfig(String keyStoreType, String keyStorePath,
+ String keyStorePassword, String keyStoreName) throws EaafConfigurationException {
+ if (StringUtils.isNotEmpty(keyStoreType)) {
+ log.debug("Injecting custom TrustStore configuration for: {} ... ", friendlyName);
+ final KeyStoreConfiguration config = new KeyStoreConfiguration();
+ config.setFriendlyName(MessageFormat.format(MSG_TRUSTSTORE_NAME, friendlyName));
+ config.setKeyStoreType(keyStoreType);
+ config.setSoftKeyStoreFilePath(keyStorePath);
+ config.setSoftKeyStorePassword(keyStorePassword);
+ config.setKeyStoreName(keyStoreName);
+ this.trustStoreConfig = config;
+
+ } else {
+ log.debug("No TrustStoreType. Skipping TrustStore configuration for: {} ... ", friendlyName);
+
+ }
+ }
+
public enum ClientAuthMode {
NONE("none"), PASSWORD("password"), SSL("ssl");
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
index 7a71bfab..ceffe26c 100644
--- 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
@@ -47,6 +47,7 @@ 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 at.gv.egiz.eaaf.core.impl.data.Pair;
import at.gv.egiz.eaaf.core.impl.http.interceptor.PreemptiveAuthInterceptor;
@@ -83,8 +84,6 @@ public class HttpClientFactory implements IHttpClientFactory {
"client.http.connection.retry.count";
public static final String PROP_CONFIG_CLIENT_HTTP_CONNECTION_RETRY_POST =
"client.http.connection.retry.post";
- public static final String PROP_CONFIG_CLIENT_HTTP_SSL_HOSTNAMEVERIFIER_TRUSTALL =
- "client.http.ssl.hostnameverifier.trustall";
public static final String PROP_CONFIG_CLIENT_HTTP_PROXY_HOST_SSL =
"client.http.connection.proxy.host.ssl";
@@ -104,17 +103,28 @@ public class HttpClientFactory implements IHttpClientFactory {
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";
+ "client.http.auth.ssl.keystore.path";
public static final String PROP_CONFIG_CLIENT_AUTH_SSL_KEYSTORE_PASSORD =
- "client.auth.ssl.keystore.password";
+ "client.http.auth.ssl.keystore.password";
private static final String PROP_CONFIG_CLIENT_AUTH_SSL_KEYSTORE_NAME =
- "client.auth.ssl.keystore.name";
+ "client.http.auth.ssl.keystore.name";
public static final String PROP_CONFIG_CLIENT_AUTH_SSL_KEYSTORE_TYPE =
- "client.auth.ssl.keystore.type";
+ "client.http.auth.ssl.keystore.type";
public static final String PROP_CONFIG_CLIENT_AUTH_SSL_KEY_ALIAS =
- "client.auth.ssl.key.alias";
+ "client.http.auth.ssl.key.alias";
public static final String PROP_CONFIG_CLIENT_AUTH_SSL_KEY_PASSWORD =
- "client.auth.ssl.key.password";
+ "client.http.auth.ssl.key.password";
+
+ public static final String PROP_CONFIG_CLIENT_AUTH_SSL_TRUSTSTORE_PATH =
+ "client.http.ssl.truststore.path";
+ public static final String PROP_CONFIG_CLIENT_AUTH_SSL_TRUSTSTORE_PASSORD =
+ "client.http.ssl.truststore.password";
+ public static final String PROP_CONFIG_CLIENT_AUTH_SSL_TRUSTSTORE_NAME =
+ "client.http.ssl.truststore.name";
+ public static final String PROP_CONFIG_CLIENT_AUTH_SSL_TRUSTSTORE_TYPE =
+ "client.http.ssl.truststore.type";
+ public static final String PROP_CONFIG_CLIENT_HTTP_SSL_HOSTNAMEVERIFIER_TRUSTALL =
+ "client.http.ssl.hostnameverifier.trustall";
// default configuration values
public static final String DEFAULT_CONFIG_CLIENT_HTTP_CONNECTION_TIMEOUT_SOCKET = "15";
@@ -306,6 +316,17 @@ public class HttpClientFactory implements IHttpClientFactory {
config.setSslKeyPassword(
basicConfig.getBasicConfiguration(PROP_CONFIG_CLIENT_AUTH_SSL_KEY_PASSWORD));
+ // set SSL TrustStore if available
+ config.buildTrustStoreConfig(
+ basicConfig.getBasicConfiguration(
+ PROP_CONFIG_CLIENT_AUTH_SSL_TRUSTSTORE_TYPE),
+ basicConfig.getBasicConfiguration(
+ PROP_CONFIG_CLIENT_AUTH_SSL_TRUSTSTORE_PATH, StringUtils.EMPTY),
+ basicConfig.getBasicConfiguration(
+ PROP_CONFIG_CLIENT_AUTH_SSL_TRUSTSTORE_PASSORD, StringUtils.EMPTY),
+ basicConfig.getBasicConfiguration(
+ PROP_CONFIG_CLIENT_AUTH_SSL_TRUSTSTORE_NAME, StringUtils.EMPTY));
+
config.setDisableHostnameValidation(basicConfig.getBasicConfigurationBoolean(
PROP_CONFIG_CLIENT_HTTP_SSL_HOSTNAMEVERIFIER_TRUSTALL, false));
@@ -369,11 +390,14 @@ public class HttpClientFactory implements IHttpClientFactory {
log.trace("Injecting SSL client-authentication into http client ... ");
sslContext = HttpUtils.buildSslContextWithSslClientAuthentication(keyStore,
httpClientConfig.getSslKeyAlias(), httpClientConfig.getSslKeyPassword(),
+ buildCustomTrustStore(httpClientConfig.getTrustStoreConfig()),
httpClientConfig.isDisableTlsHostCertificateValidation(), httpClientConfig.getFriendlyName());
} else {
log.trace("Initializing default SSL Context ... ");
- sslContext = HttpUtils.buildSslContext(httpClientConfig.isDisableTlsHostCertificateValidation(),
+ sslContext = HttpUtils.buildSslContext(
+ buildCustomTrustStore(httpClientConfig.getTrustStoreConfig()),
+ httpClientConfig.isDisableTlsHostCertificateValidation(),
httpClientConfig.getFriendlyName());
}
@@ -393,6 +417,18 @@ public class HttpClientFactory implements IHttpClientFactory {
}
+ private KeyStore buildCustomTrustStore(KeyStoreConfiguration trustStoreConfig) throws EaafException {
+ if (trustStoreConfig != null) {
+ log.debug("Open trustStore with type: {}", trustStoreConfig.getKeyStoreType());
+ final Pair<KeyStore, Provider> trustStore = keyStoreFactory.buildNewKeyStore(trustStoreConfig);
+ return trustStore.getFirst();
+
+ } else {
+ return null;
+
+ }
+ }
+
@Nonnull
private HttpClientConnectionManager injectConnectionManager(
HttpClientBuilder builder, final LayeredConnectionSocketFactory sslConnectionFactory)
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
index 0faa94f4..61076dfa 100644
--- 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
@@ -30,8 +30,6 @@ import java.security.NoSuchAlgorithmException;
import java.security.Provider;
import java.security.UnrecoverableKeyException;
-import javax.annotation.Nonnull;
-import javax.annotation.Nullable;
import javax.net.ssl.SSLContext;
import org.apache.commons.lang3.StringUtils;
@@ -53,6 +51,8 @@ import at.gv.egiz.eaaf.core.exceptions.EaafConfigurationException;
import at.gv.egiz.eaaf.core.exceptions.EaafFactoryException;
import at.gv.egiz.eaaf.core.impl.data.Pair;
import at.gv.egiz.eaaf.core.impl.data.Triple;
+import jakarta.annotation.Nonnull;
+import jakarta.annotation.Nullable;
import jakarta.servlet.http.HttpServletRequest;
import lombok.NonNull;
import lombok.extern.slf4j.Slf4j;
@@ -272,10 +272,31 @@ public class HttpUtils {
public static SSLContext buildSslContext(
boolean trustAllServerCertificates, @Nonnull String friendlyName)
throws EaafConfigurationException, EaafFactoryException {
+ return buildSslContext(null, trustAllServerCertificates, friendlyName);
+
+ }
+
+ /**
+ * Initialize a {@link SSLContext}.
+ *
+ * @param trustStore Custom SSL TrustStore, or <code>null</code>
+ * if default truststore should be used
+ * @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 buildSslContext(@Nullable final KeyStore trustStore,
+ boolean trustAllServerCertificates, @Nonnull String friendlyName)
+ throws EaafConfigurationException, EaafFactoryException {
try {
EaafSslContextBuilder sslContextBuilder = EaafSslContextBuilder.create();
- injectTrustStore(sslContextBuilder, null, trustAllServerCertificates, friendlyName);
+ injectTrustStore(sslContextBuilder, trustStore, trustAllServerCertificates, friendlyName);
return sslContextBuilder.build();
@@ -308,20 +329,8 @@ public class HttpUtils {
@Nullable String keyAlias, @Nullable String keyPasswordString,
boolean trustAllServerCertificates, @Nonnull String friendlyName)
throws EaafConfigurationException, EaafFactoryException {
- try {
- EaafSslContextBuilder sslContextBuilder = EaafSslContextBuilder.create();
-
- injectKeyStore(sslContextBuilder, keyStore, keyAlias, keyPasswordString, friendlyName);
-
- injectTrustStore(sslContextBuilder, null, trustAllServerCertificates, friendlyName);
-
- return sslContextBuilder.build();
-
- } catch (NoSuchAlgorithmException | KeyManagementException | UnrecoverableKeyException
- | KeyStoreException e) {
- throw new EaafFactoryException(ERROR_03, new Object[] { friendlyName, e.getMessage() }, e);
-
- }
+ return buildSslContextWithSslClientAuthentication(keyStore, keyAlias, keyPasswordString,
+ null, trustAllServerCertificates, friendlyName);
}
/**
@@ -346,7 +355,7 @@ public class HttpUtils {
*/
public static SSLContext buildSslContextWithSslClientAuthentication(@Nonnull final Pair<KeyStore, Provider> keyStore,
@Nullable String keyAlias, @Nullable String keyPasswordString,
- @Nullable final Pair<KeyStore, Provider> trustStore, boolean trustAllServerCertificates,
+ @Nullable final KeyStore trustStore, boolean trustAllServerCertificates,
@Nonnull String friendlyName)
throws EaafConfigurationException, EaafFactoryException {
try {
@@ -366,7 +375,7 @@ public class HttpUtils {
}
private static void injectTrustStore(EaafSslContextBuilder sslContextBuilder,
- Pair<KeyStore, Provider> trustStore, boolean trustAllServerCertificates, String friendlyName)
+ KeyStore trustStore, boolean trustAllServerCertificates, String friendlyName)
throws NoSuchAlgorithmException, KeyStoreException {
TrustStrategy trustStrategy = null;
@@ -376,14 +385,12 @@ public class HttpUtils {
}
- KeyStore trustStoreImpl = null;
if (trustStore != null) {
log.info("Http-client: {} uses custom TrustStore.", friendlyName);
- trustStoreImpl = trustStore.getFirst();
}
- sslContextBuilder.loadTrustMaterial(trustStoreImpl, trustStrategy);
+ sslContextBuilder.loadTrustMaterial(trustStore, trustStrategy);
}
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
index c566380e..33ba96e2 100644
--- 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
@@ -327,6 +327,13 @@ public class HttpClientFactoryTest {
config.setUsername("jUnit");
config.setPassword("password");
+ final String current = new java.io.File(".").getCanonicalPath();
+ System.setProperty("javax.net.ssl.trustStoreType", "jks");
+ System.setProperty("javax.net.ssl.trustStore",
+ current + "/src/test/resources/data/ssL_truststore.jks");
+ System.setProperty("javax.net.ssl.trustStorePassword",
+ "password");
+
final CloseableHttpClient client = httpClientFactory.getHttpClient(config);
Assert.assertNotNull("httpClient", client);
@@ -394,6 +401,96 @@ public class HttpClientFactoryTest {
}
@Test
+ public void withCustomTrustStore() throws EaafException, ClientProtocolException,
+ IOException, KeyStoreException {
+ final HttpClientConfiguration config = new HttpClientConfiguration("jUnit");
+ config.setEnablePreEmptiveHttpBasicAuth(false);
+ config.setAuthMode("password");
+ config.setUsername("jUnit");
+ config.setPassword("password");
+
+ final String current = new java.io.File(".").getCanonicalPath();
+ config.buildTrustStoreConfig("jks", "file:" + current + "/src/test/resources/data/ssL_truststore.jks",
+ "password", null);
+
+ final CloseableHttpClient client = httpClientFactory.getHttpClient(config);
+ Assert.assertNotNull("httpClient", client);
+
+ // set-up mock-up web-server with SSL client authentication
+ final String localhost = InetAddress.getByName("localhost").getCanonicalHostName();
+ final HeldCertificate localhostCertificate = new HeldCertificate.Builder()
+ .addSubjectAlternativeName(localhost)
+ .build();
+ final HandshakeCertificates serverCertificates = new HandshakeCertificates.Builder()
+ .heldCertificate(localhostCertificate)
+ .build();
+ mockWebServer = new MockWebServer();
+ mockWebServer.useHttps(serverCertificates.sslSocketFactory(), false);
+ 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());
+ assertThrows(Exception.class, () -> client.execute(httpGet2));
+
+ }
+
+ @Test
+ public void withWrongCustomTrustStore() throws EaafException, ClientProtocolException,
+ IOException, KeyStoreException {
+ final HttpClientConfiguration config = new HttpClientConfiguration("jUnit");
+ config.setEnablePreEmptiveHttpBasicAuth(false);
+ config.setAuthMode("password");
+ config.setUsername("jUnit");
+ config.setPassword("password");
+
+ final String current = new java.io.File(".").getCanonicalPath();
+ config.buildTrustStoreConfig("jks", "file:" + current + "/src/test/resources/data/ssL_truststore.jks",
+ "password", null);
+
+ final CloseableHttpClient client = httpClientFactory.getHttpClient(config);
+ Assert.assertNotNull("httpClient", client);
+
+ // set-up mock-up web-server with SSL client authentication
+ final String localhost = InetAddress.getByName("localhost").getCanonicalHostName();
+ final HeldCertificate localhostCertificate = new HeldCertificate.Builder()
+ .addSubjectAlternativeName(localhost)
+ .build();
+ final HandshakeCertificates serverCertificates = new HandshakeCertificates.Builder()
+ .heldCertificate(localhostCertificate)
+ .build();
+ mockWebServer = new MockWebServer();
+ mockWebServer.useHttps(serverCertificates.sslSocketFactory(), false);
+ 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());
+ assertThrows(Exception.class, () -> client.execute(httpGet2));
+
+ }
+
+ @Test
+ public void withWrongConfigCustomTrustStore() throws EaafException, ClientProtocolException,
+ IOException, KeyStoreException {
+ final HttpClientConfiguration config = new HttpClientConfiguration("jUnit");
+ config.setEnablePreEmptiveHttpBasicAuth(false);
+ config.setAuthMode("password");
+ config.setUsername("jUnit");
+ config.setPassword("password");
+
+ final String current = new java.io.File(".").getCanonicalPath();
+ config.buildTrustStoreConfig("jks", "file:" + current + "/src/test/resources/data/ssL_truststore.jks",
+ "wrongPassword", null);
+
+ EaafException error = assertThrows(EaafException.class, () -> httpClientFactory.getHttpClient(config));
+ Assert.assertEquals("wrong errorCode", "internal.keystore.06", error.getErrorId());
+
+ }
+
+ @Test
public void testHttpClientRetryOneTime() throws EaafException, InterruptedException,
ClientProtocolException, IOException {
final HttpClientConfiguration config =