/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.synapse.transport.nhttp.config; import java.io.FileInputStream; import java.io.IOException; import java.net.InetSocketAddress; import java.security.GeneralSecurityException; import java.security.KeyStore; import java.security.cert.X509Certificate; import java.util.ArrayList; import java.util.Enumeration; import java.util.HashMap; import java.util.Iterator; import java.util.List; import java.util.Locale; import java.util.Map; 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.net.ssl.X509KeyManager; import javax.xml.namespace.QName; import org.apache.axiom.om.OMElement; import org.apache.axiom.om.impl.builder.StAXOMBuilder; import org.apache.axis2.AxisFault; import org.apache.axis2.description.Parameter; import org.apache.axis2.description.TransportInDescription; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.apache.http.HttpHost; import org.apache.http.params.HttpParams; import org.apache.synapse.transport.certificatevalidation.RevocationVerificationManager; import org.apache.synapse.transport.http.conn.SSLClientAuth; import org.apache.synapse.transport.http.conn.SSLContextDetails; import org.apache.synapse.transport.http.conn.ServerConnFactory; import org.apache.synapse.transport.http.conn.ServerSSLSetupHandler; public class ServerConnFactoryBuilder { private final Log log = LogFactory.getLog(ServerConnFactoryBuilder.class); private final TransportInDescription transportIn; private final HttpHost host; private final String name; protected SSLContextDetails ssl; private Map<InetSocketAddress, SSLContextDetails> sslByIPMap = null; public ServerConnFactoryBuilder(final TransportInDescription transportIn, final HttpHost host) { this.transportIn = transportIn; this.host = host; this.name = transportIn.getName().toUpperCase(Locale.US); } protected SSLContextDetails createSSLContext( final OMElement keyStoreEl, final OMElement trustStoreEl, final OMElement cientAuthEl, final OMElement httpsProtocolsEl, final RevocationVerificationManager verificationManager, final String sslProtocol) throws AxisFault { KeyManager[] keymanagers = null; TrustManager[] trustManagers = null; if (keyStoreEl != null) { String location = getValueOfElementWithLocalName(keyStoreEl,"Location"); String type = getValueOfElementWithLocalName(keyStoreEl,"Type"); String storePassword = getValueOfElementWithLocalName(keyStoreEl,"Password"); String keyPassword = getValueOfElementWithLocalName(keyStoreEl, "KeyPassword"); FileInputStream fis = null; try { KeyStore keyStore = KeyStore.getInstance(type); fis = new FileInputStream(location); if (log.isInfoEnabled()) { log.debug(name + " Loading Identity Keystore from : " + location); } keyStore.load(fis, storePassword.toCharArray()); KeyManagerFactory kmfactory = KeyManagerFactory.getInstance( KeyManagerFactory.getDefaultAlgorithm()); kmfactory.init(keyStore, keyPassword.toCharArray()); keymanagers = kmfactory.getKeyManagers(); if (log.isInfoEnabled() && keymanagers != null) { for (KeyManager keymanager: keymanagers) { if (keymanager instanceof X509KeyManager) { X509KeyManager x509keymanager = (X509KeyManager) keymanager; Enumeration<String> en = keyStore.aliases(); while (en.hasMoreElements()) { String s = en.nextElement(); X509Certificate[] certs = x509keymanager.getCertificateChain(s); if (certs==null) continue; for (X509Certificate cert: certs) { log.debug(name + " Subject DN: " + cert.getSubjectDN()); log.debug(name + " Issuer DN: " + cert.getIssuerDN()); } } } } } } catch (GeneralSecurityException gse) { log.error(name + " Error loading Key store : " + location, gse); throw new AxisFault("Error loading Key store : " + location, gse); } catch (IOException ioe) { log.error(name + " Error opening Key store : " + location, ioe); throw new AxisFault("Error opening Key store : " + location, ioe); } finally { if (fis != null) { try { fis.close(); } catch (IOException ignore) {} } } } if (trustStoreEl != null) { String location = getValueOfElementWithLocalName(trustStoreEl, "Location"); String type = getValueOfElementWithLocalName(trustStoreEl, "Type"); String storePassword = getValueOfElementWithLocalName(trustStoreEl, "Password"); FileInputStream fis = null; try { KeyStore trustStore = KeyStore.getInstance(type); fis = new FileInputStream(location); if (log.isInfoEnabled()) { log.debug(name + " Loading Trust Keystore from : " + location); } trustStore.load(fis, storePassword.toCharArray()); TrustManagerFactory trustManagerfactory = TrustManagerFactory.getInstance( TrustManagerFactory.getDefaultAlgorithm()); trustManagerfactory.init(trustStore); trustManagers = trustManagerfactory.getTrustManagers(); } catch (GeneralSecurityException gse) { log.error(name + " Error loading Key store : " + location, gse); throw new AxisFault("Error loading Key store : " + location, gse); } catch (IOException ioe) { log.error(name + " Error opening Key store : " + location, ioe); throw new AxisFault("Error opening Key store : " + location, ioe); } finally { if (fis != null) { try { fis.close(); } catch (IOException ignore) {} } } } final String s = cientAuthEl != null ? cientAuthEl.getText() : null; final SSLClientAuth clientAuth; if ("optional".equalsIgnoreCase(s)) { clientAuth = SSLClientAuth.OPTIONAL; } else if ("require".equalsIgnoreCase(s)) { clientAuth = SSLClientAuth.REQUIRED; } else { clientAuth = null; } String[] httpsProtocols = null; final String configuredHttpsProtocols = httpsProtocolsEl != null ? httpsProtocolsEl.getText() : null; if (configuredHttpsProtocols != null && configuredHttpsProtocols.trim().length() != 0) { String[] configuredValues = configuredHttpsProtocols.trim().split(","); List<String> protocolList = new ArrayList<String>(configuredValues.length); for (String protocol : configuredValues) { if (!protocol.trim().isEmpty()) { protocolList.add(protocol.trim()); } } httpsProtocols = protocolList.toArray(new String[protocolList.size()]); } try { final String sslProtocolValue = sslProtocol != null ? sslProtocol : "TLS"; SSLContext sslContext = SSLContext.getInstance(sslProtocolValue); sslContext.init(keymanagers, trustManagers, null); ServerSSLSetupHandler sslSetupHandler = (clientAuth != null || httpsProtocols != null) ? new ServerSSLSetupHandler(clientAuth,httpsProtocols,verificationManager) : null; return new SSLContextDetails(sslContext, sslSetupHandler); } catch (GeneralSecurityException gse) { log.error(name + " Unable to create SSL context with the given configuration", gse); throw new AxisFault("Unable to create SSL context with the given configuration", gse); } } public ServerConnFactoryBuilder parseSSL() throws AxisFault { Parameter keyParam = transportIn.getParameter("keystore"); Parameter trustParam = transportIn.getParameter("truststore"); Parameter clientAuthParam = transportIn.getParameter("SSLVerifyClient"); Parameter httpsProtocolsParam = transportIn.getParameter("HttpsProtocols"); final Parameter sslpParameter = transportIn.getParameter("SSLProtocol"); final String sslProtocol = sslpParameter != null ? sslpParameter.getValue().toString() : "TLS"; OMElement keyStoreEl = keyParam != null ? keyParam.getParameterElement().getFirstElement() : null; OMElement trustStoreEl = trustParam != null ? trustParam.getParameterElement().getFirstElement() : null; OMElement clientAuthEl = clientAuthParam != null ? clientAuthParam.getParameterElement() : null; OMElement httpsProtocolsEl = httpsProtocolsParam != null ? httpsProtocolsParam.getParameterElement() : null; final Parameter cvp = transportIn.getParameter("CertificateRevocationVerifier"); final String cvEnable = cvp != null ? cvp.getParameterElement().getAttribute(new QName("enable")).getAttributeValue() : null; RevocationVerificationManager revocationVerifier = null; if ("true".equalsIgnoreCase(cvEnable)) { String cacheSizeString = cvp.getParameterElement().getFirstChildWithName(new QName("CacheSize")).getText(); String cacheDelayString = cvp.getParameterElement().getFirstChildWithName(new QName("CacheDelay")).getText(); Integer cacheSize = null; Integer cacheDelay = null; try { cacheSize = new Integer(cacheSizeString); cacheDelay = new Integer(cacheDelayString); } catch (NumberFormatException e) {} revocationVerifier = new RevocationVerificationManager(cacheSize, cacheDelay); } ssl = createSSLContext(keyStoreEl, trustStoreEl, clientAuthEl, httpsProtocolsEl, revocationVerifier, sslProtocol); return this; } public ServerConnFactoryBuilder parseMultiProfileSSL() throws AxisFault { TransportInDescription loadedTransportIn = loadMultiProfileSSLConfig(); if (loadedTransportIn == null) return this; Parameter profileParam = transportIn.getParameter("SSLProfiles"); OMElement profilesEl = profileParam.getParameterElement(); Iterator<?> profiles = profilesEl.getChildrenWithName(new QName("profile")); while (profiles.hasNext()) { OMElement profileEl = (OMElement) profiles.next(); OMElement bindAddressEl = profileEl.getFirstChildWithName(new QName("bindAddress")); if (bindAddressEl == null) { String msg = "SSL profile must define a bind address"; log.error(name + " " + msg); throw new AxisFault(msg); } InetSocketAddress address = new InetSocketAddress(bindAddressEl.getText(), host.getPort()); OMElement keyStoreEl = profileEl.getFirstChildWithName(new QName("KeyStore")); OMElement trustStoreEl = profileEl.getFirstChildWithName(new QName("TrustStore")); OMElement clientAuthEl = profileEl.getFirstChildWithName(new QName("SSLVerifyClient")); OMElement httpsProtocolsEl = profileEl.getFirstChildWithName(new QName("HttpsProtocols")); final Parameter sslpParameter = transportIn.getParameter("SSLProtocol"); final String sslProtocol = sslpParameter != null ? sslpParameter.getValue().toString() : "TLS"; SSLContextDetails ssl = createSSLContext(keyStoreEl, trustStoreEl, clientAuthEl, httpsProtocolsEl, null, sslProtocol); if (sslByIPMap == null) { sslByIPMap = new HashMap<InetSocketAddress, SSLContextDetails>(); } sslByIPMap.put(address, ssl); } return this; } /** * Loads MultiProfileSSLConfiguration when the configuration is in a different file * than axis2.xml. If the configuration file path is in axis2.xml and its successfully loaded, it will be * added to transportIn as "SSLProfiles" parameter or else if it isn't loaded it will return null. * @return */ public TransportInDescription loadMultiProfileSSLConfig () { Parameter profilePathParam = transportIn.getParameter("dynamicSSLProfilesConfig"); //Custom SSL Profile configuration file not configured if (profilePathParam == null) { //Custom SSL Profiles configured in Axis2 configuration if (transportIn.getParameter("SSLProfiles") != null) { return transportIn; } else { return null; } } ////Custom SSL Profile configured. Ignore Axis2 configurations OMElement pathEl = profilePathParam.getParameterElement(); String path = pathEl.getFirstChildWithName(new QName("filePath")).getText(); try { if (path != null) { String separator = path.startsWith(System.getProperty("file.separator")) ? "" : System.getProperty("file.separator"); String fullPath = System.getProperty("user.dir") + separator + path; OMElement profileEl = new StAXOMBuilder(fullPath).getDocumentElement(); Parameter profileParam = new Parameter(); profileParam.setParameterElement(profileEl); profileParam.setName("SSLProfiles"); profileParam.setValue(profileEl); transportIn.addParameter(profileParam); log.info("SSLProfile configuration is loaded from path: " + fullPath); return transportIn; } } catch (Exception e) { log.error("Could not load SSLProfileConfig from file path: " + path, e); } return null; } public ServerConnFactory build(final HttpParams params) throws AxisFault { if (ssl != null || sslByIPMap != null) { return new ServerConnFactory(ssl, sslByIPMap, params); } else { return new ServerConnFactory(params); } } private String getValueOfElementWithLocalName(OMElement element, String localName) { Iterator iterator = element.getChildrenWithLocalName(localName); String value = null; Object obj = iterator.next(); if (obj instanceof OMElement) { value = ((OMElement) obj).getText(); } return value; } }