Android project security — A way to hide sensitive keys

Android project security - A way to hide sensitive keys

Alejandro Moya
3 min readSep 19, 2018

--

On the verge of my birthday i had an interesting task, one of the projects I work on, required to hide the api keys to a third party service.

So I had to use gradle to achieve that and be able to replicate it on every developer machine in the team, we all know that sometimes messing with the build system is not an easy task, so, to make your life easier i thought on sharing the knowledge.

First some fast explanation on the process, we are going to create a few methods, these will read the key we want to replace from a source file, locate the placeholder on the destination file and replace it with the key, this will be done first in the build process, after the build is completed, we will replace the keys again with the placeholders to make it look as if nothing happened, you’ll have the keys available during the build and after that “they’ll go back to the original place”(they were there all the time 😄).

Now onto the process:

First create a file named keys.local in the project root and add it to the git ignore file, in the file we are going to store the keys, this must be done manually on every machine, this is the format we are going to use:

$SOME_SENSITIVE_KEY=534gh2348fvdsvadfv0sdfg8sd0
$SOME_OTHER_SENSITIVE_KEY=ergwe89e6ge7fg6r7g65fg7r6

Now we need to locate the files where the keys are stored in the project and replace those with the placeholder we want to use, as an example if we had a key on the manifest file as:

<meta-data
android:name="TheSensitiveKey"
android:value="534gh2348fvdsvadfv0sdfg8sd0" />

after replacing it will look as:

<meta-data
android:name="TheSensitiveKey"
android:value="$SOME_SENSITIVE_KEY" />

this should be done with any key we want to replace on any project file, remember that we need to save the files names and locations in the project for later as the script will use it.

Now we need to create a file named keysScript.gradle or choose another name, this file must be located in the app folder in the project structure, the same place where the build.gradle file is, in this file we’ll put this:

import org.gradle.internal.os.OperatingSystem//this variable holds the parameters used for each replacement
project.ext.parameters = [['./src/main', '$SOME_SENSITIVE_KEY','AndroidManifest.xml'],['pathToFile', '$SOME_OTHER_SENSITIVE_KEY','filename']]
//This task replaces the keys in the destination files using the parameters from the previous variable
task injectKeys() {
doLast {
for (param in project.ext.parameters) {
invokeSed(param[0], param[1], getKeys(param[1]), param[2])
}
}
}
//This method returns the destination files to the original format
def resetKeys() {
for (param in project.ext.parameters) {
invokeSed(param[0], getKeys(param[1]), param[1], param[2])
}
}
//This is the actual method used to replace the strings in the files
def invokeSed(String dir, String lookupText, String replacementText, String filename) {
exec {
if(dir != null) {
workingDir dir
}
if (OperatingSystem.current().isLinux()) {
commandLine 'sed', '-i', "s/${lookupText}/${replacementText}/g", filename
} else {
commandLine 'sed', '-i', '', "s/${lookupText}/${replacementText}/g", filename
}
}
}
//This method extracts the keys from keys.local using the supplied placeholder
def getKeys(String placeholder) {
def stdout = new ByteArrayOutputStream()
exec {
workingDir '../'
commandLine 'sed', '-n', "/${placeholder}=/p", 'keys.local'
standardOutput = stdout
}
return stdout.toString().replace("${placeholder}=", '').replace('\n', '')
}
//Here we add injectKeys as the first task in the build process
project.afterEvaluate {
preBuild.dependsOn injectKeys
}
//Here we call the method that resets the keys once the build process is completed
gradle.buildFinished {
resetKeys()
}

In the code you’ll see a brief description about each method/task/variable and the part it takes in the process.

Now, on the build.gradle file located in the same folder we need to add one line:

apply from: 'keysScript.gradle'

add it as the last apply in the list as we don’t want to create any conflict with previous scripts.

If you run the sync you’ll see that now the task injectKeys appears first on the list.

And that’s all, you have a script that will replace a placeholder in any file with the value you want to keep secured outside of the repository.

--

--

Alejandro Moya

Mobile developer, Father, geek, this is my first attempt at having a blog, be kind 😄