Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
71 changes: 71 additions & 0 deletions tomcat-password-encryption/pom.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>it.eng.knowage</groupId>
<artifactId>tomcat-password-encryption</artifactId>
<version>9.0.0-SNAPSHOT</version>
<name>Archetype - tomcat-password-encryption</name>
<url>http://maven.apache.org</url>

<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<maven.compiler.release>17</maven.compiler.release>
</properties>

<dependencies>
<dependency>
<groupId>org.apache.tomcat</groupId>
<artifactId>tomcat-jdbc</artifactId>
<version>9.0.83</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>org.jasypt</groupId>
<artifactId>jasypt</artifactId>
<version>1.9.3</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.11.0</version>
<configuration>
<release>${maven.compiler.release}</release>
<encoding>${project.build.sourceEncoding}</encoding>
</configuration>
</plugin>
<!-- Bundle dependencies (e.g., jasypt) inside one jar -->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-shade-plugin</artifactId>
<version>3.5.0</version>
<executions>
<execution>
<phase>package</phase>
<goals>
<goal>shade</goal>
</goals>
<configuration>
<createDependencyReducedPom>true</createDependencyReducedPom>
<shadedArtifactSet>
<includes>
<include>org.jasypt:jasypt</include>
</includes>
</shadedArtifactSet>
<relocations>
<!-- optional: avoid classpath conflicts by relocating jasypt -->
<relocation>
<pattern>org.jasypt</pattern>
<shadedPattern>shade.org.jasypt</shadedPattern>
</relocation>
</relocations>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
</project>
Original file line number Diff line number Diff line change
@@ -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<RefAddr> 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);
}

}
Original file line number Diff line number Diff line change
@@ -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=<KEY> [-Dknowage.enc.algorithm=PBEWithMD5AndDES] "
+ "[-Dknowage.enc.keyObtentionIterations=1000] EncryptOnce <CLEAR_TEXT_PASSWORD>");
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);
}
}
Original file line number Diff line number Diff line change
@@ -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;
}


}