From cdc8979546128df5b8170f74bb0acd2b31198b1a Mon Sep 17 00:00:00 2001 From: Emanuele Cerilli Date: Tue, 2 Sep 2025 19:23:14 +0200 Subject: [PATCH] Create project for Tomcat password encryption and decryption --- tomcat-password-encryption/pom.xml | 71 ++++++++++++++++++ ...wageTomcatEncryptedPasswordDatasource.java | 56 ++++++++++++++ .../helper/EncryptOnce.java | 31 ++++++++ .../helper/EncryptedPasswordUtils.java | 74 +++++++++++++++++++ 4 files changed, 232 insertions(+) create mode 100644 tomcat-password-encryption/pom.xml create mode 100644 tomcat-password-encryption/src/main/java/it/eng/knowage/tomcatpasswordencryption/KnowageTomcatEncryptedPasswordDatasource.java create mode 100644 tomcat-password-encryption/src/main/java/it/eng/knowage/tomcatpasswordencryption/helper/EncryptOnce.java create mode 100644 tomcat-password-encryption/src/main/java/it/eng/knowage/tomcatpasswordencryption/helper/EncryptedPasswordUtils.java diff --git a/tomcat-password-encryption/pom.xml b/tomcat-password-encryption/pom.xml new file mode 100644 index 00000000000..26f53154277 --- /dev/null +++ b/tomcat-password-encryption/pom.xml @@ -0,0 +1,71 @@ + + 4.0.0 + it.eng.knowage + tomcat-password-encryption + 9.0.0-SNAPSHOT + Archetype - tomcat-password-encryption + http://maven.apache.org + + + UTF-8 + UTF-8 + 17 + + + + + org.apache.tomcat + tomcat-jdbc + 9.0.83 + compile + + + org.jasypt + jasypt + 1.9.3 + + + + + + org.apache.maven.plugins + maven-compiler-plugin + 3.11.0 + + ${maven.compiler.release} + ${project.build.sourceEncoding} + + + + + org.apache.maven.plugins + maven-shade-plugin + 3.5.0 + + + package + + shade + + + true + + + org.jasypt:jasypt + + + + + + org.jasypt + shade.org.jasypt + + + + + + + + + \ No newline at end of file diff --git a/tomcat-password-encryption/src/main/java/it/eng/knowage/tomcatpasswordencryption/KnowageTomcatEncryptedPasswordDatasource.java b/tomcat-password-encryption/src/main/java/it/eng/knowage/tomcatpasswordencryption/KnowageTomcatEncryptedPasswordDatasource.java new file mode 100644 index 00000000000..406834b5b7a --- /dev/null +++ b/tomcat-password-encryption/src/main/java/it/eng/knowage/tomcatpasswordencryption/KnowageTomcatEncryptedPasswordDatasource.java @@ -0,0 +1,56 @@ +package it.eng.knowage.tomcatpasswordencryption; + +import java.util.Enumeration; +import java.util.Hashtable; + +import javax.naming.Context; +import javax.naming.Name; +import javax.naming.RefAddr; +import javax.naming.Reference; +import javax.naming.StringRefAddr; + +import it.eng.knowage.tomcatpasswordencryption.helper.EncryptedPasswordUtils; +import org.apache.juli.logging.Log; +import org.apache.juli.logging.LogFactory; + +public class KnowageTomcatEncryptedPasswordDatasource extends org.apache.tomcat.jdbc.pool.DataSourceFactory { + + private static final Log log = LogFactory.getLog(KnowageTomcatEncryptedPasswordDatasource.class); + + @Override + public Object getObjectInstance(Object obj, Name name, Context nameCtx, Hashtable environment) throws Exception { + try { + if (obj instanceof Reference ref) { + StringRefAddr passwordRefAddr = (StringRefAddr) ref.get(PROP_PASSWORD); + if (passwordRefAddr != null) { + String encryptedPwd = (String) passwordRefAddr.getContent(); + String cleartextPwd = decrypt(encryptedPwd); + int index = find(ref); + if (index >= 0) { + ref.remove(index); + ref.add(index, new StringRefAddr(PROP_PASSWORD, cleartextPwd)); + } + } + } + } catch (Exception e) { + log.error("Failed to decrypt password. Please check DataSource definition."); + throw e; + } + return super.getObjectInstance(obj, name, nameCtx, environment); + } + + private int find(Reference ref) { + Enumeration enu = ref.getAll(); + for (int i = 0; enu.hasMoreElements(); i++) { + RefAddr addr = enu.nextElement(); + if (addr.getType().equals(PROP_PASSWORD)) + return i; + } + return -1; + } + + public static String decrypt(String encryptSource) { + return EncryptedPasswordUtils.decrypt(encryptSource); + } + +} diff --git a/tomcat-password-encryption/src/main/java/it/eng/knowage/tomcatpasswordencryption/helper/EncryptOnce.java b/tomcat-password-encryption/src/main/java/it/eng/knowage/tomcatpasswordencryption/helper/EncryptOnce.java new file mode 100644 index 00000000000..a8bdfcfdb57 --- /dev/null +++ b/tomcat-password-encryption/src/main/java/it/eng/knowage/tomcatpasswordencryption/helper/EncryptOnce.java @@ -0,0 +1,31 @@ +package it.eng.knowage.tomcatpasswordencryption.helper; +import org.jasypt.encryption.pbe.StandardPBEStringEncryptor; +import org.jasypt.encryption.pbe.config.SimpleStringPBEConfig; + +public class EncryptOnce { + + public static void main(String[] args) { + if (args.length == 0) { + System.err.println("Usage: java -Dknowage.enc.password= [-Dknowage.enc.algorithm=PBEWithMD5AndDES] " + + "[-Dknowage.enc.keyObtentionIterations=1000] EncryptOnce "); + System.exit(1); + } + String clear = args[0]; + String key = EncryptedPasswordUtils.resolveKey(); + if (key == null || key.isEmpty()) { + System.err.println("Missing -Dknowage.enc.password.file"); + System.exit(2); + } + + SimpleStringPBEConfig cfg = new SimpleStringPBEConfig(); + cfg.setPassword(key); + cfg.setPoolSize("1"); + cfg.setStringOutputType("base64"); + + StandardPBEStringEncryptor enc = new StandardPBEStringEncryptor(); + enc.setConfig(cfg); + + String cipher = enc.encrypt(clear); + System.out.println("#encr#" + cipher); + } +} \ No newline at end of file diff --git a/tomcat-password-encryption/src/main/java/it/eng/knowage/tomcatpasswordencryption/helper/EncryptedPasswordUtils.java b/tomcat-password-encryption/src/main/java/it/eng/knowage/tomcatpasswordencryption/helper/EncryptedPasswordUtils.java new file mode 100644 index 00000000000..57afd3f8999 --- /dev/null +++ b/tomcat-password-encryption/src/main/java/it/eng/knowage/tomcatpasswordencryption/helper/EncryptedPasswordUtils.java @@ -0,0 +1,74 @@ +package it.eng.knowage.tomcatpasswordencryption.helper; + +import org.jasypt.encryption.pbe.StandardPBEStringEncryptor; +import org.jasypt.encryption.pbe.config.SimpleStringPBEConfig; + +import java.io.IOException; +import java.nio.charset.StandardCharsets; +import java.nio.file.Files; +import java.nio.file.Path; + +public final class EncryptedPasswordUtils { + private static final String ENCRYPTED_PREFIX = "#encr#"; + + private EncryptedPasswordUtils() {} + + public static String decrypt(String value) { + if (value == null || value.isEmpty()) return value; + if (!value.startsWith(ENCRYPTED_PREFIX)) { + return value; + } + String cipherText = value.substring(ENCRYPTED_PREFIX.length()); + String password = resolveKey(); + if (password == null || password.isEmpty()) { + throw new IllegalStateException(""" + Missing decryption key. Provide it via system property knowage.enc.password, " + + "environment variable KNOWAGE_ENC_PASSWORD, or a file at ${catalina.base}/conf/knowageTomcatEncryptedPasswordDatasource " + + "or -Dknowage.enc.password.file=/secure/path + """); + } + + SimpleStringPBEConfig cfg = new SimpleStringPBEConfig(); + cfg.setPassword(password); + cfg.setPoolSize("1"); + cfg.setStringOutputType("base64"); + + StandardPBEStringEncryptor enc = new StandardPBEStringEncryptor(); + enc.setConfig(cfg); + return enc.decrypt(cipherText); + } + + public static String resolveKey() { + // Prefer explicit file path via system property + String fileProp = System.getProperty("knowage.enc.password.file"); + if (fileProp != null && !fileProp.isEmpty()) { + String fromFile = readFirstLineTrimmed(Path.of(fileProp)); + if (fromFile != null && !fromFile.isEmpty()) return fromFile; + } + + // Default file under Tomcat conf: ${catalina.base}/conf/passwordEncryptionSecret + String catalinaBase = System.getProperty("catalina.base"); + if (catalinaBase != null && !catalinaBase.isEmpty()) { + Path defaultPath = Path.of(catalinaBase, "conf", "knowageTomcatEncryptedPasswordDatasource"); + String fromFile = readFirstLineTrimmed(defaultPath); + if (fromFile != null && !fromFile.isEmpty()) return fromFile; + } + + return null; + } + + private static String readFirstLineTrimmed(Path path) { + try { + if (Files.isRegularFile(path)) { + for (String line : Files.readAllLines(path, StandardCharsets.UTF_8)) { + String trimmed = line.trim(); + if (!trimmed.isEmpty()) return trimmed; + } + } + } catch (IOException ignored) { + } + return null; + } + + +} \ No newline at end of file