package at.asitplus.eidas.specific.modules.authmodule_eIDASv2.szr; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.net.URL; import java.security.KeyManagementException; import java.security.KeyStore; import java.security.KeyStoreException; import java.security.NoSuchAlgorithmException; import java.security.SecureRandom; import java.security.UnrecoverableKeyException; import java.util.ArrayList; import java.util.List; import java.util.Map; import javax.annotation.PostConstruct; import javax.annotation.Resource; import javax.net.ssl.KeyManager; import javax.net.ssl.KeyManagerFactory; import javax.net.ssl.SSLContext; import javax.net.ssl.TrustManager; import javax.net.ssl.TrustManagerFactory; import javax.xml.bind.JAXBContext; import javax.xml.bind.Marshaller; import javax.xml.namespace.QName; import javax.xml.transform.Source; import javax.xml.transform.Transformer; import javax.xml.transform.TransformerException; import javax.xml.transform.TransformerFactory; import javax.xml.transform.stream.StreamResult; import javax.xml.transform.stream.StreamSource; import javax.xml.ws.BindingProvider; import javax.xml.ws.Dispatch; import javax.xml.ws.WebServiceContext; 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.xpath.XPathAPI; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import org.w3._2000._09.xmldsig.KeyValueType; import org.w3c.dom.Document; import org.w3c.dom.Element; import at.asitplus.eidas.specific.modules.authmodule_eIDASv2.Constants; import at.asitplus.eidas.specific.modules.authmodule_eIDASv2.exception.SZRCommunicationException; import at.asitplus.eidas.specific.modules.authmodule_eIDASv2.utils.LoggingHandler; import at.gv.egiz.eaaf.core.api.data.XMLNamespaceConstants; import at.gv.egiz.eaaf.core.api.idp.IConfiguration; import at.gv.egiz.eaaf.core.impl.utils.DOMUtils; import at.gv.egiz.eaaf.core.impl.utils.FileUtils; import at.gv.egiz.eaaf.core.impl.utils.KeyStoreUtils; import szrservices.GetBPK; import szrservices.GetBPKResponse; import szrservices.GetIdentityLink; import szrservices.GetIdentityLinkResponse; import szrservices.IdentityLinkType; import szrservices.PersonInfoType; import szrservices.SZR; import szrservices.SZRException_Exception; @Service("SZRClientForeIDAS") public class SZRClient { private static final Logger log = LoggerFactory.getLogger(SZRClient.class); private static final String CLIENT_DEFAULT = "DefaultClient"; private static final String CLIENT_RAW = "RawClient"; @Autowired private IConfiguration basicConfig; @Resource private WebServiceContext wsContext; //client for anything, without identitylink private SZR szr = null; //RAW client is needed for identitylink private Dispatch dispatch = null; private SZRService szrService = null; private String szrURL = null; private QName qname = null; public IdentityLinkType getIdentityLink(PersonInfoType personInfo, List keyValue, Boolean insertERnP) throws SZRCommunicationException { try { return szr.getIdentityLink( personInfo, keyValue, insertERnP); } catch (SZRException_Exception e) { log.warn("SZR communication FAILED. Reason: " + e.getMessage(), e); throw new SZRCommunicationException("ernb.02", new Object[] {e.getMessage()}, e); } } public IdentityLinkType getIdentityLinkInRawMode(PersonInfoType personInfo, List keyValue, Boolean insertERnP) throws SZRCommunicationException { try { GetIdentityLink getIDL = new GetIdentityLink(); getIDL.setInsertERnP(insertERnP); getIDL.setPersonInfo(personInfo); getIDL.getKeyValue().addAll(keyValue); JAXBContext jaxbContext = JAXBContext.newInstance(GetIdentityLink.class); Marshaller jaxbMarshaller = jaxbContext.createMarshaller(); final ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); jaxbMarshaller.marshal(getIDL, outputStream); outputStream.flush(); Source source = new StreamSource(new ByteArrayInputStream(outputStream.toByteArray())); outputStream.close(); log.trace("Requesting SZR ... "); Source response = dispatch.invoke(source); log.trace("Receive RAW response from SZR"); byte[] szrResponse = sourceToByteArray(response); JAXBContext ctx = JAXBContext.newInstance(IdentityLinkType.class .getPackage().getName()); GetIdentityLinkResponse jaxbElement = (GetIdentityLinkResponse) ctx .createUnmarshaller().unmarshal(new ByteArrayInputStream(szrResponse)); //build response log.trace(new String(szrResponse)); log.trace("Signature successfully created. Extracting from MOA-SS container."); // ok, we have success Document doc = DOMUtils.parseDocument( new ByteArrayInputStream(szrResponse), true, XMLNamespaceConstants.ALL_SCHEMA_LOCATIONS, null, null ); String xpathExpression = "//saml:Assertion"; Element nsNode = doc.createElementNS("urn:oasis:names:tc:SAML:1.0:assertion", "saml:NSNode"); log.trace("Selecting signed doc " + xpathExpression); Element documentNode = (Element) XPathAPI.selectSingleNode(doc, xpathExpression, nsNode); log.trace("Signed document: " + DOMUtils.serializeNode(documentNode)); IdentityLinkType idl = new IdentityLinkType(); idl.setAssertion(documentNode); idl.setPersonInfo(jaxbElement.getGetIdentityLinkReturn().getPersonInfo()); return idl; //IdentityLinkType idlResp = this.szr.getIdentityLink(personInfo, keyValue, insertERnP); } catch ( Exception e) { log.warn("SZR communication FAILED. Reason: " + e.getMessage(), e); throw new SZRCommunicationException("ernb.02", new Object[] {e.getMessage()}, e); } } public String getBPK(PersonInfoType personInfo, String target, String vkz) throws SZRCommunicationException { try { GetBPK parameters = new GetBPK(); parameters.setPersonInfo(personInfo); parameters.setBereichsKennung(target); parameters.setVKZ(vkz); GetBPKResponse result = this.szr.getBPK(parameters); return result.getGetBPKReturn(); } catch (SZRException_Exception e) { log.warn("SZR communication FAILED. Reason: " + e.getMessage(), e); throw new SZRCommunicationException("ernb.02", new Object[] {e.getMessage()}, e); } } @PostConstruct private void initialize() { log.info("Starting SZR-Client initialization .... "); URL url = SZRClient.class.getResource("/szr_client/SZR-1.WSDL"); boolean useTestSZR = basicConfig.getBasicMOAIDConfigurationBoolean( Constants.CONIG_PROPS_EIDAS_SZRCLIENT_USETESTSERVICE, true); if (useTestSZR) { log.debug("Initializing SZR test environment configuration."); qname = SZRService.SZRTestumgebung; szrService = new SZRService(url, new QName("urn:SZRServices", "SZRService")); szr = szrService.getSZRTestumgebung(); szrURL = basicConfig.getBasicConfiguration(Constants.CONIG_PROPS_EIDAS_SZRCLIENT_ENDPOINT_TEST); } else { log.debug("Initializing SZR productive configuration."); qname = SZRService.SZRProduktionsumgebung; szrService = new SZRService(url, new QName("urn:SZRServices", "SZRService")); szr = szrService.getSZRProduktionsumgebung(); szrURL = basicConfig.getBasicConfiguration(Constants.CONIG_PROPS_EIDAS_SZRCLIENT_ENDPOINT_PROD); } //create raw client; dispatch = szrService.createDispatch(qname, Source.class, javax.xml.ws.Service.Mode.PAYLOAD); if (StringUtils.isEmpty(szrURL)) { log.error("No SZR service-URL found. SZR-Client initalisiation failed."); throw new RuntimeException("No SZR service URL found. SZR-Client initalisiation failed."); } log.info("Use SZR service-URL: " + szrURL); injectBindingProvider((BindingProvider) szr, CLIENT_DEFAULT); injectBindingProvider((BindingProvider) dispatch, CLIENT_RAW); log.debug("Inject HTTP client settings ... "); injectHTTPClient(szr, CLIENT_DEFAULT); injectHTTPClient(dispatch, CLIENT_RAW); log.info("SZR-Client initialization successfull"); } private void injectHTTPClient(Object raw, String clientType) { //extract client from implementation Client client = null; 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 HTTPConduit http = (HTTPConduit) client.getConduit(); //set timeout policy HTTPClientPolicy httpClientPolicy = new HTTPClientPolicy(); httpClientPolicy.setConnectionTimeout( Integer.parseInt(basicConfig.getBasicConfiguration( Constants.CONIG_PROPS_EIDAS_SZRCLIENT_TIMEOUT_CONNECTION, Constants.HTTP_CLIENT_DEFAULT_TIMEOUT_CONNECTION)) * 1000); httpClientPolicy.setReceiveTimeout( Integer.parseInt(basicConfig.getBasicConfiguration( Constants.CONIG_PROPS_EIDAS_SZRCLIENT_TIMEOUT_RESPONSE, Constants.HTTP_CLIENT_DEFAULT_TIMEOUT_RESPONSE)) * 1000); http.setClient(httpClientPolicy); //inject SSL context in case of https if (szrURL.toLowerCase().startsWith("https")) { log.debug("Adding SSLContext to client: " + clientType +" ... "); TLSClientParameters tlsParams = new TLSClientParameters(); tlsParams.setSSLSocketFactory(createSSLContext(clientType).getSocketFactory()); http.setTlsClientParameters(tlsParams ); log.info("SSLContext initialized for client: " + clientType); } } private void injectBindingProvider(BindingProvider bindingProvider, String clientType) { 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(); bindingProvider.getBinding().setHandlerChain(handlerList); } //add logging handler to trace messages if required if (basicConfig.getBasicMOAIDConfigurationBoolean( Constants.CONIG_PROPS_EIDAS_SZRCLIENT_DEBUG_TRACEMESSAGES, false)) { LoggingHandler loggingHandler = new LoggingHandler(); handlerList.add(loggingHandler); } } private SSLContext createSSLContext(String clientType) { try { SSLContext context = SSLContext.getInstance("TLS"); //initialize key-mangager for SSL client-authentication KeyManager[] keyManager = null; String keyStorePath = basicConfig.getBasicConfiguration(Constants.CONIG_PROPS_EIDAS_SZRCLIENT_SSL_KEYSTORE_PATH); String keyStorePassword = basicConfig.getBasicConfiguration(Constants.CONIG_PROPS_EIDAS_SZRCLIENT_SSL_KEYSTORE_PASSWORD); if (StringUtils.isNotEmpty(keyStorePath)) { log.trace("Find keyStore path: " + keyStorePath + " Injecting SSL client certificate ... "); try { KeyStore keyStore = KeyStoreUtils.loadKeyStore( FileUtils.makeAbsoluteURL(keyStorePath, basicConfig.getConfigurationRootDirectory()), keyStorePassword); KeyManagerFactory kmf = KeyManagerFactory.getInstance("SunX509"); kmf.init(keyStore, keyStorePassword.toCharArray()); keyManager = kmf.getKeyManagers(); log.debug("SSL client certificate injected to client: " + clientType); } catch (KeyStoreException | IOException | UnrecoverableKeyException e) { log.error("Can NOT load SSL client certificate from path: " + keyStorePath); throw new RuntimeException("Can NOT load SSL client certificate from path: " + keyStorePath, e); } } //initialize SSL TrustStore TrustManager[] trustManager = null; String trustStorePath = basicConfig.getBasicConfiguration(Constants.CONIG_PROPS_EIDAS_SZRCLIENT_SSL_TRUSTSTORE_PATH); String trustStorePassword = basicConfig.getBasicConfiguration(Constants.CONIG_PROPS_EIDAS_SZRCLIENT_SSL_TRUSTSTORE_PASSWORD); if (StringUtils.isNotEmpty(trustStorePath)) { log.trace("Find trustStore path: " + trustStorePath + " Injecting SSL TrustStore ... "); try { KeyStore trustStore = KeyStoreUtils.loadKeyStore( FileUtils.makeAbsoluteURL(trustStorePath, basicConfig.getConfigurationRootDirectory()), trustStorePassword); TrustManagerFactory tmf = TrustManagerFactory.getInstance("SunX509"); tmf.init(trustStore); trustManager = tmf.getTrustManagers(); log.debug("SSL TrustStore injected to client: " + clientType); } catch (KeyStoreException | IOException e) { log.error("Can NOT open SSL TrustStore from path: " + trustStorePath); throw new RuntimeException("Can NOT open SSL TrustStore from path: " + trustStorePath, e); } } context.init(keyManager, trustManager, new SecureRandom()); return context; } catch (NoSuchAlgorithmException | KeyManagementException e) { log.error("SSLContext initialization FAILED.", e); throw new RuntimeException("SSLContext initialization FAILED.", e); } } private byte[] sourceToByteArray(Source result) throws TransformerException { TransformerFactory factory = TransformerFactory.newInstance(); Transformer transformer = factory.newTransformer(); transformer.setOutputProperty("omit-xml-declaration", "yes"); transformer.setOutputProperty("method", "xml"); ByteArrayOutputStream out = new ByteArrayOutputStream(); StreamResult streamResult = new StreamResult(); streamResult.setOutputStream(out); transformer.transform(result, streamResult); return out.toByteArray(); } }