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) { //... }