Automatically obfuscate your Java application's configuration

Ever been worried about credentials (passwords, secrets or access keys) residing in your host’s application configuration files as plain text? Read on how to automatically obfuscate your application’s properties residing in configuration files at a system-context’s level.

My blog post Dead simple Java application configuration already outlined the ease of use of the refcodes-properties artifact. The refcodes-properties-ext-obfuscation artifact extends this functionality with obfuscation features bound to a dedicated system context.

This means, that you automatically can encrypt specific properties in your properties files which will automatically be decrypted at runtime: The file just reveals the encrypted values for the according properties. Even at runtime the according properties are kept in memory with their encrypted representation and decrypted “on-the-fly” only when actually being retrieved by your application from the properties holding object.

Properties residing in your properties file marked accordingly as “to-be-encrypted” will be encrypted upon the first time your application loads them and written back with their encrypted representation, now being marked as “to-be-decrypted”.

An open door may tempt a saint: Obfuscate!

First of all, some warning words on security: What do I mean with obfuscation?

As the encryption and decryption mechanism works without any user interaction, it is your application doing the encryption and decryption by itself. Don’t worry: The refcodes-properties-ext-obfuscation artifact does not use a hard-coded key for accomplishing this task. Nevertheless, the data must be encrypted and decrypted with some key. We use a symmetric-key algorithm, as a public-key cryptography approach would make no sense here. This symmetric-key must somehow be known by your application. This in turn means that with enough criminal intend and technical skills as well as with direct access to your host, an attacker would be able to decrypt your properties. Therefore I prefer to call this method of securing your properties obfuscation:

Obfuscating properties secures against attempts to decrypt your sensitive data in cases where the attacker has stolen your properties file but with no access to your host’s runtime. By obfuscating your properties, we close the door so to not to tempt any saints. An attacker though having access to your host may reverse-calculate the symmetric-key in use!

Before and after obfuscation

The usage of the refcodes-properties-ext-obfuscation artifact is quite simple. Before you start your application in question, you have to provide a writable properties file as described in the blog post Dead simple Java application configuration with those values to be obfuscated being prefixed with encrypt: (“to-be-encrypted”). Using the Java notation for your properties, a property with name secret to be obfuscated looks as follows: secret=encrypt:this_is_my_secret. An according properties file may look as follows:

1
2
3
4
port=8080
maxConnections=20
identity=webrunner
secret=encrypt:this_is_my_secret

After the fist execution of your application, the refcodes-properties-ext-obfuscation artifact identifies the properties to be obfuscated, obfuscates them and writes back the obfuscated properties to the file, with the obfuscated values being prefixed with decrypt: (“to-be-decrypted”). Using the Java notation for your properties, a property with name secret being obfuscated looks similar to: secret=decrypt:sOhxXOecKqNOBJm+Uzwx38Y=. An accordingly obfuscated properties file may look as follows:

1
2
3
4
port=8080
maxConnections=20
identity=webrunner
secret=decrypt:sOhxXOecKqNOBJm+Uzwx38Y=

As we do not use any hard-coded keys, how is key-generation for the obfuscation process being accomplished? The refcodes-properties-ext-obfuscation artifact knows multiple contexts of obfuscation being a combination of host-, user- and/or application individual properties.

Obfuscation contexts

As obfuscation is done automatically by the refcodes-properties-ext-obfuscation artifact, the keys for obfuscation are generated automatically: These keys are not persisted anywhere, they are calculated from specific properties whenever needed. Moreover, the keys have to be calculated by properties which stay constant for some given context but differ for other manifestations of that context. The contexts supported for obfuscation are host-individual, user-individual and/or application-individual:

Host-individual-context

The properties used for calculating the obfuscation key are specific for a dedicated host. The same properties though look different on different hosts. Calculating the key on different hosts results in different keys for each host. Calculating the key on the same host multiple times always results in the same key, independent from the user account running the application as well as from the implementing application itself.

In other words: The configuration is bound to a specific host.

User-individual-context

The properties used for calculating the obfuscation key are specific for a dedicated user account running your application. The same properties though look different for different users. Calculating the key for different users results in different keys for each user. Calculating the key for the same user multiple times always results in the same key, independent from the host running the application as well as from the implementing application itself.

In other words: The configuration is bound to a specific user.

Application-individual-context

