diff --git a/modules/core/src/main/java/org/apache/synapse/deployers/LocalEntryDeployer.java b/modules/core/src/main/java/org/apache/synapse/deployers/LocalEntryDeployer.java index ab6df62101..f7d652a5db 100644 --- a/modules/core/src/main/java/org/apache/synapse/deployers/LocalEntryDeployer.java +++ b/modules/core/src/main/java/org/apache/synapse/deployers/LocalEntryDeployer.java @@ -21,16 +21,34 @@ import org.apache.axiom.om.OMElement; import org.apache.axis2.deployment.DeploymentException; +import org.apache.commons.io.IOUtils; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; +import org.apache.commons.io.FileUtils; +import org.apache.synapse.SynapseConstants; import org.apache.synapse.config.Entry; import org.apache.synapse.config.xml.EntryFactory; import org.apache.synapse.config.xml.EntrySerializer; import org.apache.synapse.config.xml.MultiXMLConfigurationBuilder; +import org.apache.synapse.transport.dynamicconfigurations.KeyStoreReloaderHolder; +import org.apache.synapse.transport.nhttp.config.SslSenderTrustStoreHolder; import java.io.File; +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.security.KeyStore; +import java.security.KeyStoreException; +import java.security.NoSuchAlgorithmException; +import java.security.cert.Certificate; +import java.security.cert.CertificateException; +import java.security.cert.CertificateFactory; +import java.util.Iterator; import java.util.Properties; +import javax.xml.namespace.QName; + /** * Handles the LocalEntry deployment and undeployment tasks * @@ -39,6 +57,10 @@ public class LocalEntryDeployer extends AbstractSynapseArtifactDeployer { private static Log log = LogFactory.getLog(LocalEntryDeployer.class); + private static final String RESOURCES_IDENTIFIER = "resources:"; + private static final String CONVERTED_RESOURCES_IDENTIFIER = "gov:mi-resources" + File.separator; + private static final String HTTP_CONNECTION_IDENTIFIER = "http.init"; + private static final String CERTIFICATE_EXTENSION = ".crt"; @Override public String deploySynapseArtifact(OMElement artifactConfig, String fileName, @@ -66,6 +88,7 @@ public String deploySynapseArtifact(OMElement artifactConfig, String fileName, } log.info("LocalEntry named '" + e.getKey() + "' has been deployed from file : " + fileName); + handleHttpConnectorCertificates(artifactConfig); return e.getKey(); } else { handleSynapseArtifactDeploymentError("LocalEntry Deployment Failed. The artifact " + @@ -79,6 +102,66 @@ public String deploySynapseArtifact(OMElement artifactConfig, String fileName, return null; } + private void handleHttpConnectorCertificates(OMElement element) throws DeploymentException { + + OMElement httpInitElement = + element.getFirstChildWithName(new QName(SynapseConstants.SYNAPSE_NAMESPACE, HTTP_CONNECTION_IDENTIFIER)); + if (httpInitElement != null) { + Iterator childElementIterator = httpInitElement.getChildElements(); + while (childElementIterator.hasNext()) { + OMElement childElement = (OMElement) childElementIterator.next(); + String childElementValue = childElement.getText(); + String transformedElementValue = getTransformedElementValue(childElementValue); + if (transformedElementValue.endsWith(CERTIFICATE_EXTENSION)) { + loadCertificateFileToStore(transformedElementValue); + } + } + } + } + + private void loadCertificateFileToStore(String certificateFileResourceKey) throws DeploymentException { + + String certificateFilePath = getSynapseConfiguration().getRegistry().getRegistryEntry(certificateFileResourceKey).getName(); + File certificateFile = new File(certificateFilePath); + String certificateAlias = certificateFile.getName().split("\\.")[0]; + try { + FileInputStream certificateFileInputStream = FileUtils.openInputStream(new File(certificateFilePath)); + SslSenderTrustStoreHolder sslSenderTrustStoreHolder = SslSenderTrustStoreHolder.getInstance(); + KeyStore sslSenderTrustStore = sslSenderTrustStoreHolder.getKeyStore(); + CertificateFactory certificateFactory = CertificateFactory.getInstance("X.509"); + Certificate certificate = certificateFactory.generateCertificate(certificateFileInputStream); + sslSenderTrustStore.setCertificateEntry(certificateAlias, certificate); + + FileOutputStream fileOutputStream = new FileOutputStream(sslSenderTrustStoreHolder.getLocation()); + sslSenderTrustStore.store(fileOutputStream, sslSenderTrustStoreHolder.getPassword().toCharArray()); + + FileInputStream fileInputStream = new FileInputStream(sslSenderTrustStoreHolder.getLocation()); + InputStream dest = IOUtils.toBufferedInputStream(fileInputStream); + fileInputStream.close(); + sslSenderTrustStore.load(dest, sslSenderTrustStoreHolder.getPassword().toCharArray()); + dest.close(); + + sslSenderTrustStoreHolder.setKeyStore(sslSenderTrustStore); + KeyStoreReloaderHolder.getInstance().reloadAllKeyStores(); + } catch (CertificateException | IOException | KeyStoreException | NoSuchAlgorithmException e) { + throw new RuntimeException(e); + } + } + + /** + * Transforms the given element value if it indicates a resource file. + * + * @param elementValue the value of the element to be transformed + * @return the transformed element value + */ + private String getTransformedElementValue(String elementValue) { + String transformedElementValue = elementValue.trim(); + if (transformedElementValue.startsWith(RESOURCES_IDENTIFIER)) { + transformedElementValue = transformedElementValue.replace(RESOURCES_IDENTIFIER, CONVERTED_RESOURCES_IDENTIFIER); + } + return transformedElementValue; + } + @Override public String updateSynapseArtifact(OMElement artifactConfig, String fileName, String existingArtifactName, Properties properties) { diff --git a/modules/transports/core/nhttp/src/main/java/org/apache/synapse/transport/dynamicconfigurations/IKeyStoreLoader.java b/modules/transports/core/nhttp/src/main/java/org/apache/synapse/transport/dynamicconfigurations/IKeyStoreLoader.java new file mode 100644 index 0000000000..6f8d8c0220 --- /dev/null +++ b/modules/transports/core/nhttp/src/main/java/org/apache/synapse/transport/dynamicconfigurations/IKeyStoreLoader.java @@ -0,0 +1,9 @@ +package org.apache.synapse.transport.dynamicconfigurations; + +import org.apache.axis2.AxisFault; +import org.apache.axis2.description.ParameterInclude; + +public interface IKeyStoreLoader { + + void loadKeyStore(ParameterInclude transport) throws AxisFault; +} diff --git a/modules/transports/core/nhttp/src/main/java/org/apache/synapse/transport/dynamicconfigurations/KeyStoreReloader.java b/modules/transports/core/nhttp/src/main/java/org/apache/synapse/transport/dynamicconfigurations/KeyStoreReloader.java new file mode 100644 index 0000000000..e907f9d761 --- /dev/null +++ b/modules/transports/core/nhttp/src/main/java/org/apache/synapse/transport/dynamicconfigurations/KeyStoreReloader.java @@ -0,0 +1,32 @@ +package org.apache.synapse.transport.dynamicconfigurations; + +import org.apache.axis2.AxisFault; +import org.apache.axis2.description.ParameterInclude; + +public class KeyStoreReloader { + + private IKeyStoreLoader keyStoreLoader; + private ParameterInclude transportOutDescription; + + public KeyStoreReloader(IKeyStoreLoader keyStoreLoader, ParameterInclude transportOutDescription) { + + this.keyStoreLoader = keyStoreLoader; + this.transportOutDescription = transportOutDescription; + + registerListener(transportOutDescription); + } + + private void registerListener(ParameterInclude transportOutDescription) { + + KeyStoreReloaderHolder.getInstance().addKeyStoreLoader(this); + } + + public void update() { + + try { + keyStoreLoader.loadKeyStore(transportOutDescription); + } catch (AxisFault e) { + throw new RuntimeException(e); + } + } +} diff --git a/modules/transports/core/nhttp/src/main/java/org/apache/synapse/transport/dynamicconfigurations/KeyStoreReloaderHolder.java b/modules/transports/core/nhttp/src/main/java/org/apache/synapse/transport/dynamicconfigurations/KeyStoreReloaderHolder.java new file mode 100644 index 0000000000..9e7b19e3db --- /dev/null +++ b/modules/transports/core/nhttp/src/main/java/org/apache/synapse/transport/dynamicconfigurations/KeyStoreReloaderHolder.java @@ -0,0 +1,28 @@ +package org.apache.synapse.transport.dynamicconfigurations; + +import java.util.ArrayList; +import java.util.List; + +public class KeyStoreReloaderHolder { + + private static KeyStoreReloaderHolder instance = new KeyStoreReloaderHolder(); + private List keyStoreLoaders; + + private KeyStoreReloaderHolder() { + keyStoreLoaders = new ArrayList<>(); + } + + public static KeyStoreReloaderHolder getInstance() { + return instance; + } + + public void addKeyStoreLoader(KeyStoreReloader keyStoreLoader) { + keyStoreLoaders.add(keyStoreLoader); + } + + public void reloadAllKeyStores() { + for (KeyStoreReloader keyStoreLoader : keyStoreLoaders) { + keyStoreLoader.update(); + } + } +} diff --git a/modules/transports/core/nhttp/src/main/java/org/apache/synapse/transport/nhttp/config/ClientConnFactoryBuilder.java b/modules/transports/core/nhttp/src/main/java/org/apache/synapse/transport/nhttp/config/ClientConnFactoryBuilder.java index 66fde8487c..d85def1532 100644 --- a/modules/transports/core/nhttp/src/main/java/org/apache/synapse/transport/nhttp/config/ClientConnFactoryBuilder.java +++ b/modules/transports/core/nhttp/src/main/java/org/apache/synapse/transport/nhttp/config/ClientConnFactoryBuilder.java @@ -370,14 +370,18 @@ private SSLContext createSSLContext(OMElement keyStoreElt, OMElement trustStoreE if (log.isDebugEnabled()) { log.debug(name + " Loading Trust Keystore from : " + location); } - SslSenderTrustStoreHolder.getInstance().setLocation(location); - SslSenderTrustStoreHolder.getInstance().setPassword(passwordElement.getText()); - SslSenderTrustStoreHolder.getInstance().setType(type); + trustStore.load(fis, storePassword.toCharArray()); TrustManagerFactory trustManagerfactory = TrustManagerFactory.getInstance( TrustManagerFactory.getDefaultAlgorithm()); trustManagerfactory.init(trustStore); trustManagers = trustManagerfactory.getTrustManagers(); + + SslSenderTrustStoreHolder sslSenderTrustStoreHolder = SslSenderTrustStoreHolder.getInstance(); + sslSenderTrustStoreHolder.setKeyStore(trustStore); + sslSenderTrustStoreHolder.setLocation(location); + sslSenderTrustStoreHolder.setPassword(storePassword); + SslSenderTrustStoreHolder.getInstance().setType(type); } catch (GeneralSecurityException gse) { log.error(name + " Error loading Key store : " + location, gse); throw new AxisFault("Error loading Key store : " + location, gse); @@ -472,6 +476,11 @@ private SSLContext createSSLContext(OMElement keyStoreElt, OMElement trustStoreE trustManagerfactory.init(trustStore); trustManagers = trustManagerfactory.getTrustManagers(); + SslSenderTrustStoreHolder sslSenderTrustStoreHolder = SslSenderTrustStoreHolder.getInstance(); + sslSenderTrustStoreHolder.setKeyStore(trustStore); + sslSenderTrustStoreHolder.setLocation(location); + sslSenderTrustStoreHolder.setPassword(storePassword); + } catch (GeneralSecurityException gse) { log.error(name + " Error loading Key store : " + location, gse); throw new AxisFault("Error loading Key store : " + location, gse); diff --git a/modules/transports/core/nhttp/src/main/java/org/apache/synapse/transport/nhttp/config/SslSenderTrustStoreHolder.java b/modules/transports/core/nhttp/src/main/java/org/apache/synapse/transport/nhttp/config/SslSenderTrustStoreHolder.java index ae1a4dcb6d..cb7ef8884c 100644 --- a/modules/transports/core/nhttp/src/main/java/org/apache/synapse/transport/nhttp/config/SslSenderTrustStoreHolder.java +++ b/modules/transports/core/nhttp/src/main/java/org/apache/synapse/transport/nhttp/config/SslSenderTrustStoreHolder.java @@ -17,6 +17,8 @@ */ package org.apache.synapse.transport.nhttp.config; +import java.security.KeyStore; + /** * The SSL Sender TrustStore Holder class to store the client trust store's configurable details. */ @@ -26,6 +28,7 @@ public class SslSenderTrustStoreHolder { private SslSenderTrustStoreHolder() {} + private KeyStore keyStore; private String location; private String password; private String type; @@ -33,7 +36,7 @@ private SslSenderTrustStoreHolder() {} public static SslSenderTrustStoreHolder getInstance() { if (instance == null) { - synchronized (TrustStoreHolder.class) { + synchronized (SslSenderTrustStoreHolder.class) { if (instance == null) { instance = new SslSenderTrustStoreHolder(); } @@ -42,6 +45,21 @@ public static SslSenderTrustStoreHolder getInstance() { return instance; } + public static void resetInstance() { + + instance = null; + } + + public KeyStore getKeyStore() { + + return keyStore; + } + + public void setKeyStore(KeyStore keyStore) { + + this.keyStore = keyStore; + } + public void setLocation(String location) { this.location = location; } diff --git a/modules/transports/core/nhttp/src/main/java/org/apache/synapse/transport/passthru/PassThroughHttpSSLSender.java b/modules/transports/core/nhttp/src/main/java/org/apache/synapse/transport/passthru/PassThroughHttpSSLSender.java index 5fe55383d0..f10541807c 100644 --- a/modules/transports/core/nhttp/src/main/java/org/apache/synapse/transport/passthru/PassThroughHttpSSLSender.java +++ b/modules/transports/core/nhttp/src/main/java/org/apache/synapse/transport/passthru/PassThroughHttpSSLSender.java @@ -21,18 +21,21 @@ import org.apache.axis2.description.ParameterInclude; import org.apache.axis2.description.TransportOutDescription; import org.apache.synapse.transport.certificatevalidation.cache.CertCache; +import org.apache.synapse.transport.dynamicconfigurations.IKeyStoreLoader; +import org.apache.synapse.transport.dynamicconfigurations.KeyStoreReloader; import org.apache.synapse.transport.dynamicconfigurations.SSLProfileLoader; import org.apache.synapse.transport.dynamicconfigurations.SenderProfileReloader; import org.apache.synapse.transport.http.conn.Scheme; import org.apache.synapse.transport.nhttp.config.ClientConnFactoryBuilder; import org.apache.synapse.transport.nhttp.config.TrustStoreHolder; -public class PassThroughHttpSSLSender extends PassThroughHttpSender implements SSLProfileLoader { +public class PassThroughHttpSSLSender extends PassThroughHttpSender implements SSLProfileLoader, IKeyStoreLoader { @Override public void init(ConfigurationContext configurationContext, TransportOutDescription transportOutDescription) throws AxisFault { super.init(configurationContext, transportOutDescription); + new KeyStoreReloader(this, transportOutDescription); new SenderProfileReloader(this, transportOutDescription); } @@ -60,4 +63,10 @@ public void reloadConfig(ParameterInclude transport) throws AxisFault { reloadDynamicSSLConfig((TransportOutDescription) transport); } + @Override + public void loadKeyStore(ParameterInclude transport) throws AxisFault { + CertCache.resetCache(); + TrustStoreHolder.resetInstance(); + reloadSSL((TransportOutDescription) transport); + } } diff --git a/modules/transports/core/nhttp/src/main/java/org/apache/synapse/transport/passthru/PassThroughHttpSender.java b/modules/transports/core/nhttp/src/main/java/org/apache/synapse/transport/passthru/PassThroughHttpSender.java index 3af961c03f..ccb24756c1 100644 --- a/modules/transports/core/nhttp/src/main/java/org/apache/synapse/transport/passthru/PassThroughHttpSender.java +++ b/modules/transports/core/nhttp/src/main/java/org/apache/synapse/transport/passthru/PassThroughHttpSender.java @@ -722,6 +722,18 @@ public void reloadDynamicSSLConfig(TransportOutDescription transport) throws Axi } } + public void reloadSSL(TransportOutDescription transport) throws AxisFault { + log.info("PassThroughHttpSender SSL Config.."); + ClientConnFactoryBuilder connFactoryBuilder = + initConnFactoryBuilder(transport, this.configurationContext).parseSSL(); + connFactory = connFactoryBuilder.createConnFactory(targetConfiguration.getHttpParams()); + + handler.setConnFactory(connFactory); + ioEventDispatch.setConnFactory(connFactory); + + log.info("Pass-through " + namePrefix + " Sender updated with SSL Configuration Updates ..."); + } + /** * Set content type headers along with the charactor encoding if content type header is not preserved * @param msgContext message context diff --git a/modules/transports/core/nhttp/src/test/java/org/apache/synapse/transport/nhttp/config/SslSenderTrustStoreHolderTest.java b/modules/transports/core/nhttp/src/test/java/org/apache/synapse/transport/nhttp/config/SslSenderTrustStoreHolderTest.java new file mode 100644 index 0000000000..e220083b19 --- /dev/null +++ b/modules/transports/core/nhttp/src/test/java/org/apache/synapse/transport/nhttp/config/SslSenderTrustStoreHolderTest.java @@ -0,0 +1,14 @@ +package org.apache.synapse.transport.nhttp.config; + +import org.junit.Assert; +import org.junit.Test; + +public class SslSenderTrustStoreHolderTest { + + @Test + public void testGetInstance() { + SslSenderTrustStoreHolder instance = SslSenderTrustStoreHolder.getInstance(); + SslSenderTrustStoreHolder instance2 = SslSenderTrustStoreHolder.getInstance(); + Assert.assertEquals(instance, instance2); + } +}