From 65163646205b6e05139485fe957bceabe531f447 Mon Sep 17 00:00:00 2001 From: Christof Rabensteiner Date: Tue, 9 Jul 2019 12:56:54 +0200 Subject: Minor Fixes And Refactoring - Fix typo in SafeResponseToFileSink. - MoaZSException: Simplify constructor by replacing lots of arguments with the builder instance. - Fix minor codesmells (unused imports, superfluous braces). --- .../at/gv/egiz/moazs/client/ClientFactory.java | 113 +++++++++++++++++ .../at/gv/egiz/moazs/client/SSLContextCreator.java | 133 +++++++++++++++++++++ 2 files changed, 246 insertions(+) create mode 100644 src/main/java/at/gv/egiz/moazs/client/ClientFactory.java create mode 100644 src/main/java/at/gv/egiz/moazs/client/SSLContextCreator.java (limited to 'src/main/java/at/gv/egiz/moazs/client') diff --git a/src/main/java/at/gv/egiz/moazs/client/ClientFactory.java b/src/main/java/at/gv/egiz/moazs/client/ClientFactory.java new file mode 100644 index 0000000..d0a445b --- /dev/null +++ b/src/main/java/at/gv/egiz/moazs/client/ClientFactory.java @@ -0,0 +1,113 @@ +package at.gv.egiz.moazs.client; + +import at.gv.egiz.moazs.util.FileUtils; +import at.gv.egiz.moazs.util.StoreSOAPBodyBinaryInRepositoryInterceptor; +import at.gv.zustellung.app2mzs.xsd.ClientType; +import at.gv.zustellung.app2mzs.xsd.KeyStoreType; +import at.gv.zustellung.app2mzs.xsd.SSLType; +import org.apache.cxf.configuration.jsse.TLSClientParameters; +import org.apache.cxf.endpoint.Client; +import org.apache.cxf.frontend.ClientProxy; +import org.apache.cxf.jaxws.JaxWsClientFactoryBean; +import org.apache.cxf.jaxws.JaxWsProxyFactoryBean; +import org.apache.cxf.transport.http.HTTPConduit; +import org.apache.cxf.transports.http.configuration.HTTPClientPolicy; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; + +import javax.annotation.Nullable; +import javax.net.ssl.SSLContext; +import javax.xml.ws.BindingProvider; +import javax.xml.ws.soap.SOAPBinding; + +import static at.gv.zustellung.app2mzs.xsd.KeyStoreType.keyStoreTypeBuilder; + +@Component +public class ClientFactory { + + private static final Logger log = LoggerFactory.getLogger(ClientFactory.class); + + private final StoreSOAPBodyBinaryInRepositoryInterceptor storeResponseInterceptor; + private final SSLContextCreator sslContextCreator; + private final FileUtils fileUtils; + + @Autowired + public ClientFactory(StoreSOAPBodyBinaryInRepositoryInterceptor storeResponseInterceptor, + SSLContextCreator creator, + FileUtils fileUtils) { + this.storeResponseInterceptor = storeResponseInterceptor; + this.sslContextCreator = creator; + this.fileUtils = fileUtils; + } + + /** + * Creates a client that communicates with a soap service. + * + * @param params for the client, such as service url and ssl parameters. + * @return the client + */ + public T create(ClientType params, Class clazz) { + + var factory = new JaxWsClientFactoryBean(); + factory.setServiceClass(clazz); + factory.setAddress(params.getURL()); + factory.getInInterceptors().add(storeResponseInterceptor); + + var proxy = new JaxWsProxyFactoryBean(factory).create(); + Client client = ClientProxy.getClient(proxy); + HTTPConduit http = (HTTPConduit) client.getConduit(); + + var bindingProvider = (BindingProvider) proxy; + SOAPBinding binding = (SOAPBinding) bindingProvider.getBinding(); + binding.setMTOMEnabled(true); + + var httpClientPolicy = new HTTPClientPolicy(); + httpClientPolicy.setConnectionTimeout(params.getConnectionTimeout().longValueExact()); + httpClientPolicy.setReceiveTimeout(params.getReceiveTimeout().longValueExact()); + http.setClient(httpClientPolicy); + + if (params.getURL().startsWith("https")) { + TLSClientParameters tlsParams = setupTLSParams(params.getSSL()); + http.setTlsClientParameters(tlsParams); + log.info("SSLContext initialized. "); + } + + return ((T)proxy); + } + + private TLSClientParameters setupTLSParams(SSLType ssl) { + + var tlsParams = new TLSClientParameters(); + var keystore = resolveKeyStorePath(ssl.getKeyStore()); + + SSLContext sslContext; + if (ssl.isTrustAll()) { + sslContext = sslContextCreator.createUnsafeSSLContext(keystore); + } else { + var truststore = resolveKeyStorePath(ssl.getTrustStore()); + sslContext = sslContextCreator.createSSLContext(keystore, truststore); + } + tlsParams.setSSLSocketFactory(sslContext.getSocketFactory()); + + if (ssl.isLaxHostNameVerification()) { + tlsParams.setDisableCNCheck(true); + } + + return tlsParams; + } + + private KeyStoreType resolveKeyStorePath(@Nullable KeyStoreType store) { + + if (store == null) return null; + + var resolvedURI = "file:" + fileUtils.determinePath(store.getFileName()); + log.trace("Resolved key store path from {} to {}.", store.getFileName(), resolvedURI); + + return keyStoreTypeBuilder(store) + .withFileName(resolvedURI) + .build(); + } + +} diff --git a/src/main/java/at/gv/egiz/moazs/client/SSLContextCreator.java b/src/main/java/at/gv/egiz/moazs/client/SSLContextCreator.java new file mode 100644 index 0000000..8fb5d80 --- /dev/null +++ b/src/main/java/at/gv/egiz/moazs/client/SSLContextCreator.java @@ -0,0 +1,133 @@ +package at.gv.egiz.moazs.client; + +import at.gv.egiz.eaaf.core.impl.utils.KeyStoreUtils; +import at.gv.zustellung.app2mzs.xsd.KeyStoreType; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.lang.Nullable; +import org.springframework.stereotype.Component; +import java.security.cert.CertificateException; +import java.security.cert.X509Certificate; +import javax.net.ssl.X509TrustManager; + +import javax.net.ssl.*; +import java.io.IOException; +import java.security.*; + +import static at.gv.egiz.moazs.MoaZSException.moaZSException; +import static java.lang.String.format; + +@Component + /** + * Adapted from at.asitplus.eidas.specific.modules.authmodule_eIDASv2.szr.SZRClient + */ +public class SSLContextCreator { + + private static final Logger log = LoggerFactory.getLogger(SSLContextCreator.class); + + private static final String SSL_WARN_MAN_IN_THE_MIDDLE_MSG = + "HTTP Client trusts ANY server certificate and is therefore vulnerable to Man-In-The-Middle attacks. " + + "Use this configuration for testing purposes only and NOT IN PRODUCTION. "; + + /** + * Creates an SSL Context. + * + * @param keystore (if null, use no key store) + * @param truststore (if null, use default trust store) + * @throws at.gv.egiz.moazs.MoaZSException + */ + public SSLContext createSSLContext(@Nullable KeyStoreType keystore, @Nullable KeyStoreType truststore) { + return createSSLContext(keystore, false, truststore); + } + + /** + * Creates an SSL Context that trusts all certificates. Don't use in production. + * + * @param keystore (if null, use no key store) + * @throws at.gv.egiz.moazs.MoaZSException + */ + public SSLContext createUnsafeSSLContext(@Nullable KeyStoreType keystore) { + log.warn(SSL_WARN_MAN_IN_THE_MIDDLE_MSG); + return createSSLContext(keystore, true, null); + } + + private SSLContext createSSLContext(@Nullable KeyStoreType keystore, boolean trustAll, @Nullable KeyStoreType truststore) { + try { + SSLContext context = SSLContext.getInstance("TLS"); + KeyManager[] keyManager = initKeyManager(keystore); + TrustManager[] trustManager = trustAll + ? new TrustManager[]{new TrustAllManager()} + : initTrustManager(truststore); + context.init(keyManager, trustManager, new SecureRandom()); + return context; + } catch (NoSuchAlgorithmException | KeyManagementException e) { + throw moaZSException("SSLContext initialization FAILED.", e); + } + } + + private KeyManager[] initKeyManager(KeyStoreType keystore) { + if (keystore == null) { + log.trace("No keystore path provided. NOT using SSL client authentication. "); + return null; + } else { + log.trace("Find keystore path: {}. Injecting SSL client certificate... ", keystore.getFileName()); + try { + KeyStore keyStore = KeyStoreUtils.loadKeyStore( + keystore.getFileType(), keystore.getFileName(), keystore.getPassword()); + KeyManagerFactory kmf = KeyManagerFactory.getInstance("SunX509"); + kmf.init(keyStore, keystore.getPassword().toCharArray()); + log.trace("SSL client certificate injected."); + return kmf.getKeyManagers(); + } catch (IOException | GeneralSecurityException e) { + throw moaZSException(format("Can NOT load SSL client certificate from path: %s.", + keystore.getFileName()), e); + } + } + } + + private TrustManager[] initTrustManager(KeyStoreType truststore) { + if (truststore == null) { + log.trace("Using default truststore. "); + return null; + } else { + log.trace("Find truststore path: {}. Injecting SSL truststore... ", truststore.getFileName()); + try { + KeyStore trustStore = KeyStoreUtils.loadKeyStore( + truststore.getFileType(), truststore.getFileName(), truststore.getPassword()); + TrustManagerFactory tmf = TrustManagerFactory.getInstance("SunX509"); + tmf.init(trustStore); + log.trace("SSL TrustStore injected to client. "); + return tmf.getTrustManagers(); + } catch (GeneralSecurityException | IOException e) { + throw moaZSException(format("Can NOT open SSL TrustStore from path: %s.", + truststore.getFileName()), e); + } + } + } + + /** + * Class implementing a trust manager that trusts all certificates. + * + * @author Arne Tauber + */ + public static class TrustAllManager implements X509TrustManager { + + private static Logger log = LoggerFactory.getLogger(TrustAllManager.class); + + public X509Certificate[] getAcceptedIssuers() { + return new X509Certificate[0]; + } + + public void checkClientTrusted(X509Certificate[] arg0, String arg1) + throws CertificateException { + log.debug("Automatically accepting client certificate as trusted."); + } + + public void checkServerTrusted(X509Certificate[] arg0, String arg1) + throws CertificateException { + log.debug("Automatically accepting server certificate as trusted."); + } + } + + +} -- cgit v1.2.3