Skip to content

Commit

Permalink
wip
Browse files Browse the repository at this point in the history
  • Loading branch information
006627 committed Jan 26, 2024
1 parent 891bb64 commit 2a94e14
Show file tree
Hide file tree
Showing 7 changed files with 215 additions and 12 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -739,6 +739,17 @@ public enum DistributedModeType {

// Plugins certificates
public static final String PLUGINS_CERTIFICATE_OPT_IN_PROPERTY = "core.plugins.external.certificates.opt-in";
public static final String PLUGINS_CERTIFICATE_DEFAULT_TRUSTSTORE_AUTH_TYPE = "RSA";
public static final String PLUGINS_CERTIFICATE_DEFAULT_TRUSTSTORE_PATH = "/config/market/truststore/";

public static final String PLUGINS_CERTIFICATE_RODA_TRUSTSTORE_TYPE = "PKCS12";
public static final String PLUGINS_CERTIFICATE_RODA_TRUSTSTORE_NAME = "roda-truststore.p12";
public static final String PLUGINS_CERTIFICATE_RODA_TRUSTSTORE_PASS = "changeit";

public static final String PLUGINS_CERTIFICATE_CUSTOM_TRUSTSTORE_ENABLE_PROPERTY = "core.plugins.external.certificates.custom.truststore.enable";
public static final String PLUGINS_CERTIFICATE_CUSTOM_TRUSTSTORE_TYPE_PROPERTY = "core.plugins.external.certificates.custom.truststore.type";
public static final String PLUGINS_CERTIFICATE_CUSTOM_TRUSTSTORE_NAME_PROPERTY = "core.plugins.external.certificates.custom.truststore.name";
public static final String PLUGINS_CERTIFICATE_CUSTOM_TRUSTSTORE_PASS_PROPERTY = "core.plugins.external.certificates.custom.truststore.pass";

// MARKET
public static final String MARKET_INFO_URL_PROPERTY = "core.market.info.url";
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,8 @@
import org.roda.core.data.v2.jobs.Report;
import org.roda.core.data.v2.risks.IndexedRisk;
import org.roda.core.data.v2.risks.Risk;
import org.roda.core.plugins.certificate.PluginCertificateException;
import org.roda.core.plugins.certificate.PluginCertificateUtils;
import org.roda.core.storage.fs.FSUtils;
import org.roda.core.util.ClassLoaderUtility;
import org.roda.core.util.CompoundClassLoader;
Expand Down Expand Up @@ -691,11 +693,17 @@ private void loadPlugin(PluginLoadInfo p) {
LOGGER.error("Plugin failed to initialize: {}", p.jarPath, e);
}
}
if (!certificateInfo.isNotVerified() || optIn) {
// Let's cache Plugin classloader
jarPluginClassloaderCache.put(getPluginClassLoaderCacheKey(p.jarPath), classloader);
}

