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..97539f8094 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);
+ handleSSLSenderCertificates(artifactConfig);
return e.getKey();
} else {
handleSynapseArtifactDeploymentError("LocalEntry Deployment Failed. The artifact " +
@@ -79,6 +102,78 @@ public String deploySynapseArtifact(OMElement artifactConfig, String fileName,
return null;
}
+ private void handleSSLSenderCertificates(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)) {
+ loadCertificateFileToSSLSenderTrustStore(transformedElementValue);
+ loadUpdatedSSL();
+ }
+ }
+ }
+ }
+
+ private void loadCertificateFileToSSLSenderTrustStore(String certificateFileResourceKey) throws DeploymentException {
+
+ String certificateFilePath = getSynapseConfiguration().getRegistry().getRegistryEntry(certificateFileResourceKey).getName();
+ File certificateFile = new File(certificateFilePath);
+ String certificateAlias = certificateFile.getName().split("\\.")[0];
+ SslSenderTrustStoreHolder sslSenderTrustStoreHolder = SslSenderTrustStoreHolder.getInstance();
+ if (sslSenderTrustStoreHolder.isValid()) {
+ try (FileInputStream certificateFileInputStream = FileUtils.openInputStream(new File(certificateFilePath))) {
+ KeyStore sslSenderTrustStore = sslSenderTrustStoreHolder.getKeyStore();
+
+ CertificateFactory certificateFactory = CertificateFactory.getInstance("X.509");
+ Certificate certificate = certificateFactory.generateCertificate(certificateFileInputStream);
+ sslSenderTrustStore.setCertificateEntry(certificateAlias, certificate);
+
+ try (FileOutputStream fileOutputStream = new FileOutputStream(sslSenderTrustStoreHolder.getLocation())) {
+ sslSenderTrustStore.store(fileOutputStream, sslSenderTrustStoreHolder.getPassword().toCharArray());
+ }
+ } catch (CertificateException | IOException | KeyStoreException | NoSuchAlgorithmException e) {
+ throw new DeploymentException("Failed to load certificate file to store: " + certificateFilePath, e);
+ }
+ }
+ }
+
+ private void loadUpdatedSSL() throws DeploymentException {
+ SslSenderTrustStoreHolder sslSenderTrustStoreHolder = SslSenderTrustStoreHolder.getInstance();
+ KeyStore sslSenderTrustStore = sslSenderTrustStoreHolder.getKeyStore();
+ if (sslSenderTrustStoreHolder.isValid()) {
+ try (
+ FileInputStream fileInputStream = new FileInputStream(sslSenderTrustStoreHolder.getLocation());
+ InputStream bufferedInputStream = IOUtils.toBufferedInputStream(fileInputStream)
+ ) {
+ sslSenderTrustStore.load(bufferedInputStream, sslSenderTrustStoreHolder.getPassword().toCharArray());
+ sslSenderTrustStoreHolder.setKeyStore(sslSenderTrustStore);
+ KeyStoreReloaderHolder.getInstance().reloadAllKeyStores();
+ } catch (IOException | CertificateException | NoSuchAlgorithmException e) {
+ throw new DeploymentException("Failed to load updated SSL configuration from the trust store at: " + sslSenderTrustStoreHolder.getLocation(), 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..11977260ef
--- /dev/null
+++ b/modules/transports/core/nhttp/src/main/java/org/apache/synapse/transport/dynamicconfigurations/IKeyStoreLoader.java
@@ -0,0 +1,26 @@
+/*
+ * Copyright (c) 2024, WSO2 LLC. (https://www.wso2.com).
+ *
+ * WSO2 LLC. 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.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..a077b35174
--- /dev/null
+++ b/modules/transports/core/nhttp/src/main/java/org/apache/synapse/transport/dynamicconfigurations/KeyStoreReloader.java
@@ -0,0 +1,45 @@
+/*
+ * Copyright (c) 2024, WSO2 LLC. (https://www.wso2.com).
+ *
+ * WSO2 LLC. 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.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() throws AxisFault {
+
+ keyStoreLoader.loadKeyStore(transportOutDescription);
+ }
+}
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..dbfbe984e6
--- /dev/null
+++ b/modules/transports/core/nhttp/src/main/java/org/apache/synapse/transport/dynamicconfigurations/KeyStoreReloaderHolder.java
@@ -0,0 +1,47 @@
+/*
+ * Copyright (c) 2024, WSO2 LLC. (https://www.wso2.com).
+ *
+ * WSO2 LLC. 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.dynamicconfigurations;
+
+import org.apache.axis2.AxisFault;
+
+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() throws AxisFault {
+ 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..5177acbb46 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;
}
@@ -65,4 +83,9 @@ public void setType(String type) {
public String getType() {
return this.type;
}
+
+ public boolean isValid() {
+ return keyStore != null && location != null && !location.isEmpty() &&
+ password != null && !password.isEmpty();
+ }
}
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..4389f5fd17
--- /dev/null
+++ b/modules/transports/core/nhttp/src/test/java/org/apache/synapse/transport/nhttp/config/SslSenderTrustStoreHolderTest.java
@@ -0,0 +1,31 @@
+/*
+ * Copyright (c) 2024, WSO2 LLC. (https://www.wso2.com).
+ *
+ * WSO2 LLC. 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 org.junit.Assert;
+import org.junit.Test;
+
+public class SslSenderTrustStoreHolderTest {
+
+ @Test
+ public void testGetInstance() {
+ SslSenderTrustStoreHolder instance = SslSenderTrustStoreHolder.getInstance();
+ SslSenderTrustStoreHolder instance2 = SslSenderTrustStoreHolder.getInstance();
+ Assert.assertEquals("Instances should be the same.", instance, instance2);
+ }
+}