Securing API Keys using Android NDK

Abhinav Tyagi
4 min readOct 3, 2016

**UPDATE**

check the following link first for better solution — https://developer.android.com/training/articles/keystore

Today there are thousands of third-party libraries and APIs available to developers for creating cool, amazing mobile apps. These APIs are not only rich in features, but they also speed up the development process. With great feature sets, many such APIs comes with subscription plans and can be accessed using API keys that we get during the subscription process. The API publishers earn money based on the number of times the API is accessed. To track the access, API keys are passed with every server request. So, sometimes, these are stored or hardcoded in the code.

Now, for the subscriber, this can be potentially less secure as someone may reverse engineer the application build and fetch these API keys. Losing keys not only affect the billing for the API access but may also lead to privacy issues with the user base of the app.

There are different ways in which API keys can be kept hidden. I will show one such way of storing keys in the native C/C++ class and accessing them in our Java classes.

Please note that this is not full proof and you can still extract the keys. However, this will add an extra layer of obfuscation to your keys.

Steps:

  1. Create a folder “jni” under src/main
create jni folder under src/main

2. Create and add “Android.mk” file under “jni” folder with the following content:

LOCAL_PATH := $(call my-dir)

include $(CLEAR_VARS)

LOCAL_MODULE := keys
LOCAL_SRC_FILES := keys.c

include $(BUILD_SHARED_LIBRARY)

Here LOCAL_MODULE = “keys” is the name of the library with which you want to access it inside your java code and LOCAL_SRC_FILES = “keys.c” is the source file for your native C code. More details can be found at Android.mk

3. Create and add “Application.mk” file under “jni” folder with the following content:

APP_ABI := all

This is for setting different Application Binary Interface or ABI. In the above case, it is set for all types. More details can be found at Application.mk

4. Create the C/C++ file “keys.c” and add it under “jni” folder. Add the following content to it:

#include <jni.h>

JNIEXPORT jstring JNICALL
Java_com_tyagiabhinav_hellosecretkeys_MainActivity_getNativeKey1(JNIEnv *env, jobject instance) {

return (*env)-> NewStringUTF(env, "TmF0aXZlNWVjcmV0UEBzc3cwcmQx");
}

JNIEXPORT jstring JNICALL
Java_com_tyagiabhinav_hellosecretkeys_MainActivity_getNativeKey2(JNIEnv *env, jobject instance) {

return (*env)->NewStringUTF(env, "TmF0aXZlNWVjcmV0UEBzc3cwcmQy");
}

Here, “Java_com_tyagiabhinav_hellosecretkeys_MainActivity_getNativeKey1” represents the Java code with package name “com.tyagiabhinav.hellosecretkeys” followed by Activity name “MainActivity” where we want to call the native function and the static method “getNativeKey1” to fetch the API key from the native function. In the above code, I have encoded the actual API key using Base64 encoding.

5. The file structure should now look like this:

Android.mk, Application.mk and keys.c file structure

6. In the Activity where you want to access the keys (in our case MainActivity), create a static block and load the library “keys” like:

static {
System.loadLibrary("keys");
}

7. Declare two member functions of type “native” to access the keys from the C/C++ file. Since we have stored 2 keys, we will declare 2 functions:

public native String getNativeKey1();
public native String getNativeKey2();

8. For the demo, access the keys in the code like:

String key1 = new String(Base64.decode(getNativeKey1(),Base64.DEFAULT));
String key2 = new String(Base64.decode(getNativeKey2(),Base64.DEFAULT));

((TextView)findViewById(R.id.key)).setText("Key1-->"+key1+"\nKey2-->"+key2);

9. Now, our C/C++ native files and Java code are ready. But to compile or make the native build using NDK, we need to add entry into the gradle file:

android {
.....
buildTypes {
.....
}
externalNativeBuild {
ndkBuild {
path 'src/main/jni/Android.mk'
}
}
}

We need to provide the path for our “Android.mk” file.

10. Now, sync and build the project. Make sure, you have pointed the NDK path correctly in your module settings.

11. On the successful run you will see the decoded keys from the Native C/C++ code as:

Decoded keys from Native C/C++ code

Please refer to the GitHub project for the demo app.

***** UPDATE *****

In the newer updates, Android Studio can automatically create the above steps and help you obfuscate your Keys.

  1. While creating a new project simply enable “include C++ support”

2. Keep C++ related stuff to default

3. Open the pre-written C++ class “native-lib.cpp” and rename the method to “getNativeKey” or your own preferred name and enter the Base64 encoded key. You can also have some sort of encryption added to it.

4. Open the Activity class and call the “getNativeKey” method and fetch the key. Make sure to decode/decrypt it.

The app Gradle file has the following default entries:

The GitHub project has been updated and tested for those who are still using older implementations.

--

--

Abhinav Tyagi

{ “designation”:”Principal Consultant”, “hobby”:[ “click”, “cook”, “colour“, “code”,”DIY” ], “interest”:[ “android”,”bots”,”IoT”, “food”, “future”, “space” ] }