From a849dd49daf60128db79311293d7f5c466bd0642 Mon Sep 17 00:00:00 2001 From: Thomas <> Date: Fri, 16 Apr 2021 22:08:42 +0200 Subject: Use custom SSLContext builder to generate BouncyCastle specific TrustManager in case of keys base on HSM-Facade, because SSLContext based on BCJSSE needs BCJSSE TrustManager BCJSSE is not compatible to SunJSSE TrustManager in Java >= 9 --- eaaf_core_utils/pom.xml | 17 +- .../eaaf/core/impl/http/EaafSslContextBuilder.java | 433 +++++++++++++++++++++ .../at/gv/egiz/eaaf/core/impl/http/HttpUtils.java | 22 +- .../test/http/HttpClientFactoryProdHostTest.java | 98 +++++ .../eaaf/core/test/http/HttpClientFactoryTest.java | 96 +++++ .../test/resources/data/hsm_ee-RSA_rootcert.crt | 3 + .../src/test/resources/data/hsm_ee_eecert.crt | 3 + .../src/test/resources/data/hsm_ee_rootcert.crt | 3 + .../src/test/resources/data/server_host.crt | 18 + .../src/test/resources/data/ssL_truststore.jks | Bin 0 -> 799 bytes .../src/test/resources/data/ssl_host.jks | Bin 0 -> 2081 bytes pom.xml | 4 +- 12 files changed, 682 insertions(+), 15 deletions(-) create mode 100644 eaaf_core_utils/src/main/java/at/gv/egiz/eaaf/core/impl/http/EaafSslContextBuilder.java create mode 100644 eaaf_core_utils/src/test/java/at/gv/egiz/eaaf/core/test/http/HttpClientFactoryProdHostTest.java create mode 100644 eaaf_core_utils/src/test/resources/data/hsm_ee-RSA_rootcert.crt create mode 100644 eaaf_core_utils/src/test/resources/data/hsm_ee_eecert.crt create mode 100644 eaaf_core_utils/src/test/resources/data/hsm_ee_rootcert.crt create mode 100644 eaaf_core_utils/src/test/resources/data/server_host.crt create mode 100644 eaaf_core_utils/src/test/resources/data/ssL_truststore.jks create mode 100644 eaaf_core_utils/src/test/resources/data/ssl_host.jks diff --git a/eaaf_core_utils/pom.xml b/eaaf_core_utils/pom.xml index 4e3bbeee..e0cb88e3 100644 --- a/eaaf_core_utils/pom.xml +++ b/eaaf_core_utils/pom.xml @@ -121,8 +121,23 @@ com.squareup.okhttp3 okhttp-tls test + + + org.bouncycastle + bctls-jdk15on + + + org.bouncycastle + bcpkix-jdk15on + + + + + ch.qos.logback + logback-classic + 1.2.3 + test - diff --git a/eaaf_core_utils/src/main/java/at/gv/egiz/eaaf/core/impl/http/EaafSslContextBuilder.java b/eaaf_core_utils/src/main/java/at/gv/egiz/eaaf/core/impl/http/EaafSslContextBuilder.java new file mode 100644 index 00000000..1cd739de --- /dev/null +++ b/eaaf_core_utils/src/main/java/at/gv/egiz/eaaf/core/impl/http/EaafSslContextBuilder.java @@ -0,0 +1,433 @@ +package at.gv.egiz.eaaf.core.impl.http; + +import java.net.Socket; +import java.security.KeyManagementException; +import java.security.KeyStore; +import java.security.KeyStoreException; +import java.security.NoSuchAlgorithmException; +import java.security.Principal; +import java.security.PrivateKey; +import java.security.Provider; +import java.security.SecureRandom; +import java.security.Security; +import java.security.UnrecoverableKeyException; +import java.security.cert.CertificateException; +import java.security.cert.X509Certificate; +import java.util.Collection; +import java.util.Collections; +import java.util.HashMap; +import java.util.LinkedHashSet; +import java.util.Map; +import java.util.Set; + +import javax.net.ssl.KeyManager; +import javax.net.ssl.KeyManagerFactory; +import javax.net.ssl.SSLContext; +import javax.net.ssl.SSLEngine; +import javax.net.ssl.TrustManager; +import javax.net.ssl.TrustManagerFactory; +import javax.net.ssl.X509ExtendedKeyManager; +import javax.net.ssl.X509TrustManager; + +import org.apache.http.ssl.PrivateKeyDetails; +import org.apache.http.ssl.PrivateKeyStrategy; +import org.apache.http.ssl.SSLContextBuilder; +import org.apache.http.ssl.TrustStrategy; +import org.bouncycastle.jsse.provider.BouncyCastleJsseProvider; + +/** + * Fork of {@link SSLContextBuilder} that uses JSSE provider to get TrustManager. + * + *

This implementation fix an incompatibility between {@link BouncyCastleJsseProvider} and JAVA JDK >= v9

+ * + * @author tlenz + * + */ +public class EaafSslContextBuilder { + + static final String TLS = "TLS"; + + private String protocol; + private final Set keyManagers; + private String keyManagerFactoryAlgorithm = KeyManagerFactory.getDefaultAlgorithm(); + private String keyStoreType = KeyStore.getDefaultType(); + private final Set trustManagers; + private String trustManagerFactoryAlgorithm = TrustManagerFactory.getDefaultAlgorithm(); + private SecureRandom secureRandom; + private Provider provider; + + public static EaafSslContextBuilder create() { + return new EaafSslContextBuilder(); + } + + /** + * Get a new SSLContext builder object. + */ + public EaafSslContextBuilder() { + super(); + this.keyManagers = new LinkedHashSet<>(); + this.trustManagers = new LinkedHashSet<>(); + } + + /** + * Sets the SSLContext protocol algorithm name. + * + * @param protocol the SSLContext protocol algorithm name of the requested + * protocol. See the SSLContext section in the Java + * Cryptography Architecture Standard Algorithm Name + * Documentation for more information. + * @return this builder + * @see Java + * Cryptography Architecture Standard Algorithm Name Documentation + * @deprecated Use {@link #setProtocol(String)}. + */ + @Deprecated + public EaafSslContextBuilder useProtocol(final String protocol) { + this.protocol = protocol; + return this; + } + + /** + * Sets the SSLContext protocol algorithm name. + * + * @param protocol the SSLContext protocol algorithm name of the requested + * protocol. See the SSLContext section in the Java + * Cryptography Architecture Standard Algorithm Name + * Documentation for more information. + * @return this builder + * @see Java + * Cryptography Architecture Standard Algorithm Name Documentation + * @since 4.4.7 + */ + public EaafSslContextBuilder setProtocol(final String protocol) { + this.protocol = protocol; + return this; + } + + public EaafSslContextBuilder setSecureRandom(final SecureRandom secureRandom) { + this.secureRandom = secureRandom; + return this; + } + + public EaafSslContextBuilder setProvider(final Provider provider) { + this.provider = provider; + return this; + } + + public EaafSslContextBuilder setProvider(final String name) { + this.provider = Security.getProvider(name); + return this; + } + + /** + * Sets the key store type. + * + * @param keyStoreType the SSLkey store type. See the KeyStore section in the + * Java + * Cryptography Architecture Standard Algorithm Name + * Documentation for more information. + * @return this builder + * @see Java + * Cryptography Architecture Standard Algorithm Name Documentation + * @since 4.4.7 + */ + public EaafSslContextBuilder setKeyStoreType(final String keyStoreType) { + this.keyStoreType = keyStoreType; + return this; + } + + /** + * Sets the key manager factory algorithm name. + * + * @param keyManagerFactoryAlgorithm the key manager factory algorithm name of + * the requested protocol. See the + * KeyManagerFactory section in the Java + * Cryptography Architecture Standard + * Algorithm Name Documentation for more + * information. + * @return this builder + * @see Java + * Cryptography Architecture Standard Algorithm Name Documentation + * @since 4.4.7 + */ + public EaafSslContextBuilder setKeyManagerFactoryAlgorithm(final String keyManagerFactoryAlgorithm) { + this.keyManagerFactoryAlgorithm = keyManagerFactoryAlgorithm; + return this; + } + + /** + * Sets the trust manager factory algorithm name. + * + * @param trustManagerFactoryAlgorithm the trust manager algorithm name of the + * requested protocol. See the + * TrustManagerFactory section in the + * Java + * Cryptography Architecture Standard + * Algorithm Name Documentation for more + * information. + * @return this builder + * @see Java + * Cryptography Architecture Standard Algorithm Name Documentation + * @since 4.4.7 + */ + public EaafSslContextBuilder setTrustManagerFactoryAlgorithm(final String trustManagerFactoryAlgorithm) { + this.trustManagerFactoryAlgorithm = trustManagerFactoryAlgorithm; + return this; + } + + /** + * Load custom truststore. + * + * @param truststore {@link KeyStore} if trusted certificates + * @param trustStrategy Trust validation strategy + * @return {@link EaafSslContextBuilder} + * @throws NoSuchAlgorithmException In case of an invalid TrustManager algorithm + * @throws KeyStoreException In case of an invalid KeyStore + */ + public EaafSslContextBuilder loadTrustMaterial( + final KeyStore truststore, + final TrustStrategy trustStrategy) throws NoSuchAlgorithmException, KeyStoreException { + + final String alg = trustManagerFactoryAlgorithm == null + ? TrustManagerFactory.getDefaultAlgorithm() + : trustManagerFactoryAlgorithm; + + final TrustManagerFactory tmfactory = provider != null + ? TrustManagerFactory.getInstance(alg, provider) + : TrustManagerFactory.getInstance(alg); + tmfactory.init(truststore); + final TrustManager[] tms = tmfactory.getTrustManagers(); + if (tms != null) { + if (trustStrategy != null) { + for (int i = 0; i < tms.length; i++) { + final TrustManager tm = tms[i]; + if (tm instanceof X509TrustManager) { + tms[i] = new TrustManagerDelegate((X509TrustManager) tm, trustStrategy); + } + } + } + Collections.addAll(this.trustManagers, tms); + } + return this; + } + + public EaafSslContextBuilder loadTrustMaterial( + final TrustStrategy trustStrategy) throws NoSuchAlgorithmException, KeyStoreException { + return loadTrustMaterial(null, trustStrategy); + } + + + /** + * Load SSL client-authentication key-material into SSL context. + * + * @param keystore {@link KeyStore} for SSL client-authentication + * @param keyPassword Password for this keystore + * @param aliasStrategy Stategy to select keys by alias + * @return {@link EaafSslContextBuilder} + * @throws NoSuchAlgorithmException In case of an invalid KeyManagerFactory algorithm + * @throws KeyStoreException In case of an invalid KeyStore + * @throws UnrecoverableKeyException In case of a invalid Key in this KeyStore + */ + public EaafSslContextBuilder loadKeyMaterial( + final KeyStore keystore, + final char[] keyPassword, + final PrivateKeyStrategy aliasStrategy) + throws NoSuchAlgorithmException, KeyStoreException, UnrecoverableKeyException { + final KeyManagerFactory kmfactory = KeyManagerFactory + .getInstance(keyManagerFactoryAlgorithm == null ? KeyManagerFactory.getDefaultAlgorithm() + : keyManagerFactoryAlgorithm); + kmfactory.init(keystore, keyPassword); + final KeyManager[] kms = kmfactory.getKeyManagers(); + if (kms != null) { + if (aliasStrategy != null) { + for (int i = 0; i < kms.length; i++) { + final KeyManager km = kms[i]; + if (km instanceof X509ExtendedKeyManager) { + kms[i] = new KeyManagerDelegate((X509ExtendedKeyManager) km, aliasStrategy); + } + } + } + Collections.addAll(keyManagers, kms); + } + return this; + } + + public EaafSslContextBuilder loadKeyMaterial( + final KeyStore keystore, + final char[] keyPassword) throws NoSuchAlgorithmException, KeyStoreException, + UnrecoverableKeyException { + return loadKeyMaterial(keystore, keyPassword, null); + } + + protected void initSslContext( + final SSLContext sslContext, + final Collection keyManagers, + final Collection trustManagers, + final SecureRandom secureRandom) throws KeyManagementException { + sslContext.init( + !keyManagers.isEmpty() ? keyManagers.toArray(new KeyManager[keyManagers.size()]) : null, + !trustManagers.isEmpty() ? trustManagers.toArray(new TrustManager[trustManagers.size()]) : null, + secureRandom); + } + + /** + * Build a {@link SSLContext} from this builder. + * + * @return new {@link SSLContext} + * @throws NoSuchAlgorithmException In case of an unknown SSL protocol + * @throws KeyManagementException In case of a key-access error + */ + public SSLContext build() throws NoSuchAlgorithmException, KeyManagementException { + final SSLContext sslContext; + final String protocolStr = this.protocol != null ? this.protocol : TLS; + if (this.provider != null) { + sslContext = SSLContext.getInstance(protocolStr, this.provider); + } else { + sslContext = SSLContext.getInstance(protocolStr); + } + initSslContext(sslContext, keyManagers, trustManagers, secureRandom); + return sslContext; + } + + static class TrustManagerDelegate implements X509TrustManager { + + private final X509TrustManager trustManager; + private final TrustStrategy trustStrategy; + + TrustManagerDelegate(final X509TrustManager trustManager, final TrustStrategy trustStrategy) { + super(); + this.trustManager = trustManager; + this.trustStrategy = trustStrategy; + } + + @Override + public void checkClientTrusted( + final X509Certificate[] chain, final String authType) throws CertificateException { + this.trustManager.checkClientTrusted(chain, authType); + } + + @Override + public void checkServerTrusted( + final X509Certificate[] chain, final String authType) throws CertificateException { + if (!this.trustStrategy.isTrusted(chain, authType)) { + this.trustManager.checkServerTrusted(chain, authType); + } + } + + @Override + public X509Certificate[] getAcceptedIssuers() { + return this.trustManager.getAcceptedIssuers(); + } + + } + + static class KeyManagerDelegate extends X509ExtendedKeyManager { + + private final X509ExtendedKeyManager keyManager; + private final PrivateKeyStrategy aliasStrategy; + + KeyManagerDelegate(final X509ExtendedKeyManager keyManager, final PrivateKeyStrategy aliasStrategy) { + super(); + this.keyManager = keyManager; + this.aliasStrategy = aliasStrategy; + } + + @Override + public String[] getClientAliases( + final String keyType, final Principal[] issuers) { + return this.keyManager.getClientAliases(keyType, issuers); + } + + public Map getClientAliasMap( + final String[] keyTypes, final Principal[] issuers) { + final Map validAliases = new HashMap<>(); + for (final String keyType : keyTypes) { + final String[] aliases = this.keyManager.getClientAliases(keyType, issuers); + if (aliases != null) { + for (final String alias : aliases) { + validAliases.put(alias, + new PrivateKeyDetails(keyType, this.keyManager.getCertificateChain(alias))); + } + } + } + return validAliases; + } + + public Map getServerAliasMap( + final String keyType, final Principal[] issuers) { + final Map validAliases = new HashMap<>(); + final String[] aliases = this.keyManager.getServerAliases(keyType, issuers); + if (aliases != null) { + for (final String alias : aliases) { + validAliases.put(alias, + new PrivateKeyDetails(keyType, this.keyManager.getCertificateChain(alias))); + } + } + return validAliases; + } + + @Override + public String chooseClientAlias( + final String[] keyTypes, final Principal[] issuers, final Socket socket) { + final Map validAliases = getClientAliasMap(keyTypes, issuers); + return this.aliasStrategy.chooseAlias(validAliases, socket); + } + + @Override + public String[] getServerAliases( + final String keyType, final Principal[] issuers) { + return this.keyManager.getServerAliases(keyType, issuers); + } + + @Override + public String chooseServerAlias( + final String keyType, final Principal[] issuers, final Socket socket) { + final Map validAliases = getServerAliasMap(keyType, issuers); + return this.aliasStrategy.chooseAlias(validAliases, socket); + } + + @Override + public X509Certificate[] getCertificateChain(final String alias) { + return this.keyManager.getCertificateChain(alias); + } + + @Override + public PrivateKey getPrivateKey(final String alias) { + return this.keyManager.getPrivateKey(alias); + } + + @Override + public String chooseEngineClientAlias( + final String[] keyTypes, final Principal[] issuers, final SSLEngine sslEngine) { + final Map validAliases = getClientAliasMap(keyTypes, issuers); + return this.aliasStrategy.chooseAlias(validAliases, null); + } + + @Override + public String chooseEngineServerAlias( + final String keyType, final Principal[] issuers, final SSLEngine sslEngine) { + final Map validAliases = getServerAliasMap(keyType, issuers); + return this.aliasStrategy.chooseAlias(validAliases, null); + } + + } + + @Override + public String toString() { + return "[provider=" + provider + ", protocol=" + protocol + ", keyStoreType=" + keyStoreType + + ", keyManagerFactoryAlgorithm=" + keyManagerFactoryAlgorithm + ", keyManagers=" + keyManagers + + ", trustManagerFactoryAlgorithm=" + trustManagerFactoryAlgorithm + ", trustManagers=" + + trustManagers + + ", secureRandom=" + secureRandom + "]"; + } +} 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 365e969d..3058c9b5 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 @@ -40,8 +40,6 @@ import org.apache.http.client.ClientProtocolException; import org.apache.http.client.ResponseHandler; import org.apache.http.conn.ssl.TrustAllStrategy; import org.apache.http.entity.ContentType; -import org.apache.http.ssl.SSLContextBuilder; -import org.apache.http.ssl.SSLContexts; import org.apache.http.ssl.TrustStrategy; import org.apache.http.util.EntityUtils; import org.bouncycastle.jsse.provider.BouncyCastleJsseProvider; @@ -56,7 +54,6 @@ import lombok.extern.slf4j.Slf4j; public class HttpUtils { private static final String ERROR_03 = "internal.httpclient.03"; - /** * Simple Http response-handler that only give http status-code as result. @@ -174,7 +171,7 @@ public class HttpUtils { * @param url URL * @param paramname Name of the parameter. * @param paramvalue Value of the parameter. - * @return + * @return Url with parameter */ public static String addUrlParameter(final String url, final String paramname, final String paramvalue) { @@ -210,7 +207,7 @@ public class HttpUtils { boolean trustAllServerCertificates, @Nonnull String friendlyName) throws EaafConfigurationException, EaafFactoryException { try { - SSLContextBuilder sslContextBuilder = SSLContexts.custom(); + EaafSslContextBuilder sslContextBuilder = EaafSslContextBuilder.create(); injectKeyStore(sslContextBuilder, keyStore, keyAlias, keyPasswordString, friendlyName); @@ -251,7 +248,7 @@ public class HttpUtils { @Nonnull String friendlyName) throws EaafConfigurationException, EaafFactoryException { try { - SSLContextBuilder sslContextBuilder = SSLContexts.custom(); + EaafSslContextBuilder sslContextBuilder = EaafSslContextBuilder.create(); injectKeyStore(sslContextBuilder, keyStore, keyAlias, keyPasswordString, friendlyName); @@ -266,7 +263,7 @@ public class HttpUtils { } } - private static void injectTrustStore(SSLContextBuilder sslContextBuilder, + private static void injectTrustStore(EaafSslContextBuilder sslContextBuilder, Pair trustStore, boolean trustAllServerCertificates, String friendlyName) throws NoSuchAlgorithmException, KeyStoreException { @@ -276,7 +273,7 @@ public class HttpUtils { trustStrategy = new TrustAllStrategy(); } - + KeyStore trustStoreImpl = null; if (trustStore != null) { log.info("Http-client: {} uses custom TrustStore.", friendlyName); @@ -288,16 +285,18 @@ public class HttpUtils { } - private static void injectKeyStore(SSLContextBuilder sslContextBuilder, Pair keyStore, + private static void injectKeyStore(EaafSslContextBuilder sslContextBuilder, Pair keyStore, String keyAlias, String keyPasswordString, String friendlyName) throws UnrecoverableKeyException, NoSuchAlgorithmException, KeyStoreException { + + Provider provider; if (keyStore.getSecond() != null) { - Provider provider = new BouncyCastleJsseProvider(keyStore.getSecond()); + provider = new BouncyCastleJsseProvider(keyStore.getSecond()); log.debug("KeyStore: {} provide special security-provider. Inject: {} into SSLContext", friendlyName, provider.getName()); sslContextBuilder.setProvider(provider); - } + } log.trace("Open SSL Client-Auth keystore with password: {}", keyPasswordString); final char[] keyPassword = keyPasswordString == null ? StringUtils.EMPTY.toCharArray() @@ -313,5 +312,4 @@ public class HttpUtils { } } - } diff --git a/eaaf_core_utils/src/test/java/at/gv/egiz/eaaf/core/test/http/HttpClientFactoryProdHostTest.java b/eaaf_core_utils/src/test/java/at/gv/egiz/eaaf/core/test/http/HttpClientFactoryProdHostTest.java new file mode 100644 index 00000000..55c17ee8 --- /dev/null +++ b/eaaf_core_utils/src/test/java/at/gv/egiz/eaaf/core/test/http/HttpClientFactoryProdHostTest.java @@ -0,0 +1,98 @@ +package at.gv.egiz.eaaf.core.test.http; + +import java.io.IOException; +import java.security.KeyStore; +import java.security.KeyStoreException; +import java.security.NoSuchAlgorithmException; +import java.security.Provider; +import java.security.UnrecoverableKeyException; +import java.security.cert.CertificateEncodingException; +import java.security.cert.X509Certificate; +import java.util.Base64; + +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.Assert; +import org.junit.Before; +import org.junit.BeforeClass; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.test.annotation.DirtiesContext; +import org.springframework.test.annotation.DirtiesContext.MethodMode; +import org.springframework.test.context.ContextConfiguration; +import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; + +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 ch.qos.logback.classic.Level; +import ch.qos.logback.classic.Logger; + +@RunWith(SpringJUnit4ClassRunner.class) +@ContextConfiguration("/spring/test_eaaf_pvp_not_lazy.beans.xml") +@DirtiesContext +public class HttpClientFactoryProdHostTest { + + @Autowired private IHttpClientFactory httpClientFactory; + @Autowired private EaafKeyStoreFactory keyStoreFactory; + + /** + * Initialize full class. + */ + @BeforeClass + public static void classInitializer() { + final Logger logger = (Logger) LoggerFactory.getLogger("org.bouncycastle.jsse"); + logger.setLevel(Level.TRACE); + + } + + /** + * JUnit test set-up. + * + */ + @Before + public void setup() { + + } + + @Test + @DirtiesContext(methodMode = MethodMode.BEFORE_METHOD) + public void getCustomClientX509AuthWithHsmFacadeTrustStore() throws EaafException, ClientProtocolException, + IOException, KeyStoreException, UnrecoverableKeyException, NoSuchAlgorithmException, + CertificateEncodingException { + System.setProperty("javax.net.debug", "ssl:handshake"); + + final HttpClientConfiguration clientConfig = new HttpClientConfiguration("jUnit-client"); + clientConfig.setAuthMode("ssl"); + //clientConfig.buildKeyStoreConfig("hsmfacade", null, null, "eid-junit"); + //clientConfig.setSslKeyAlias("rsa-key-1"); + clientConfig.buildKeyStoreConfig("hsmfacade", null, null, "authhandler"); + clientConfig.setSslKeyAlias("authhandler-sign"); + clientConfig.setDisableTlsHostCertificateValidation(false); + + final CloseableHttpClient client = httpClientFactory.getHttpClient(clientConfig); + Assert.assertNotNull("httpClient", client); + + final Pair sslClientKeyStore = + keyStoreFactory.buildNewKeyStore(clientConfig.getKeyStoreConfig()); + final X509Certificate clientRootCert = (X509Certificate) sslClientKeyStore.getFirst() + .getCertificateChain(clientConfig.getSslKeyAlias())[1]; + final X509Certificate clientEeCert = (X509Certificate) sslClientKeyStore.getFirst() + .getCertificateChain(clientConfig.getSslKeyAlias())[0]; + Base64.getEncoder().encodeToString(clientEeCert.getEncoded()); + + //perform test request + final HttpUriRequest httpGet2 = new HttpGet("https://apps.egiz.gv.at//sslclientcertdemo/"); + final CloseableHttpResponse httpResp2 = client.execute(httpGet2); + Assert.assertEquals("http statusCode", 200, httpResp2.getStatusLine().getStatusCode()); + + } + +} 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 baedadc8..c71d8352 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 @@ -5,9 +5,14 @@ import java.io.IOException; import java.net.HttpURLConnection; import java.net.InetAddress; import java.net.SocketTimeoutException; +import java.security.Key; +import java.security.KeyPair; import java.security.KeyStore; import java.security.KeyStoreException; +import java.security.NoSuchAlgorithmException; +import java.security.PrivateKey; import java.security.Provider; +import java.security.UnrecoverableKeyException; import java.security.cert.X509Certificate; import org.apache.commons.lang3.RandomStringUtils; @@ -20,10 +25,13 @@ import org.apache.http.client.methods.HttpUriRequest; import org.apache.http.entity.ContentType; import org.apache.http.impl.client.CloseableHttpClient; import org.junit.After; +import org.junit.AfterClass; import org.junit.Assert; import org.junit.Before; +import org.junit.BeforeClass; import org.junit.Test; import org.junit.runner.RunWith; +import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.test.annotation.DirtiesContext; import org.springframework.test.annotation.DirtiesContext.MethodMode; @@ -32,12 +40,16 @@ import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; 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.data.Triple; import at.gv.egiz.eaaf.core.impl.http.HttpClientConfiguration; import at.gv.egiz.eaaf.core.impl.http.HttpUtils; import at.gv.egiz.eaaf.core.impl.http.IHttpClientFactory; import at.gv.egiz.eaaf.core.impl.utils.StreamUtils; +import ch.qos.logback.classic.Level; +import ch.qos.logback.classic.Logger; import okhttp3.HttpUrl; import okhttp3.mockwebserver.MockResponse; import okhttp3.mockwebserver.MockWebServer; @@ -57,6 +69,27 @@ public class HttpClientFactoryTest { private MockWebServer mockWebServer = null; private HttpUrl mockServerUrl; + /** + * Initialize full class. + */ + @BeforeClass + public static void classInitializer() { + final Logger logger = (Logger) LoggerFactory.getLogger("org.bouncycastle.jsse"); + logger.setLevel(Level.TRACE); + + } + + /** + * Reset test environment. + */ + @AfterClass + public static void classReset() { + System.clearProperty("javax.net.ssl.trustStoreType"); + System.clearProperty("javax.net.ssl.trustStore"); + System.clearProperty("javax.net.ssl.trustStorePassword"); + + } + /** * JUnit test set-up. * @@ -595,4 +628,67 @@ public class HttpClientFactoryTest { } + @Test + @DirtiesContext(methodMode = MethodMode.BEFORE_METHOD) + public void getCustomClientX509AuthWithHsmFacadeTrustStore() throws EaafException, ClientProtocolException, + IOException, KeyStoreException, UnrecoverableKeyException, NoSuchAlgorithmException { + + 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 KeyStoreConfiguration sslServerCertConfig = new KeyStoreConfiguration(); + sslServerCertConfig.setKeyStoreType(KeyStoreType.JKS); + sslServerCertConfig.setFriendlyName("SSL host cert"); + sslServerCertConfig.setSoftKeyStoreFilePath("src/test/resources/data/ssl_host.jks"); + sslServerCertConfig.setSoftKeyStorePassword("password"); + + Pair sslServerHostKeyStore = + keyStoreFactory.buildNewKeyStore(sslServerCertConfig); + + + final HttpClientConfiguration clientConfig = new HttpClientConfiguration("jUnit-client"); + clientConfig.setAuthMode("ssl"); + clientConfig.buildKeyStoreConfig("hsmfacade", null, null, "authhandler"); + clientConfig.setSslKeyAlias("authhandler-sign"); + clientConfig.setDisableTlsHostCertificateValidation(false); + + final CloseableHttpClient client = httpClientFactory.getHttpClient(clientConfig); + Assert.assertNotNull("httpClient", client); + + //set-up mock-up web-server with SSL client authentication + final Pair sslClientKeyStore = + keyStoreFactory.buildNewKeyStore(clientConfig.getKeyStoreConfig()); + final X509Certificate clientRootCert = (X509Certificate) sslClientKeyStore.getFirst() + .getCertificateChain(clientConfig.getSslKeyAlias())[1]; + final X509Certificate clientEeCert = (X509Certificate) sslClientKeyStore.getFirst() + .getCertificateChain(clientConfig.getSslKeyAlias())[0]; + + Key sslKey = sslServerHostKeyStore.getFirst().getKey("ssl", "password".toCharArray()); + X509Certificate sslCert = (X509Certificate) sslServerHostKeyStore.getFirst().getCertificate("ssl"); + KeyPair keyPair = new KeyPair(sslCert.getPublicKey(), (PrivateKey) sslKey); + HeldCertificate localhostCertificate = new HeldCertificate(keyPair, sslCert); + final HandshakeCertificates serverCertificates = new HandshakeCertificates.Builder() + .addTrustedCertificate(clientEeCert) + .addTrustedCertificate(clientRootCert) + .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_core_utils/src/test/resources/data/hsm_ee-RSA_rootcert.crt b/eaaf_core_utils/src/test/resources/data/hsm_ee-RSA_rootcert.crt new file mode 100644 index 00000000..aa83c8d9 --- /dev/null +++ b/eaaf_core_utils/src/test/resources/data/hsm_ee-RSA_rootcert.crt @@ -0,0 +1,3 @@ +-----BEGIN CERTIFICATE----- +MIICDTCCAbOgAwIBAgIIVLxIFI8kRpkwCgYIKoZIzj0EAwIwEjEQMA4GA1UEAwwHRUMtUm9vdDAeFw0yMDA2MTgwNzM2MTBaFw0yNTA2MTgwNzM2MTBaMDwxGzAZBgNVBAMMEmludC1yc2Eta2V5LTEtMDAwMTERMA8GA1UECgwIc29mdHdhcmUxCjAIBgNVBAUTATEwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDrM1ocQqtch95Dm21JHi0V35nlWZibsjLqR+g8ERdD1qFgun/X0I/Rbft+KxB8QsDX7UmIjXGdavNcEjY/XcbiJxUcpv7vn/2+x3JxZO6Iye/ut001okICt3OGIqP93ZEnIaTTNhDsK7OnvD/eUjlmuHiTaFq1dZLKYDQlz9jl/9F4axfrz1V7oo60iqFIW+7tlUeh8VGDUPjQpHghzjHXTJv/OIAt752K31Tn8KR3kvkn6WTPo8eOWVaPQ480Dik0e2afTPPJNZJ7BW111IwqBAOKp586yVsQ4XVEF8H64Cq+s+b4/HBboo9TDJKTJvo2yQmcTsahbH+Rlm20ifUTAgMBAAEwCgYIKoZIzj0EAwIDSAAwRQIhANKN/N2Atb5fbeHSB2Myv/JcNf9JonxFe92AOu4f62NNAiBjOEeg4OyJZKPiDl6aqYVtz1Qroo6xzUC9UVA4qNe4LA== +-----END CERTIFICATE----- diff --git a/eaaf_core_utils/src/test/resources/data/hsm_ee_eecert.crt b/eaaf_core_utils/src/test/resources/data/hsm_ee_eecert.crt new file mode 100644 index 00000000..b4c47c78 --- /dev/null +++ b/eaaf_core_utils/src/test/resources/data/hsm_ee_eecert.crt @@ -0,0 +1,3 @@ +-----BEGIN CERTIFICATE----- +MIIDEzCCArqgAwIBAgIIMrAOP6EqywMwCgYIKoZIzj0EAwIwEjEQMA4GA1UEAwwHRUMtUm9vdDAeFw0yMDA2MTgwNzM2MDlaFw0yNTA2MTgwNzM2MDlaMEMxIjAgBgNVBAMMGWludC1hdXRoaGFuZGxlci1zaWduLTAwMDExETAPBgNVBAoMCHNvZnR3YXJlMQowCAYDVQQFEwExMIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEA5j2vR8ZV88fOJzFHmLuPizHXk+aiE0fX8Bu63FqCNa2Vy343u2k+4HVQBWpAtyRej8nL7JQuhFa11YRtVF+Vos9cyUDk482sUlYA2LdAFIuHJP6iTRrrHYdiTskc3FQUm9m9KorVFwnfzqJ40wjn9hD0xKCE0odk9IHA5aLTul0mSMcHQ+MMu2cbelK2TWTEC0wcGum2NrTWukhf0E4/e4SuqMgEUPdHSEaNZLGKZU0kyVtXKK0HCtWF8OKGay4V504dk3qnmRHHO8ik3oyPk2yLipQAPL0bUGMZ7tlRLo5VxJvkZSsJfAzvYn1JzcQIt6bXszQGU/JS6dw7Jq99Ckzox/vk8p1YUPd4tCuUJPXzhFhI7CedHKGItwk7mQf5llgeVJy40sVrFwHLDPufY5mlm9C+gVBs71+ibT/sJ96gCueWqduzDX8fHzeaNVj2NFEx2pT46D5cbRWZBow3+Rx+hpge6SDMzaebpbkgLPX+35D952bMLjm+9T0DulGk1tLL2k4YK+1WWUprlo+rm65OA5FxnO5YxNmGk2Uizu3tSoZMxmXwEWoFfYaMsTt5Not3+CLPjfs6pE4ML9ifAOxW+pPsmanDiNaMiL0/aSddvvkv3lSMRiU9nYh3DuOu6sGIMaPdyYF0V/9Fua2SDZV3QKIbzPbJfEsXFKtjtzUCAwEAATAKBggqhkjOPQQDAgNHADBEAiBHsK819KfxVn91KAmjRjb7zx8TLLBApyaZHXi5HVrYUwIgQbF6u6bXziAWdijlJC0IOWdBuTLWmTvFqBH7uX1HL9c= +-----END CERTIFICATE----- diff --git a/eaaf_core_utils/src/test/resources/data/hsm_ee_rootcert.crt b/eaaf_core_utils/src/test/resources/data/hsm_ee_rootcert.crt new file mode 100644 index 00000000..fa7b132f --- /dev/null +++ b/eaaf_core_utils/src/test/resources/data/hsm_ee_rootcert.crt @@ -0,0 +1,3 @@ +-----BEGIN CERTIFICATE----- +MIIBPDCB46ADAgECAghZ0/gtbA6FrjAKBggqhkjOPQQDAjASMRAwDgYDVQQDDAdFQy1Sb290MB4XDTIwMDYxODA3MzU1M1oXDTMwMDYxODA3MzU1M1owEjEQMA4GA1UEAwwHRUMtUm9vdDBZMBMGByqGSM49AgEGCCqGSM49AwEHA0IABIjgL+6qiE9oj2yWCkVm6s7AaYkbDhTptYXTW92MhASiTqxL6g8tr28MlRA2P8HPrNSK9payeMe5QW9Kxn+EMPejIzAhMA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgWgMAoGCCqGSM49BAMCA0gAMEUCIDq2f4xjYD8pzr+mdzuT8wzePRnj/EatjmimGnvNt3FjAiEArezudh6G+wE+ds6S0dnFxG0o/BrbR0fiRNTQwiZA9ec= +-----END CERTIFICATE----- diff --git a/eaaf_core_utils/src/test/resources/data/server_host.crt b/eaaf_core_utils/src/test/resources/data/server_host.crt new file mode 100644 index 00000000..21d3a1e4 --- /dev/null +++ b/eaaf_core_utils/src/test/resources/data/server_host.crt @@ -0,0 +1,18 @@ +-----BEGIN CERTIFICATE----- +MIIC2TCCAcECBGB5WpEwDQYJKoZIhvcNAQELBQAwMTELMAkGA1UEBhMCQVQxDjAM +BgNVBAsMBWpVbml0MRIwEAYDVQQDDAlsb2NhbGhvc3QwHhcNMjEwNDE2MDkzNjE3 +WhcNMjQwMTEwMDkzNjE3WjAxMQswCQYDVQQGEwJBVDEOMAwGA1UECwwFalVuaXQx +EjAQBgNVBAMMCWxvY2FsaG9zdDCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoC +ggEBAJVYLzPzq7oBGS5Wer0++rHbp+DWI7srAV1lGHdq8ST6APh/7fEVWpdZDpMY +bOXl6uIiVmMsx/jUhQwOu4rFXThiQlwyQOv57SO7WHqNPqbRs/EUVnzW35aXU/DB +CmkqKyjK/+vuq7tIahlpqrppCzBVC9/Z15U+RMTdnATrohALNJovydH3VSkdkKX0 +5BDx779/8malTgyWTUgl+p3F/91iIIl4ZvIngo2ZYQCFm1nV6jmpErGFkG6YVrO7 +oe3OlGKFiXtqCmq+NSFeXsv/SaXWNUw82pYKuK/5EFSLX49HLBBDI14eOCuVLnGA +H/kG3tGteYMBNzSMmC/kcKgRDnUCAwEAATANBgkqhkiG9w0BAQsFAAOCAQEAAJn2 +a/VbtXGmHe9wmtu8K3noyECfG5fbu9URUjXhCBlXGcdjfz1gzrOHcmaBndk0a566 +R2W0fLvjLpjWChrj7r34EpNYGPMLV2gp3ZkiSGl9kv8mf9iChK6+ga3SlyHJuXXu +gw6eOIAxBrE/vLw+pZtCEV9yPrIydkt19jjejf1wjs5y2G7m5r5pBIh6Wlmmc4f2 +3M6l6Dge78WVdUaU5AeAHjgGgXwULxmLGxi6yiS5HsSeb79oGz9psHbq1EAvwOVY +sLepTbDQvX/VAAG7HOJXhdGM0fRIkM7HFA5+6joTHvAKhuMlFIJ8Y4QIG2QaIBAh +eBBh91x/aB2xOKs+Kg== +-----END CERTIFICATE----- diff --git a/eaaf_core_utils/src/test/resources/data/ssL_truststore.jks b/eaaf_core_utils/src/test/resources/data/ssL_truststore.jks new file mode 100644 index 00000000..4d7bc2f3 Binary files /dev/null and b/eaaf_core_utils/src/test/resources/data/ssL_truststore.jks differ diff --git a/eaaf_core_utils/src/test/resources/data/ssl_host.jks b/eaaf_core_utils/src/test/resources/data/ssl_host.jks new file mode 100644 index 00000000..4ca07595 Binary files /dev/null and b/eaaf_core_utils/src/test/resources/data/ssl_host.jks differ diff --git a/pom.xml b/pom.xml index c43076fa..e333b276 100644 --- a/pom.xml +++ b/pom.xml @@ -52,8 +52,8 @@ 3.4.5 2.2.0 1.2.4 - 1.67 - 1.67 + 1.68 + 1.68 1.7.30 1.2.3 -- cgit v1.2.3