The 2 Best ways to secure your API KEYS in Android Projects

The 2 Best ways to secure your API KEYS in Android Projects

Protecting your Android app’s API keys ensures the privacy of user information and secures any unauthorized access to your APIs, this is important and every application should make sure to secure their API keys to prevent any misuse of their services.

NOTE: There are always some ways a hacker can reverse engineer your app and get access to your keys, we can only try our best to make it harder for them.

I will show you two easy and best ways to maximize the security of the keys you store in your Application.

First Approach: Using Properties File

  1. We will be storing all our secret keys in a Properties file.

  2. Create an “app.properties” file, where this “app” can be any name you want, suppose “sagar.properties”.

API_KEY = "1234567890"
API_KEY_PROD = "12345678901"

3. Paste this file at the top level of your project, where you can also see the gradle.properties file.

4. Now, go to your Module level build.gradle file and access this Properties file.

val localProps = Properties()
val localPropertiesFile = File(rootProject.rootDir,"sagar.properties")
if (localPropertiesFile.exists() && localPropertiesFile.isFile) {
    localPropertiesFile.inputStream().use {
        localProps.load(it)
    }
}

Note that I am using the Kotlin Gradle file.

5. I generally like to create this object in the top-level ,that is outside of the android block. So, that I can easily access this localProps in my buildTypes and productFlavours block.

6. Our main goal is to use our defined keys in the Koltin code, not in the Gradle file, so how should we do it?

7. In Android, we have the option to generate some files named BuildConfig or String Resource files, which you can use in your Kotlin Code or your Manifest.

7.2. First enable these features of Generated things…

buildFeatures {
    ...
    buildConfig = true
    resValues = true // (Not required in our case) Only If you wanna generate anything for Res files 
}

8. So, we will define the specific keys for each productFlavour or buildType that we need to be present in this generated BuildConfig file.

buildTypes {
    release {
        ...
        buildConfigField("String", "API_KEY", localProps.getProperty("API_KEY_PROD"))// IF any
    }
    debug {
        ...
        buildConfigField("String", "API_KEY", localProps.getProperty("API_KEY"))
    }
}

9. With this buildConfigField method(resValue method for Res files), we added a String Variable “API_KEY” to our BuildConfig file.

10. Now, clean and rebuild your project. You’ll see a BuildConfig file in the generated folder that you can access anywhere in your module code.

public final class BuildConfig { // Generated file
  ...
  // Field from build type: debug
  public static final String API_KEY = "1234567890";
}
override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    BuildConfig.API_KEY // USe like this
}

Second Approach: Using CMake

  1. Install NDK and CMake
    Go to Android Studio Settings and download the following items…

2. Install a CMake Highlighter

3. Add your NDK location in the local.properties file:


sdk.dir=C\:\\Users\\sagar\\AppData\\Local\\Android\\Sdk 
ndk.dir=C\:\\Users\\sagar\\AppData\\Local\\Android\\Sdk\\ndk\\28.0.12433566
// Specify your location and version no., you can see that from your File explorer in the same path.

4. Create a “cpp” Folder in your app directory.

Go to project view, Right-Click on the app directory, create a new directory, and name it cpp.

5. Create a CMakeLists.txt file
Right-click on the cpp folder -> New -> File -> CMakeLists.txt
Make sure to give the exact name.

6. Add the following code in your CMakeLists:

cmake_minimum_required(VERSION 3.30.5) // Define your CMake version

add_library(

    native-lib

    SHARED

    libnative-lib.cpp

)

This is a bridge between your C++ code and your Kotlin/JAVA code, that says native-lib is the shared library name you can access in your JAVA code and “libnative-lib.cpp” is the file name that is being shared.

7. So, now we need to make this “libnative-lib.cpp” file to write some C++ code.
Create this file in the same cpp folder.

8. Now, write your C++ code in it. It is easy if you are familiar with C++, but let me give you a template and explain it…

#include <jni.h>
#include <string>

std::string getData(int x) { // Some function to get the key depends on the parameter
    std::string app_secret = "Null";

    if (x == 1) app_secret = "123456789";
    if (x == 2) app_secret = "abcdefg";

    // The number of parameters to be protected can be increased.

    return app_secret;
}

extern "C" jstring
Java_com_sagar_demo_MainActivity_getApiKey(
        JNIEnv *env,
        jobject /* this */,
        jint id
) {
    std::string app_secret = "Null";
    app_secret = getData(id);
    return env->NewStringUTF(app_secret.c_str());
}

The important thing to notice here is the function named “Java_com_sagar_demo_MainActivity_getApiKey”.
This function should have the exact same name as the File where you want the function to be accessible.
I want the function to be accessed with the function named “getApiKey” which will be in “MainActivity” with the package as “com.sagar.demo”.
So, its arranged in the same order with “Java” as prefix.
“Java_com_sagar_demo_MainActivity_getApiKey”

9. Add following code in the build.gradle of your module(inside android block) where you want to get the keys:

externalNativeBuild {
    cmake {
        path = File("cpp","CMakeLists.txt")
        version = "3.30.5"
    }
}
android.ndkVersion = "28.0.12433566"

10. After adding the configuration in the build.gradle, we will be able to access the function in our MainActivity.

Add this in your MainActivity:

companion object{
        init {
            System.loadLibrary("native-lib")// Load the library
        }
    }

private external fun getApiKey(id: Int): String //The implementation will be from C++ file
//Add more functions that you created in C++ file.

11. Now, you will be able to use the getApiKey function as a normal Kotlin/Java function.

Follow “Sagar Malhotra for more such content.