Encrypted properties with Spring
It may happen, for whatever reason, that you must store credentials in your project. You could store the plain text credentials in your code repository, but this would allow everybody with access to the repository to use these credentials. Therefore, you should avoid this. Fortunately Spring has a solution for that and allows properties to be encrypted.
Example Application
To show this by an example, I created a small application which returns the value of a decrypted property via a REST interface. The encryption of the properties is not part of the code, I will do this with Spring Boot CLI. As a start I created a new project at https://start.spring.io/ with the following dependencies:
- Cloud Bootstrap: To decrypt the properties
- Web: To create a simple RestController
This is the complete pom.xml:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 |
<?xml version="1.0" encoding="UTF-8"?> <project xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://maven.apache.org/POM/4.0.0" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>com.example</groupId> <artifactId>demo</artifactId> <version>0.0.1-SNAPSHOT</version> <packaging>jar</packaging> <name>demo</name> <description>Demo project for Spring Boot</description> <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>1.5.10.RELEASE</version> <relativePath/> <!-- lookup parent from repository --> </parent> <properties> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding> <java.version>1.8</java.version> <spring-cloud.version>Dalston.SR1</spring-cloud.version> </properties> <dependencies> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> </dependencies> <dependencyManagement> <dependencies> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-dependencies</artifactId> <version>${spring-cloud.version}</version> <type>pom</type> <scope>import</scope> </dependency> </dependencies> </dependencyManagement> <build> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> </plugin> </plugins> </build> </project> |
The Java code is really simple. It only returns the value of the property
encrypted.property :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
@SpringBootApplication public class DemoApplication { public static void main(String[] args) { SpringApplication.run(DemoApplication.class, args); } @RestController @RequestMapping(path = "/test") static class Controller { @Value("${encrypted.property}") private String encrypted; @GetMapping public String getEncrypted() { return encrypted; } } } |
Encrypting the properties
Before we can add the encrypted property encrypted.property to application.properties, we first have to encrypt it. To do this, I use Spring Boot CLI:
Install Spring Boot CLI, then add the cloud extension:
1 |
spring install org.springframework.cloud:spring-cloud-cli:1.3.2.RELEASE |
Now it is possible to encrypt the value “mysecret” with the key “foo”:
1 |
spring encrypt mysecret --key foo |
The return value of this command is the encrypted property and we can add it to application.properties:
1 |
encrypted.property={cipher}711448026e2c6a977b2be1b22f13649cc938366397fbd345113d2a50e27c348f |
The prefixed {cipher} allows Spring to recognize encrypted properties.
Decrypting the properties
If you try to start this application, it will fail with the following exception:
1 |
java.lang.IllegalStateException: Cannot decrypt: key=encrypted.property |
Spring cannot decrypt the property, because the decryption key is still missing. You have to provide the key via the property
encrypt.key. As mentioned above, we still do not want to add a plain text password (the decryption key) to the file application.properties, because then it would be added to the code repository and this would finally lead the whole concept to absurdity. We need another way to provide the decryption key.: Spring can not only read property values from a properties file, you can also use environment variables. Therefore, I provide the key as an environment variable:
1 |
ENCRYPT_KEY=foo java -jar target/demo-0.0.1-SNAPSHOT.jar |
As a result, it is now possible to start the application and call the endpoint to receive the decrypted property:
1 |
curl localhost:8080/test |
As expected, this returns “mysecret” which is the decrypted value of the property.
In a real world example you wouldn’t start the application manually and provide the key as shown above. To automate this process, you can add the encryption key to your deployment infrastructure, and let it start the application with the key as an environment variable. In this way you can start the application, but you do not have to store the decryption key in the code repository.
A criticism of this approach could be that it is easier to provide the password directly as an environment variable instead of first encrypting it and then providing the decryption key. Since with both approaches you have to provide one environment variable. At least when there is more than one password, you will see the advantage of encrypted properties, because you have to set only one environment variable, independent of the number of passwords.
Conclusion
In this example I showed how to encrypt and decrypt properties with Spring without any additional infrastructure. With the help of encrypted property you can add credentials to property files without worrying that someone with access to your code repository is able to misuse them, because the decryption key is not stored in the code repository.
If you want to switch to Spring Cloud Config at any later date, it is also possible, because the encryption and decryption of properties in Spring Cloud Config Server work exactly the same.
Comment article
Recent posts






