I've been working on a project that at one point needed encrypted configuration files. The JASYPT library provides a very nice tie-in with Spring. There were some pieces I had to put together, so here are the results of my @Configuration file that handles encryption. It will scan a given directory (specified by the VM argument config.dir) for any .properties files, and uses JASYPT to decrypt any encrypted values. I am using the Bouncy Castle encryption library, to get around the 128bit default encryption limit of the JVM (which can be worked around in other situations), and using fairly strong encryption - PBEWITHSHA256AND256BITAES-CBC-BC with 4000 key iterations.
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.Properties;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
import org.jasypt.encryption.StringEncryptor;
import org.jasypt.encryption.pbe.StandardPBEStringEncryptor;
import org.jasypt.encryption.pbe.config.EnvironmentStringPBEConfig;
import org.jasypt.encryption.pbe.config.SimplePBEConfig;
import org.jasypt.properties.PropertyValueEncryptionUtils;
import org.jasypt.salt.RandomSaltGenerator;
import org.jasypt.spring31.properties.EncryptablePropertySourcesPlaceholderConfigurer;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.support.PropertySourcesPlaceholderConfigurer;
import org.springframework.core.io.FileSystemResource;
import org.springframework.core.io.Resource;
@Configuration
public class ConfigFileEncryption {
private static SimplePBEConfig encryptionConfig(String configFilePassword) {
EnvironmentStringPBEConfig pbeConfig = new EnvironmentStringPBEConfig();
pbeConfig.setAlgorithm("PBEWITHSHA256AND256BITAES-CBC-BC");
pbeConfig.setKeyObtentionIterations(4000);
pbeConfig.setPassword(configFilePassword);
pbeConfig.setProvider(new BouncyCastleProvider());
pbeConfig.setSaltGenerator(new RandomSaltGenerator());
return pbeConfig;
}
@Bean(name = "configFileEncryptor")
@Autowired
public StringEncryptor configurationEncryptor(@Qualifier("configFilePassword") String configFilePassword) {
StandardPBEStringEncryptor se = new StandardPBEStringEncryptor();
se.setConfig(encryptionConfig(configFilePassword));
return se;
}
@Bean
@Autowired
public PropertySourcesPlaceholderConfigurer propertyConfigurer(
@Qualifier("configFileEncryptor") StringEncryptor encryptor,
@Value("#{ systemProperties['configdir'] }") String configDir) {
EncryptablePropertySourcesPlaceholderConfigurer pc = new EncryptablePropertySourcesPlaceholderConfigurer(encryptor);
File dir = new File(configDir);
List<Resource> resources = new ArrayList<Resource>();
if (dir.exists() && dir.isDirectory()) {
for (File f : dir.listFiles()) {
if (f.isFile() && f.getName().endsWith(".properties")) {
resources.add(new FileSystemResource(f));
}
}
}
pc.setLocations(resources.toArray(new Resource[resources.size()]));
return pc;
}
}
To generate the properties:
public static void main(String[] args) throws IOException {
ConfigFileEncryption cfe = new ConfigFileEncryption();
StringEncryptor enc = cfe.configurationEncryptor("s3cr3tk3y!");
Properties props = new Properties();
props.put("database.username", PropertyValueEncryptionUtils.encrypt("dbusername", enc));
props.put("database.password", PropertyValueEncryptionUtils.encrypt("dbpassword", enc));
props.put("database.url", PropertyValueEncryptionUtils.encrypt("jdbc://...", enc));
props.store(System.out, "");
}
To use the properties, you need to @Import / @ComponentScan the ConfigFileEncryption class, and then use the properties as arguments to your data source bean:
@Bean
@Autowired
public DataSource dataSource(@Value("${database.username}") String username,
@Value("${database.password}") String password,
@Value("${database.url}") String url) {
//...
}
