Securing API Keys in Android App using NDK (Native Development Kit)
Securing API keys in Android application has become a challenging by traditional methods (storing them in strings.xml or in Gradle etc.), since they can be easily exposed by reverse engineering the app. It may lead to privacy issues and also may affect the billing for third party paid API access.
Though its not possible to fully protect this API keys from reverse engineering but we can make extra layer for security by using NDK in our app.
The NDK (Native Development Kit) allows to write code in C/C++ in our app. And the good news is that ndk libraries can not be decompiled easily which make the APIs harder to find. {Although the NDK compiled code can still be opened with a hexadecimal editor, but by the nature of the API key they are harder to pick out in a hexadecimal editor.}
Today, We are going to store API keys in the native C/C++ class and accessing them in our Java classes. Now Let’s started:
Requirements
To compile and debug native code for our app, we need the following components:
The Android Native Development Kit (NDK):
a set of tools that allows to use C and C++ code with Android.
CMake:
an external build tool that works alongside Gradle to build your native library. You do not need this component if you only plan to use ndk-build.
LLDB:
the debugger Android Studio uses to debug native code.
You can install these components using the SDK Manager:
- From an open project, select Tools > Android > SDK Manager from the menu bar.
- Click the SDK Tools tab.
- Check the boxes next to LLDB, CMake, and NDK, as shown in below figure:
- Click Apply, and then click OK in the pop-up dialog.
- When the installation is complete, click Finish, and then click OK.
Two ways for using NDK: ndk-build, CMake
- NDK is a collection of compilers and libraries that are needed to build C/C++ code for android.
- ndk-build and CMake both use the NDK and solve the same problem.
— — CMake:
- CMake is a new way and the default build tool for using ndk. If we are creating new native library , then we should use CMake.
— — ndk-build:
- Android studio also support this ndk-build(this is a build system included in NDK) due to the large number of existing projects that use the build toolkit to compile their native code.
- It uses
android.mk
files. in other way we can sayandroid.mk
file contains ndk-build. - If you use CMake then you don’t need
Android.mk
instead you will needCMakeList.txt
CMake or ndk-build ???
- If you’re not familiar with either or your project is cross platform, CMake is probably the better choice. Because CMake’s main advantage is that you can use one set of build files for all your targets (Android, Linux, Windows, iOS, etc).
- ndk-build should be preferred if you’re building a project that already uses
Android.mk
files for its build system (legacy projects).
***Securing Keys using ndk-build…(Item-01)
step 01: Create a folder “jni”
under src/main.
step 02: Create a file ‘Android.mk’
under “jni”
folder with following content:
LOCAL_PATH := $(call my-dir)include $(CLEAR_VARS)LOCAL_MODULE := keys
LOCAL_SRC_FILES := keys.cinclude $(BUILD_SHARED_LIBRARY)
step 03: Create another file ‘Application.mk’
file under “jni”
folder with the following content:
APP_ABI := all
Here, This is for setting different Application Binary Interface or ABI. More details can be found at Application.mk.
step 04: Create the C/C++ file “keys.c” under “jni” folder. Add the following content to it:
#include <jni.h>JNIEXPORT jstring JNICALL
Java_com_shishirstudio_ndktest_MainActivity_getFacebookApiKey(JNIEnv *env, jobject instance) { return (*env)-> NewStringUTF(env, "TmF0aXZlNWVjcmV0UEBzc3cwcmQx");
}JNIEXPORT jstring JNICALL
Java_com_shishirstudio_ndktest_MainActivity_getBaseApi(JNIEnv *env, jobject instance) { return (*env)->NewStringUTF(env, "TmF0aXZlNWVjcmV0UEBzc3cwcmQy");
}
Here,
- “Java_com_shishirstudio_ndktest_MainActivity_getFacebookApiKey”: represents the Java code with package name “com.shishirstudio.ndktest” [dot(.) must be replaced with underscore(_)]followed by Activity name “MainActivity” where we want to call the native function and the static method “getFacebookApiKey” to fetch the API key from the native function. So the combination is:{Package Name}_{Activity Name}_{Method Name}
- In the above code, I have encoded the actual API key using Base64 encoding (You can store actual API key) for making another security layer.
step 05: Add a static block and load the library ‘keys’ in the activity where you will access the keys. (In our case in MainActivity).
static {
System.loadLibrary("keys");
}
step 06: We also have to add member functions of type “native” to access the keys from the C/C++ file. Since we stored 2 keys, we will declare 2 functions:
public native String getFacebookApiKey();
public native String getBaseApi();
step 07: Now access the keys in the code like:
String facebookApiKey= new String(Base64.decode(getFacebookApiKey(),Base64.DEFAULT));String baseApi = new String(Base64.decode(getBaseApi(),Base64.DEFAULT));
step 08: Oh ! one more things, to compile or make the native build using NDK, don’t forget to add entry into the gradle file:
android {
.....
buildTypes {
.....
}
externalNativeBuild {
ndkBuild {
path 'src/main/jni/Android.mk'//path of Android.mk file
}
}
}
step 09: Now, sync and build the project. And you will find everything is ok….
***Securing Keys using CMake…(item-02)
step 01: Create a new project or use existing project.
step 02: Create a folder named “cpp” under src/main.
step 03: Create the C/C++ file “native-lib.cpp” under “cpp” folder. And add the following content to it:
#include <jni.h>
#include <string>
extern "C" JNIEXPORT jstring
JNICALL
Java_shishirtstudio_com_ndktes_MainActivity_stringFromJNI(JNIEnv *env, jobject object) {
std::string hello = "Hello from Jni";
return env->NewStringUTF(hello.c_str());
}
Here,
- “Java_com_shishirstudio_ndktest_MainActivity_stringFromJNI”: represents the Java code with package name “com.shishirstudio.ndktest” [dot(.) must be replaced with underscore(_)]followed by Activity name “MainActivity” where we want to call the native function and the static method “stringFromJNI” to fetch the API key from the native function. So the combination is:{Package Name}_{Activity Name}_{Method Name}
Now the directory looks like below…
step 04: Create a CMake build script CMakeLists.txt
under app folder ( This is a plain text file that you must name CMakeLists.txt
) and add the following content to it.
Step 05: 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:
externalNativeBuild {
cmake {
path "CMakeLists.txt"
}
}
Step 06: To use this API follow the step 05,06 and 07 of item-01.
***** The easiest and best way*****
In the newer updates of Android Studio its very easy because everything will be ready for you automatically. Just follow the below step:-
step 01: Enable “include C++ support” while taking a new project in android studio.
step 02: Keep C++ related stuff to default.
3. Yes, everything is ready for you. Just open the pre-written C++ class “native-lib.cpp” and customize as you want.
4. Now load the library with a static block in activity and access the keys ( The same process we did before). That’s it………
Note: Make sure to decode/decrypt it if you stored encrypted key.
…You may Join me on Linkedin, Github and Facebook.
Thanks for reading this article. Don’t forget to give claps in case you find this article helpful.