Ways to Secure Android Secret Keys
As an Android developer, it’s important to secure our sensitive information like API keys, Base URLs, and other sensitive information from hackers or prying eyes who want to reverse-engineer the app. These keys can be used to authenticate, and authorize access to various services.
In this post, we will explore all of the ways and some techniques to secure data.
Storing Keys in strings.xml File
This string.xml file is not the preferred way to use sensitive information, but it is a very common method to store keys and we often use this a lot. The drawback of using that it can be easily accessible after reverse engineering.
<resources>
<string name="api_key">your_api_key</string>
</resources>
Storing Keys As Constants
Another way to store and declare sensitive information is in a Constant file. This is a lot easier way to access, but it will cost us a lot. Java/Kotlin classes can easily be decompiled and your information is gone TADA.
const val API_KEY = "your_api_key"
Storing Keys in the gradle.properties
One effective method to secure our data is to utilize the gradle.properties file. This file serves as a storage option within our Android project and ensures that sensitive information, such as API keys, is not exposed in the version control system by just adding this file to the .gitignore file. This information can be accessed only after a successful build.
Open the gradle.properties
file in a text editor or directly within Android Studio which is usually located in the root directory of the project.
Add keys like API_KEY="YOUR_API_KEY".
We can define multiple keys by adding additional lines in the same format.
By storing our API keys in the gradle.properties
file, we separate them from our source code, reducing the risk of exposing sensitive information. This approach allows us to easily manage and update our keys without modifying the codebase.
To access these keys we need to define buildConfigField("String", "API_KEY”, "YOUR_API_KEY"
in an android block. This code creates a build configuration field named API_KEY
of type String
and assigns the value stored in the gradle.properties
file. Sync your project and access these keys in your code by
val API_KEY = BuildConfig.API_KEY
We often assume that this is a safe and secure way to hide secrets from intruders, but actually, it’s not. It’s the same as insecure as other methods like strings.xml
or constants
.
Storing API Keys in the local.properties File
This way same goes for gradle.properties and by adding this file into the .gitignore file keys are not exposed in your version control system.
Open the local.properties
file in a text editor or directly within Android Studio which is usually located in the root directory of the project.
Add keys like API_KEY="YOUR_API_KEY".
You can define multiple keys by adding additional lines in the same format. To configure these keys to buildConfigureFields, Just add the following code in android-DefaultConfig
android {
// Other configurations...
defaultConfig {
// Get the API keys from local.properties
Properties properties = new Properties()
properties.load(project.rootProject.file("local.properties").newDataInputStream())
// Set API keys in BuildConfig
buildConfigField "String", "API_KEY", "\"${properties.getProperty("API_KEY")}\""
}
// Other configurations...
}
And to access the MAP_KEY
in Manifest
manifestPlaceholders["MAPS_API_KEY"] = properties.getProperty("MAPS_API_KEY")
Storing Keys Natively with C++
As per previous ways, we can explore that our classes can be easily decompiled by reverse engineering. For that, Android provides us another way to secure your keys by implementing C/C++ native code which is secure and safer than the previous ways.
To implement this, First, we need to install NDK (Native Development Kit) which allows C/C++ code to work with Android Studio and CMake to build C/C++ code.
Now, create a package named cpp
in App’s src > main >
and create a C++ file with the name native-lib.cpp
.
Now we have created the file, add the following code snippet to that cpp
file.
#include <jni.h>
#include <string>
extern "C" JNIEXPORT jstringJNICALL
Java_com_package_name_YourKeys_apiKey(JNIEnv *env, jobject object) {
std::string api_key = "your_api_key";
return env->NewStringUTF(api_key.c_str());
}
Here com_package_name
represents the package name of our app.
Note that instead of the dots, add the package name with an underscore like com.package.name
to com_package_name
.
_YourKeys
next to the package name represents the name of the file which allows us to access the API key in Java/Kotlin files from C++ code and _apiKey
is the name of the function which will return the actual API key to make it accessible in our App.
Now let’s create the YourKeys.kt
file, and write the following code
object YourKeys {
init {
System.loadLibrary("native-lib")
}
external fun apiKey(): String
}
That code snippet is going to be used to access the keys throughout the application. Note that the apiKey
method doesn’t have any method definition. The external
modifier makes sure that this method’s definition is brought from the native C++ code through the native-lib.cpp
file.
You can add more keys and sensitive information by adding more methods in the same files YourKeys.kt
and native-lib.cpp.
After that, wherever we like to access this information we can easily access it just by adding this line YourKeys.apiKey()
.
And that’s it TADA. Now we can feel like our API keys and sensitive pieces of information are safe and can not be easily decompiled by reverse engineering the APK.
If you have any questions or feedback, please feel free to contact me.
Connect with me on Linkedin.
Best Regards.
Comments
Post a Comment