Comments
Alice
Hi,
Thanks for the article.
I am unable to figure out on how the decryption is done in the application.
The application’s test endpoint returns the encrypted password from the properties.
Can you please help?
Dilli Johannes
Hi Alice,
thank you for your feedback and I’m really sorry for my late reply.
Did you add “Cloud Bootstrap”(org.springframework.cloud:spring-cloud-starter) to your project? If you forget this dependency, then your application will compile and start, but the properties will not be decrypted and the web endpoint will return “{cipher}711448026e2c6a977b2be1b22f13649cc938366397fbd345113d2a50e27c348f”.
Ashish
I get the error “Unable to initiaze due to invalid secret key.” when I try to encrypt, any idea ?
spring encrypt mysecret –key foo –debug
Could not create public key RSA Encryptor (String is not PEM encoded data, nor a public key encoded for ssh)
Trying symmetric key
Unable to initialize due to invalid secret key
java.lang.IllegalArgumentException: Unable to initialize due to invalid secret key
at org.springframework.security.crypto.encrypt.CipherUtils.initCipher(CipherUtils.java:120)
at org.springframework.security.crypto.encrypt.AesBytesEncryptor.encrypt(AesBytesEncryptor.java:115)
at org.springframework.security.crypto.encrypt.HexEncodingTextEncryptor.encrypt(HexEncodingTextEncryptor.java:36)
at org.springframework.cloud.cli.command.encrypt.EncryptCommand$EncryptOptionHandler.run(EncryptCommand.java:61)
at org.springframework.boot.cli.command.options.OptionHandler.run(OptionHandler.java:84)
at org.springframework.boot.cli.command.OptionParsingCommand.run(OptionParsingCommand.java:54)
at org.springframework.boot.cli.command.CommandRunner.run(CommandRunner.java:219)
at org.springframework.boot.cli.command.CommandRunner.runAndHandleErrors(CommandRunner.java:171)
at org.springframework.boot.cli.SpringCli.main(SpringCli.java:63)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:606)
at org.springframework.boot.loader.MainMethodRunner.run(MainMethodRunner.java:48)
at org.springframework.boot.loader.Launcher.launch(Launcher.java:87)
at org.springframework.boot.loader.Launcher.launch(Launcher.java:50)
at org.springframework.boot.loader.JarLauncher.main(JarLauncher.java:51)
Caused by: java.security.InvalidKeyException: Illegal key size
at javax.crypto.Cipher.checkCryptoPerm(Cipher.java:1024)
at javax.crypto.Cipher.implInit(Cipher.java:790)
at javax.crypto.Cipher.chooseProvider(Cipher.java:849)
at javax.crypto.Cipher.init(Cipher.java:1348)
at javax.crypto.Cipher.init(Cipher.java:1282)
at org.springframework.security.crypto.encrypt.CipherUtils.initCipher(CipherUtils.java:113)
… 16 more
Johannes Dilli
Hi Caldash,
the error message looks like it was caused by an Oracle database. Does the login work without encrypting the property?
If Spring doesn’t complain that it cannot decrypt the property, it could be the same problem as Jay’s (see comment above). Did you install JCE?
The decryption of the properties takes place at runtime. This means you can build the jar file even though encryption is not working properly. It should still be possible to overwrite properties via an environment variable.
Caldash
When I try to run the application I receive the following error code: ORA-01017: invalid username/password; logon denied
instead of : java.lang.IllegalStateException: Cannot decrypt: key=encrypted.property
Does this mean that my application is not acknowledging the cipher?
Even if I get illegal state exception, will I be able to run mvn clean install on the app to generate jar file so that I can pass it the environment variable as a parameter?
I have in the application.properties file the following db.password:
spring.datasource.password= {cipher}GeneratedKey
Thanks
Johannes Dilli
I had a look at the documentation of Spring Cloud Config. The problem could be that you’re missing a “full-strength JCE installation in your JVM”: https://cloud.spring.io/spring-cloud-config/single/spring-cloud-config.html#_encryption_and_decryption Without that the encryption and decryption features of Spring Cloud won’t work.
Can you try to install the JCE on Windows and try it again?
Jay
Running the same application with no change on Ubuntu works (Windows 7 still not working).
Jay
I tried a copy and paste of the whole example but still getting error at start up
Caused by: java.lang.UnsupportedOperationException: No decryption for FailsafeTextEncryptor. Did you configure the keystore correctly?
Fazle Khan
I figured out the problem.
My archiva server was not correctly proxying requests to the spring repo so the libraries for Finchley were never being downloaded to my local maven repo.
Once I removed the maven mirror to my archiva server the libraries were downloaded and the functionality worked as expected.
http://repo.spring.io/milestone
Thanks!
Fazle Khan
The problem seems to be with the latest versions of Spring (2.0.0.RELEASE) and Cloud-Config (Finchley.BUILD-SNAPSHOT) If I downgrade to the versions 1.5.10.RELEASE and Dalston.SR1 like in the sample pom everything works as expected. But, if I use the more recent versions the functionality breaks.
I don’t want to downgrade Spring so while keeping it at 2.0.0.RELEASE I’ll explore if a more recent of Cloud-Config works.
4.0.0
com.example
demo
0.0.1-SNAPSHOT
jar
demo
Demo project for Spring Boot
org.springframework.boot
spring-boot-starter-parent
2.0.0.RELEASE
UTF-8
UTF-8
1.8
Finchley.BUILD-SNAPSHOT
org.springframework.cloud
spring-cloud-starter
org.springframework.boot
spring-boot-starter-web
org.springframework.cloud
spring-cloud-dependencies
${spring-cloud.version}
pom
import
org.springframework.boot
spring-boot-maven-plugin
Johannes Dilli
Hi,
Thank you for trying my example. The @EnableConfigServer annotation is part of the Spring Cloud Config Server package and should not be available on the classpath.
Did you add spring-cloud-starter dependency? (I just added the complete pom. xml at the beginning of this article.) Without the spring-cloud-starter the application is not aware of encrypted properties and will return the original value of encrypted.property.
You can test this by starting the application without “ENCRYPT_KEY=foo”: If it starts, spring-cloud-starter is missing. If it fails with exception “java.lang.IllegalStateException: Cannot decrypt: key=encrypted.property”, it should be able to decrypt the property if the decryption key is available.
Fazle Khan
So it looks like the annotation @EnableConfigServer must be added to the DemoApplication.java and the configuration spring.profiles.active=native added to the bootstrap.properties for this to work
Fazle Khan
When I try to use your example the original value of ‘encrypted.property’ is returned by the rest controller. The application does not try and decrypt the property