if (certificateInfo.isNotVerified() && !optIn) {
LOGGER.error("Plugin '{}' will not be activated due to certificate validation failures",
p.jarPath.getFileName());
return;
} else if (certificateInfo.isNotVerified() && optIn) {
LOGGER.warn("OptIn is enabled. The plugin '{}' will be activated despite certificate validation failures",
p.jarPath.getFileName());
}
// Let's cache Plugin classloader
jarPluginClassloaderCache.put(getPluginClassLoaderCacheKey(p.jarPath), classloader);
}
} catch (IOException e1) {
LOGGER.error("Plugin failed to initialize: {}", p.jarPath, e1);
Expand All @@ -720,6 +728,7 @@ private CertificateInfo loadAndCheckCertificates(Path jarPath) throws IOExceptio
while (is.read(buffer) != -1)
;
Certificate[] certificates = entry.getCertificates();

CodeSigner[] signers = entry.getCodeSigners();

if (signers != null && certificates != null) {
Expand All @@ -734,15 +743,16 @@ private CertificateInfo loadAndCheckCertificates(Path jarPath) throws IOExceptio

certificateInfo.addCertificates(issuerDN, subjectDN, notBefore, notAfter);
try {
validateCertificate(x509Certificates);
PluginCertificateUtils.validate(x509Certificates);
// validateCertificate(x509Certificates);
certificateInfo.setCertificateStatus(CertificateInfo.CertificateStatus.VERIFIED);
} catch (NoSuchAlgorithmException | CertificateException | KeyStoreException e) {
LOGGER.error("Plugin not signed by RODA");
} catch (NoSuchAlgorithmException | CertificateException | KeyStoreException | PluginCertificateException e) {
LOGGER.error(e.getMessage(), e);
certificateInfo.setCertificateStatus(CertificateInfo.CertificateStatus.NOT_VERIFIED);
break;
}
} else {
LOGGER.error("Plugin not signed by RODA");
LOGGER.error("Plugin '{}' does not contain certificates", jarPath.getFileName());
certificateInfo.setCertificateStatus(CertificateInfo.CertificateStatus.NOT_VERIFIED);
break;
}
Expand All @@ -753,13 +763,14 @@ private CertificateInfo loadAndCheckCertificates(Path jarPath) throws IOExceptio

private static void validateCertificate(X509Certificate[] certificates)
throws NoSuchAlgorithmException, KeyStoreException, CertificateException, IOException {
TrustManagerFactory trustManagerFactory = TrustManagerFactory
.getInstance(TrustManagerFactory.getDefaultAlgorithm());
KeyStore keyStore = KeyStore.getInstance("PKCS12");

// default truststore
keyStore.load(PluginManager.class.getResourceAsStream("/config/market/roda-truststore.p12"),
KeyStore keyStore = KeyStore.getInstance("PKCS12");
keyStore.load(PluginManager.class.getResourceAsStream("/config/market/truststore/roda-truststore.p12"),
"changeit".toCharArray());

TrustManagerFactory trustManagerFactory = TrustManagerFactory
.getInstance(TrustManagerFactory.getDefaultAlgorithm());
trustManagerFactory.init(keyStore);

for (TrustManager trustManager : trustManagerFactory.getTrustManagers()) {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
package org.roda.core.plugins.certificate;

import java.security.cert.Certificate;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;

import javax.net.ssl.X509TrustManager;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
* @author Gabriel Barros <[email protected]>
*/
public class CompositeX509TrustManager implements X509TrustManager {
private static final Logger LOGGER = LoggerFactory.getLogger(CompositeX509TrustManager.class);
private final List<X509TrustManager> trustManagers;

public CompositeX509TrustManager(List<X509TrustManager> trustManagers) {
this.trustManagers = trustManagers;
}

@Override
public void checkClientTrusted(X509Certificate[] x509Certificates, String authType) throws CertificateException {
boolean isTrusted = trustManagers.stream().anyMatch(trustManager -> {
try {
trustManager.checkClientTrusted(x509Certificates, authType);
return true;
} catch (CertificateException e) {
LOGGER.debug("Unable to trust the client certificates "
+ Arrays.stream(x509Certificates).map(Certificate::toString).collect(Collectors.toSet()));
return false;
}
});

if (!isTrusted) {
throw new CertificateException("None of the TrustManagers can trust this client certificate chain");
}
}

@Override
public void checkServerTrusted(X509Certificate[] x509Certificates, String authType) throws CertificateException {
boolean isTrusted = trustManagers.stream().anyMatch(trustManager -> {
try {
trustManager.checkServerTrusted(x509Certificates, authType);
return true;
} catch (CertificateException e) {
LOGGER.debug("Unable to trust the server certificates "
+ Arrays.stream(x509Certificates).map(Certificate::toString).collect(Collectors.toSet()));
return false;
}
});

if (!isTrusted) {
throw new CertificateException("None of the TrustManagers can trust this client certificate chain");
}
}

@Override
public X509Certificate[] getAcceptedIssuers() {
ArrayList<X509Certificate> x509Certificates = new ArrayList<>(trustManagers.size());
for (X509TrustManager trustManager : trustManagers) {
x509Certificates.addAll(Arrays.asList(trustManager.getAcceptedIssuers()));
}
return x509Certificates.toArray(X509Certificate[]::new);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
package org.roda.core.plugins.certificate;

import org.roda.core.data.exceptions.RODAException;

/**
* @author Gabriel Barros <[email protected]>
*/
public class PluginCertificateException extends RODAException {

public PluginCertificateException() {
super();
}

public PluginCertificateException(String message) {
super(message);
}

public PluginCertificateException(String message, Throwable cause) {
super(message, cause);
}

public PluginCertificateException(Throwable cause) {
super(cause);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
package org.roda.core.plugins.certificate;

import java.io.IOException;
import java.security.KeyStore;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;

import javax.net.ssl.TrustManagerFactory;
import javax.net.ssl.X509TrustManager;

import org.roda.core.RodaCoreFactory;
import org.roda.core.data.common.RodaConstants;

/**
* @author Gabriel Barros <[email protected]>
*/
public class PluginCertificateUtils {
public static void validate(X509Certificate[] certificates)
throws KeyStoreException, CertificateException, IOException, NoSuchAlgorithmException, PluginCertificateException {

// RODA truststore
KeyStore rodaTrustStore = KeyStore.getInstance(RodaConstants.PLUGINS_CERTIFICATE_RODA_TRUSTSTORE_TYPE);
String rodaTrustStorePath = RodaConstants.PLUGINS_CERTIFICATE_DEFAULT_TRUSTSTORE_PATH
+ RodaConstants.PLUGINS_CERTIFICATE_RODA_TRUSTSTORE_NAME;
rodaTrustStore.load(PluginCertificateUtils.class.getResourceAsStream(rodaTrustStorePath),
RodaConstants.PLUGINS_CERTIFICATE_RODA_TRUSTSTORE_PASS.toCharArray());

List<X509TrustManager> allTrustManagers = new ArrayList<>(
getTrustManager(rodaTrustStore, TrustManagerFactory.getDefaultAlgorithm()));

// Custom truststore
if (RodaCoreFactory.getProperty(RodaConstants.PLUGINS_CERTIFICATE_CUSTOM_TRUSTSTORE_TYPE_PROPERTY, false)) {
String customTrustStoreType = getMandatoryCertificateProperty(
RodaConstants.PLUGINS_CERTIFICATE_CUSTOM_TRUSTSTORE_TYPE_PROPERTY);
String customName = getMandatoryCertificateProperty(
RodaConstants.PLUGINS_CERTIFICATE_CUSTOM_TRUSTSTORE_NAME_PROPERTY);
String customPass = getMandatoryCertificateProperty(
RodaConstants.PLUGINS_CERTIFICATE_CUSTOM_TRUSTSTORE_PASS_PROPERTY);

String customTrustStorePath = RodaConstants.PLUGINS_CERTIFICATE_DEFAULT_TRUSTSTORE_PATH + customName;
KeyStore customTrustStore = KeyStore.getInstance(customTrustStoreType);
customTrustStore.load(RodaCoreFactory.getConfigurationFileAsStream(customTrustStorePath),
customPass.toCharArray());
allTrustManagers.addAll(getTrustManager(customTrustStore, TrustManagerFactory.getDefaultAlgorithm()));
}

CompositeX509TrustManager compositeX509TrustManager = new CompositeX509TrustManager(allTrustManagers);
compositeX509TrustManager.checkServerTrusted(certificates,
RodaConstants.PLUGINS_CERTIFICATE_DEFAULT_TRUSTSTORE_AUTH_TYPE);
}

private static String getMandatoryCertificateProperty(String propertyKey) throws PluginCertificateException {
String property = RodaCoreFactory.getProperty(propertyKey, "");
if (property.isEmpty()) {
throw new PluginCertificateException("Mandatory property not defined: " + propertyKey);
}
return property;
}

private static List<X509TrustManager> getTrustManager(final KeyStore keyStore, final String algorithm)
throws NoSuchAlgorithmException, KeyStoreException {
TrustManagerFactory trustManagerFactory = TrustManagerFactory.getInstance(algorithm);
trustManagerFactory.init(keyStore);

return Arrays.stream(trustManagerFactory.getTrustManagers()).filter(X509TrustManager.class::isInstance)
.map(X509TrustManager.class::cast).collect(Collectors.toList());
}
}
11 changes: 11 additions & 0 deletions roda-core/roda-core/src/main/resources/config/roda-core.properties
Original file line number Diff line number Diff line change
Expand Up @@ -573,3 +573,14 @@ core.aip.default_permissions.creator.permission[] = GRANT
#
##########################################################################
core.user_registration.disabled = false


##########################################################################
# Plugins certificates setting
#
##########################################################################
# core.plugins.external.certificates.opt-in = false
core.plugins.external.certificates.custom.truststore.enable = true
# core.plugins.external.certificates.custom.truststore.type = PKCS12
# core.plugins.external.certificates.custom.truststore.name = custom-truststore.p12
# core.plugins.external.certificates.custom.truststore.pass = changeit

0 comments on commit 2a94e14

Please sign in to comment.