The properties used for calculating the obfuscation key are specific for a dedicated application. The same properties though look different for different applications. Calculating the key for different applications results in different keys for each application. Calculating the key for the same application multiple times always results in the same key, independent from the host running the application as well as from user account running the application.

In other words: The configuration is bound to a specific application.

Combining contexts

The refcodes-properties-ext-obfuscation artifact now allows you to use any combination of the above mentioned three levels of obfuscation: APPLICATION, USER_APPLICATION, HOST_USER_APPLICATION, HOST_APPLICATION, USER, HOST_USER, HOST. These levels of obfuscation are defined by the enumeration SystemContext to be used when constructing your obfuscated properties.

An attacker having access to the host may determine the host-, user- and/or application-specific properties in order to calculate the actual key. Major changes on the host, the user account or the application itself may result in different keys being calculated!

Getting started

To get up and running, include the following dependency (without the three dots “…”) in your pom.xml:

1
2
3
4
5
6
7
8
9
<dependencies>
	...
	<dependency>
		<artifactId>refcodes-properties-ext-obfuscation</artifactId>
		<groupId>org.refcodes</groupId>
		<version>3.3.5</version>
	</dependency>
	...
</dependencies>

Some basic example, loading and storing properties

A very (very!) basic example may look as below, just loading some Java based properties file using syntactic sugar:

1
2
3
4
5
6
7
8
9
10
11
import static org.refcodes.properties.ext.obfuscation.ObfuscationPropertiesSugar.*;
// ...
public class Foo {
	public Foo() throws IOException, ParseException {
		ResourcePropertiesBuilder properties = seekFromJavaProperties( "application.properties" );
		ObfuscationResourcePropertiesBuilder obfuscateProperties = obfuscate( properties );
		obfuscateProperties.put("secret", "encrypt:Hello world!");
		obfuscateProperties.flush();
	}
	// ...
}

In the above example, we load properties from a file called “application.properties” as described here (see the ConfigLocator on how the properties file is discovered and where it is to be placed). Then we decorate the properties with a decorator of type ObfuscationResourcePropertiesBuilder. The actual implementation of this type is the ObfuscationResourcePropertiesBuilderDecorator class. Specifying a level of obfuscation would look as follows:

1
2
3
4
5
6
7
8
9
10
11
import static org.refcodes.properties.ext.obfuscation.ObfuscationPropertiesSugar.*;
// ...
public class Foo {
	public Foo() throws IOException, ParseException {
		ResourcePropertiesBuilder properties = seekFromJavaProperties( "application.properties" );
		ObfuscationResourcePropertiesBuilder  obfuscateProperties = obfuscate( properties, SystemContext.HOST_USER );
		obfuscateProperties.put("secret", "encrypt:Hello world!");
		obfuscateProperties.flush();
	}
	// ...
}

Here, the key generated for obfuscation is host- and user-individual, e.g. different applications executed by the same user on the same host will use the same key, though when being executed by a different user and/or on a different host will result in a different key to be used.

Instead of passing a SystemContext value to the obfuscate method, you might also pass your own individual String being the secret from which the obfuscation key is being derived.

Under the hood

Above we use syntactic sugar: When invoking obfuscate, a decorator is being wrapped around the properties object. We can write the same functionality without the syntactic sugar as follows:

1
2
3
4
5
6
7
8
9
10
11
import org.refcodes.properties.ext.obfuscation.ObfuscationResourcePropertiesBuilderDecorator;
// ...
public class Foo {
	public Foo() throws IOException, ParseException {
		ResourcePropertiesBuilder properties = ...;
		ObfuscationResourcePropertiesBuilder  obfuscateProperties = new ObfuscationResourcePropertiesBuilderDecorator( properties, SystemContext.HOST_USER );
		obfuscateProperties.put("secret", "encrypt:Hello world!");
		obfuscateProperties.flush();
	}
	// ...
}

Instead of passing a SystemContext value to the constructor, you might also pass your own individual String being the secret from which the obfuscation key is being derived.

Further reading

See the blog post on the refcodes-properties artifact for further details on the usage of this artifact. Also see the blog posts Dead simple Java application configuration, Automatically obfuscate your Java application’s configuration and All-in-one Java configuration properties at hand. For examples and usage, please take a look at the according Unit-Tests here. On chaos-based encryption, see also the refcodes-security artifact and the according Chaos-based encryption blog post.