Spring Boot: How to encrypt properties in application.properties
Sometimes you don’t want your properties to stay as plain text in application.properties file. Maybe you are connecting to a database and you have to write your database password in application.properties. In this tutorial, I am going to use Jasypt library for that purpose. Jasypt (Java Simplified Encryption) is a java library which allows the developer to add basic encryption capabilities to his/her projects with minimum effort, and without the need of having deep knowledge on how cryptography works.
Let’s begin,
First, add the related dependency to the project. I am using maven, so I will add the maven dependency to my pom.xml
<dependency>
<groupId>com.github.ulisesbocchio</groupId>
<artifactId>jasypt-spring-boot-starter</artifactId>
<version>2.0.0</version>
</dependency>
We only need to add @EnableConfigurationProperties annotation to our application and jasypt will automaticly detect encrypted values and decrypt them before they are being used. The CommandLineRunner I have added below is just to test the decryption mechanism.
@SpringBootApplication
public class JasyptExampleApplication {
public static void main(String[] args) {
SpringApplication.run(JasyptExampleApplication.class, args);
}
@Component
public class MyRunner implements CommandLineRunner {
@Value("${myProperty}")
private String myProperty;
@Override
public void run(String... args) throws Exception {
System.out.println("My property is = " + myProperty);
}
}
}
This is because Jasypt needs to know the secret(password) to decrypt the property. We can tell this to our program several ways:
1- We can give it as a command line argument when running the application;
–jasypt.encryptor.password=MY_SECRET
2- We can set it as an environment variable, this is also useful when you are running your application on Tomcat. You can give it to Tomcat’s setenv.sh file;
export CATALINA_OPTS=”-Djasypt.encryptor.password=MY_SECRET”
You can also unset the environment variable after running the application, so there will be no doorway left behind, at least in a human-readable sense.
3- You can give it in application.properties but this might be the dumbest way as it has no difference with giving the property as plain text.
If you know a better way, write a comment below!
Now let’s look at the final output:
2018-04-25 14:03:26.413 INFO 10028 --- [ main] c.u.j.EncryptablePropertySourceConverter : Converting PropertySource commandLineArgs [org.springframework.core.env.SimpleCommandLinePropertySource] to EncryptableEnumerablePropertySourceWrapper
2018-04-25 14:03:26.414 INFO 10028 --- [ main] c.u.j.EncryptablePropertySourceConverter : Converting PropertySource systemProperties [org.springframework.core.env.MapPropertySource] to EncryptableMapPropertySourceWrapper
2018-04-25 14:03:26.414 INFO 10028 --- [ main] c.u.j.EncryptablePropertySourceConverter : Converting PropertySource systemEnvironment [org.springframework.boot.env.SystemEnvironmentPropertySourceEnvironmentPostProcessor$OriginAwareSystemEnvironmentPropertySource] to EncryptableMapPropertySourceWrapper
2018-04-25 14:03:26.414 INFO 10028 --- [ main] c.u.j.EncryptablePropertySourceConverter : Converting PropertySource random [org.springframework.boot.env.RandomValuePropertySource] to EncryptablePropertySourceWrapper
2018-04-25 14:03:26.415 INFO 10028 --- [ main] c.u.j.EncryptablePropertySourceConverter : Converting PropertySource applicationConfig: [classpath:/application.properties] [org.springframework.boot.env.OriginTrackedMapPropertySource] to EncryptableMapPropertySourceWrapper
2018-04-25 14:03:26.468 INFO 10028 --- [ main] c.u.j.r.DefaultLazyPropertyResolver : Property Resolver custom Bean not found with name 'encryptablePropertyResolver'. Initializing Default Property Resolver
2018-04-25 14:03:26.470 INFO 10028 --- [ main] c.u.j.d.DefaultLazyPropertyDetector : Property Detector custom Bean not found with name 'encryptablePropertyDetector'. Initializing Default Property Detector
2018-04-25 14:03:26.472 INFO 10028 --- [ main] c.u.j.encryptor.DefaultLazyEncryptor : String Encryptor custom Bean not found with name 'jasyptStringEncryptor'. Initializing Default String Encryptor
2018-04-25 14:03:26.478 INFO 10028 --- [ main] c.u.j.encryptor.DefaultLazyEncryptor : Encryptor config not found for property jasypt.encryptor.algorithm, using default value: PBEWithMD5AndDES
2018-04-25 14:03:26.479 INFO 10028 --- [ main] c.u.j.encryptor.DefaultLazyEncryptor : Encryptor config not found for property jasypt.encryptor.keyObtentionIterations, using default value: 1000
2018-04-25 14:03:26.479 INFO 10028 --- [ main] c.u.j.encryptor.DefaultLazyEncryptor : Encryptor config not found for property jasypt.encryptor.poolSize, using default value: 1
2018-04-25 14:03:26.479 INFO 10028 --- [ main] c.u.j.encryptor.DefaultLazyEncryptor : Encryptor config not found for property jasypt.encryptor.providerName, using default value: null
2018-04-25 14:03:26.479 INFO 10028 --- [ main] c.u.j.encryptor.DefaultLazyEncryptor : Encryptor config not found for property jasypt.encryptor.providerClassName, using default value: null
2018-04-25 14:03:26.479 INFO 10028 --- [ main] c.u.j.encryptor.DefaultLazyEncryptor : Encryptor config not found for property jasypt.encryptor.saltGeneratorClassname, using default value: org.jasypt.salt.RandomSaltGenerator
2018-04-25 14:03:26.480 INFO 10028 --- [ main] c.u.j.encryptor.DefaultLazyEncryptor : Encryptor config not found for property jasypt.encryptor.stringOutputType, using default value: base64
2018-04-25 14:03:26.934 INFO 10028 --- [ main] o.s.j.e.a.AnnotationMBeanExporter : Registering beans for JMX exposure on startup
2018-04-25 14:03:26.948 INFO 10028 --- [ main] demo.JasyptExampleApplication : Started JasyptExampleApplication in 1.264 seconds (JVM running for 2.06)
My property is MBcoder
As you can see it picked up the PBEWithMD5AndDES algorithm as default value and with the given password, MY_SECRET, it successfully decrypted myProperty
I hope this article was useful, see you another time!
how can we handle this if i need to maintain 2 encrypted passwords in application.properties file
You can store them in different variables. And use in your application. Like;
Password1=ENC(…)
Password2=ENC(…)
But of course secret key for generating those values should be same for both. By secret key, I mean the password field of Jasypt.If you were talking about that password, I think it is not possible to give different secret keys at the same time.
If it is not a spring application that runs but is a service that is called how do you get JASypt to automatically detect and convert the values? Mine stay as ENC(…).
We have a Spring application project as well where it works just fine as you stated above.
Hello,
You can do this without Spring Boot. Jasypt provides java based configuration. I will copy a part from Jasypt.org, hope this is what you are looking for:
By using an org.jasypt.properties.EncryptableProperties object, an application would be able to correctly read and use a .properties file like this:
datasource.driver=com.mysql.jdbc.Driver
datasource.url=jdbc:mysql://localhost/reportsdb
datasource.username=reportsUser
datasource.password=ENC(G6N718UuyPE5bHyWKyuLQSm02auQPUtm)
Note that the database password is encrypted (in fact, any other property could also be encrypted, be it related with database configuration or not).
How do we read this value? like this:
/*
* First, create (or ask some other component for) the adequate encryptor for
* decrypting the values in our .properties file.
*/
StandardPBEStringEncryptor encryptor = new StandardPBEStringEncryptor();
encryptor.setPassword(“jasypt”); // could be got from web, env variable…
/*
* Create our EncryptableProperties object and load it the usual way.
*/
Properties props = new EncryptableProperties(encryptor);
props.load(new FileInputStream(“/path/to/my/configuration.properties”));
/*
* To get a non-encrypted value, we just get it with getProperty…
*/
String datasourceUsername = props.getProperty(“datasource.username”);
/*
* …and to get an encrypted value, we do exactly the same. Decryption will
* be transparently performed behind the scenes.
*/
String datasourcePassword = props.getProperty(“datasource.password”);
// From now on, datasourcePassword equals “reports_passwd”…
For decrypting the encrypted value, we just had to access it with getProperty, just as with any other non-encrypted value.
EncryptableProperties objects can receive as a constructor argument both an org.jasypt.encryption.StringEncryptor implementation or an org.jasypt.util.TextEncryptor object.
A good place to save your secreet password is the home directory of the user
Path path = Paths.get(System.getProperty(“user.home”)+”/.mySecret”);
String secret = new String(Files.readAllBytes(path));
secret = secret.trim();
System.setProperty(“jasypt.encryptor.password”, secret);
Is it safe to use third party library in our application: com.github.ulisesbocchio? Do we have any alternate way?
I don’t know if there is any other way. I mean of course you can implement your own encryption utility and you can use that on to encrypt and decrypt data. Shouldn’t be that hard.
About if it is safe to use that 3rd party library. It is an open source project on Github so you can go and check if there is something dangerous in the code. Or you can check how they achieved it and try to implement it yourself to be super safe.
ok.. Thanks for the reply.. one more query.. I need to create war file of the application with above configuration. How could I achieve that? I am getting error.
Required Encryption configuration property missing: jasypt.encryptor.password
In the above tutorial it exactly tells you why you get this error. Please read the tutorial again 🙂