Encrypting config files with Spring 3.1 and jasypt

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

 

Leave a Reply