package at.asitplus.eidas.specific.modules.auth.eidas.v2.clients; import java.security.KeyManagementException; import java.security.KeyStore; import java.security.NoSuchAlgorithmException; import java.security.Provider; import java.util.ArrayList; import java.util.List; import java.util.Map; import javax.net.ssl.SSLContext; import javax.xml.ws.BindingProvider; import javax.xml.ws.handler.Handler; import org.apache.commons.lang3.StringUtils; import org.apache.cxf.configuration.jsse.TLSClientParameters; import org.apache.cxf.endpoint.Client; import org.apache.cxf.frontend.ClientProxy; import org.apache.cxf.jaxws.DispatchImpl; import org.apache.cxf.transport.http.HTTPConduit; import org.apache.cxf.transports.http.configuration.HTTPClientPolicy; import org.apache.http.ssl.SSLContextBuilder; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.lang.Nullable; import at.asitplus.eidas.specific.modules.auth.eidas.v2.utils.LoggingHandler; 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.HttpUtils; import lombok.Builder; import lombok.Getter; import lombok.extern.slf4j.Slf4j; @Slf4j public class AbstractSoapClient { @Autowired protected IConfiguration basicConfig; @Autowired EaafKeyStoreFactory keyStoreFactory; @Builder @Getter public static class HttpClientConfig { private final String clientName; private final String clientUrl; private final String clientType; private final String connectionTimeout; private final String responseTimeout; private final KeyStoreConfiguration keyStoreConfig; private final String keyAlias; private final String keyPassword; private final KeyStoreConfiguration trustStoreConfig; @Builder.Default private final boolean trustAll = false; } /** * Build a validated KeyStore Configuration-Object from configuration keys. * * @param keyStoreTypeKey Configuration key for type * @param keyStorePathKey Configuration key for path * @param keyStorePasswordKey Configuration key for password * @param keyStoreNameKey Configuration key for name * @param friendlyName Friendlyname for logging and errorhandling * @return Valid KeyStore configuration or null if no type was * defined * @throws EaafConfigurationException In case of validation error */ @Nullable protected KeyStoreConfiguration buildKeyStoreConfiguration(String keyStoreTypeKey, String keyStorePathKey, String keyStorePasswordKey, String keyStoreNameKey, String friendlyName) throws EaafConfigurationException { if (StringUtils.isNotEmpty(basicConfig.getBasicConfiguration(keyStoreTypeKey))) { log.debug("Starting configuration of: {} ... ", friendlyName); final KeyStoreConfiguration config = new KeyStoreConfiguration(); config.setFriendlyName(friendlyName); config.setKeyStoreType(basicConfig.getBasicConfiguration(keyStoreTypeKey, KeyStoreType.PKCS12.name())); config.setKeyStoreName(basicConfig.getBasicConfiguration(keyStoreNameKey)); config.setSoftKeyStoreFilePath(basicConfig.getBasicConfiguration(keyStorePathKey)); config.setSoftKeyStorePassword(basicConfig.getBasicConfiguration(keyStorePasswordKey)); // validate keystore configuration config.validate(); return config; } else { log.info("Skipping configuration of: {}", friendlyName); return null; } } protected void injectHttpClient(Object raw, HttpClientConfig config) { // extract client from implementation Client client; if (raw instanceof DispatchImpl) { client = ((DispatchImpl) raw).getClient(); } else if (raw instanceof Client) { client = ClientProxy.getClient(raw); } else { throw new RuntimeException("SOAP Client for SZR connection is of UNSUPPORTED type: " + raw.getClass() .getName()); } // set basic connection policies final HTTPConduit http = (HTTPConduit) client.getConduit(); // set timeout policy final HTTPClientPolicy httpClientPolicy = new HTTPClientPolicy(); httpClientPolicy.setConnectionTimeout(Integer.parseInt(config.getConnectionTimeout()) * 1000L); httpClientPolicy.setReceiveTimeout(Integer.parseInt(config.getResponseTimeout()) * 1000L); http.setClient(httpClientPolicy); // inject SSL context in case of https if (config.getClientUrl().toLowerCase().startsWith("https")) { try { log.debug("Adding SSLContext to client: " + config.getClientType() + " ... "); final TLSClientParameters tlsParams = new TLSClientParameters(); if (config.getKeyStoreConfig() != null) { final SSLContext sslContext = HttpUtils.buildSslContextWithSslClientAuthentication( keyStoreFactory.buildNewKeyStore(config.getKeyStoreConfig()), config.getKeyAlias(), config.getKeyPassword(), loadTrustStore(config.getTrustStoreConfig(), config.getClientName()), config.isTrustAll(), config.getClientName()); tlsParams.setSSLSocketFactory(sslContext.getSocketFactory()); } else { log.debug( "No KeyStore for SSL Client Auth. found. Initializing SSLContext for: {} without authentication ... ", config.getClientName()); tlsParams.setSSLSocketFactory(SSLContextBuilder.create().build().getSocketFactory()); } http.setTlsClientParameters(tlsParams); log.info("SSLContext initialized for client: " + config.getClientType()); } catch (EaafException | KeyManagementException | NoSuchAlgorithmException e) { log.error("SSLContext initialization FAILED.", e); throw new RuntimeException("SSLContext initialization FAILED.", e); } } } private Pair loadTrustStore(KeyStoreConfiguration trustStoreConfig, String friendlyName) throws EaafException { if (trustStoreConfig != null) { log.info("Build custom SSL truststore for: {}", friendlyName); return keyStoreFactory.buildNewKeyStore(trustStoreConfig); } else { log.info("Use default SSL truststore for: {}", friendlyName); return null; } } protected void injectBindingProvider(BindingProvider bindingProvider, String clientType, String szrUrl, boolean enableTraceLogging) { final Map requestContext = bindingProvider.getRequestContext(); requestContext.put(BindingProvider.ENDPOINT_ADDRESS_PROPERTY, szrUrl); log.trace("Adding JAX-WS request/response trace handler to client: " + clientType); List handlerList = bindingProvider.getBinding().getHandlerChain(); if (handlerList == null) { handlerList = new ArrayList<>(); } // add unique TransactionId into SOAP header handlerList.add(new BmiSoapTransactionHeaderInterceptor(clientType)); // add logging handler to trace messages if required if (enableTraceLogging) { final LoggingHandler loggingHandler = new LoggingHandler(); handlerList.add(loggingHandler); } bindingProvider.getBinding().setHandlerChain(handlerList); } }