Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Handle certificates mentioned in HTTP connection during local entry deployment. #2243

Merged
Original file line number Diff line number Diff line change
Expand Up @@ -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 <code>LocalEntry</code> deployment and undeployment tasks
*
Expand All @@ -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,
Expand Down Expand Up @@ -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 " +
Expand All @@ -79,6 +102,66 @@ public String deploySynapseArtifact(OMElement artifactConfig, String fileName,
return null;
}

private void handleHttpConnectorCertificates(OMElement element) throws DeploymentException {
chathuranga-jayanath-99 marked this conversation as resolved.
Show resolved Hide resolved

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));
chathuranga-jayanath-99 marked this conversation as resolved.
Show resolved Hide resolved
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());
chathuranga-jayanath-99 marked this conversation as resolved.
Show resolved Hide resolved
dest.close();

sslSenderTrustStoreHolder.setKeyStore(sslSenderTrustStore);
KeyStoreReloaderHolder.getInstance().reloadAllKeyStores();
} catch (CertificateException | IOException | KeyStoreException | NoSuchAlgorithmException e) {
throw new RuntimeException(e);
chathuranga-jayanath-99 marked this conversation as resolved.
Show resolved Hide resolved
}
}

/**
* 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) {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
package org.apache.synapse.transport.dynamicconfigurations;
chathuranga-jayanath-99 marked this conversation as resolved.
Show resolved Hide resolved

import org.apache.axis2.AxisFault;
import org.apache.axis2.description.ParameterInclude;

public interface IKeyStoreLoader {

void loadKeyStore(ParameterInclude transport) throws AxisFault;
}
Original file line number Diff line number Diff line change
@@ -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);
chathuranga-jayanath-99 marked this conversation as resolved.
Show resolved Hide resolved
}
}
}
Original file line number Diff line number Diff line change
@@ -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<KeyStoreReloader> 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();
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down Expand Up @@ -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);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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.
*/
Expand All @@ -26,14 +28,15 @@ public class SslSenderTrustStoreHolder {

private SslSenderTrustStoreHolder() {}

private KeyStore keyStore;
private String location;
private String password;
private String type;

public static SslSenderTrustStoreHolder getInstance() {

if (instance == null) {
synchronized (TrustStoreHolder.class) {
synchronized (SslSenderTrustStoreHolder.class) {
if (instance == null) {
instance = new SslSenderTrustStoreHolder();
}
Expand All @@ -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;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}

Expand Down Expand Up @@ -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);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
Original file line number Diff line number Diff line change
@@ -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() {
chathuranga-jayanath-99 marked this conversation as resolved.
Show resolved Hide resolved
SslSenderTrustStoreHolder instance = SslSenderTrustStoreHolder.getInstance();
SslSenderTrustStoreHolder instance2 = SslSenderTrustStoreHolder.getInstance();
Assert.assertEquals(instance, instance2);
chathuranga-jayanath-99 marked this conversation as resolved.
Show resolved Hide resolved
}
}
Loading