Skip to content

Instantly share code, notes, and snippets.

@vxhviet
Last active January 22, 2021 10:25
Show Gist options
  • Save vxhviet/794100b0417b34524dd54aff9e4dac04 to your computer and use it in GitHub Desktop.
Save vxhviet/794100b0417b34524dd54aff9e4dac04 to your computer and use it in GitHub Desktop.

Android - How To Protect API Keys From Decompiled APK

  • Background Knowledge:

SOURCE, SOURCE, SOURCE

  • Some Libraries:

SOURCE (untested), SOURCE (can't add to gradle), SOURCE

There are many ways to do this, some common way is to add the keys/secret to properties or xml resource files and read it via Gradle or Resource. Rememer to gitignore from version control so we don't push it to remote repo.

However, these methods are not enough to protect against decompiled APK so another method is to put those keys/ secret in native code which will make it harder for attacker to reverse engineer our app.

Remember, this method is still not enough, a combination of ProGuard/ DexGuard, encrypt/ decrypt keys/ secret in native code, fetching keys/ secret from server, using Android Keystore, etc. is recommended to fully protect our app.

Prerequisites

In your Android Studio, click on Tools > SDK Manager > SDK Tools.

Select LLBD, NDK, and CMake.

Click on Apply and after downloading and installing click on OK.

Using the Native C++ template

In the latest versions of Android Studio, we have support for native code i.e. for the C and C++. Follow the below steps to add Native C++ in your project:

Step1: Create a new project in Android studio with Native C++ template.

Step2: Add the details of the project and click on Next.

Step3: Keep the settings to default and click on Finish.

Step4: You can find that by default you will be having native-lib.cpp file and the CMakeLists.txt file added to your project under the cpp directory.

The native-lib.cpp file is the file that contains your API keys. You can add your API keys in the file as below:

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

extern "C" JNIEXPORT jstring JNICALL
Java_com_example_weatherforecast_repository_ForecastRepository_getAppID(
        JNIEnv* env,
        jobject /* this */) {
    std::string app_id = "60c6fbeb4b93ac653c492ba806fc346d";
    return env->NewStringUTF(app_id.c_str());
}

the format is:

Package_ClassName_MethodName

Step5: To call this message in any class, do this:

class ForecastRepository(private val database: ForecastDatabase) : BaseRepository() {
    init {
        System.loadLibrary("native-lib")
    }

    private external fun getAppID(): String
 
  ...
  
  fun getSomethingFromNetwork() {
    ...
    
    response = apiService.getDailyForecastForCity(
                    input,
                    10,
                    getAppID(),
                    "Metric"
                )
    ...
  }
}

To call it from companion object add @JvmStatic to the function SOURCE:

class ForecastDatabase : RoomDatabase() {
    companion object {
        init {
            System.loadLibrary("native-lib")
        }

        @JvmStatic
        private external fun getDBPass(): String

        fun getDatabase(context: Context): ForecastDatabase {
            ....
            val pass = getDBPass()
        }
    }
}

To verify if the compiled APK have our native libs, do this: SOURCE

  • Select Build > Build Bundles(s) / APK(s) > Build APK(s)
  • Select Build > Analyze APK.
  • Select the APK from the app/build/outputs/apk/ directory and click OK.
  • As shown in figure 3, you can see libnative-lib.so in the APK Analyzer window under lib/<ABI>/.

For other method using CMake or ndk-build see the Source Article.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment