Android – How To Secure API Keys Using Android NDK ?

Hello Readers, CoolMonkTechie heartily welcomes you in this article.

In this article, we will learn about how to secure API Keys using the Android Native Development Kit. We will focus the below points to understand the API Keys Security Concepts in Android NDK:

  • What is the current problem during secure API Keys?
  • What is the proposed solution to secure the API Keys?
  • How to do this in Android?

A famous quote about learning is :

The more that you read, the more things you will know. The more that you learn, the more places you’ll go.

So Let’s begin one by one.

Overview

What is the current problem during secure API Keys?

Here First question, What is the current problem during secure API Keys?

The Answer is :

While developing Android applications, we use various third-parties libraries that make the development of our application fast. In these libraries, we just call some functions according to our need and we don’t know the code of these functions and we don’t care about that. But to call these functions, we need API keys that are different for different users. Some of these libraries are paid and if somehow, someone gets our API key then we might land to high payment bills and many other problems. During the starting days to Android development, we put our API keys either in strings.xml file or in gradle file.

strings.xml file

Following is an example of storing an API key in strings.xml file:

<resources>
    <string name="app_name">SecureAPI</string>
    <string name="MyAPIKey">YOUR_API_KEY_HERE</string>
</resources>

The problem with approach is that anyone can get the API key by reverse engineering.

gradle file

Another approach that was used is the gradle file approach. Here we add the API key in the gradle.properties file:

#gradle.properties file

#API Key
APIKey = "YOUR_API_KEY_HERE"

After that, import the API key as buildConfigField in the build.gradle file.

buildTypes {
    debug {
        buildConfigField 'String', "ApiKey", MyAPIKey
        resValue 'string', "api_key", MyAPIKey
    }
    ...
}

But still anyone can get the API key by reverse engineering your code. So, both methods failed to secure the API keys. We need a certain concrete method that can be used so that even after reverse-engineering the code, no one can get the desired API key. 

Cool !! So We understood the problem of secure API keys in Android development.

What is the proposed solution to secure the API Keys ?

So the next question is, What is the proposed solution to secure the API Keys ? And what should we do so that even after reverse engineering no one can get the API key ?

The Answer is :

One solution to the above problem can be the use of native language in our application code. In the native languages like in C or C++, the Android Native Development Kit(NDK) compiles the code to .so file. The benefit with this .so file is that it contains binary numbers i.e. in the form of 0s and 1s. So, even after reverse engineering, we will get 0s and 1s and it is very difficult to identify what is written in the form of 0s and 1s.

Great !!

There are methods to get the code from 0s and 1s also, but as I said earlier, we are just providing some extra security layers to our code.

How to do this in Android ?

So next, we will discuss that How to do this in Android ?

The Answer is :

In Android, we have the support of Native languages with the help of the Android Native Development Kit (NDK). Also, there is JNI (Java Native Interface) in Android. JNI defines a way for the byte code that is generated from the Java or Kotlin code to interact with the native code i.e. code written in C or C++.

Three Approaches to secure our API keys

So, with the help of Android NDK, we can secure the API keys. Based on the versions of Android Studio, we have three approaches to secure our API keys using :

  • The Native C++ template
  • CMake
  • ndk-build

But before moving on to these approaches, there are some prerequisites. So, before moving onto that, we have to download some tools. Follow the below steps:

  1. In Android Studio, click on Tools > SDK Manager > SDK Tools.
  2. Select LLBDNDK, and CMake.
  3. Click on Apply and after downloading and installing click on OK.

  • LLBD: It is used by Android Studio to debug the native code present in the project.
  • NDK: Native Development Kit(NDK) is used to code in C and C++ i.e. native languages for Android.
  • CMake: It is an open-source system that manages the build process in an operating system and a compiler-independent manner.

Now, we have done with downloading the tools, let’s quickly move on to the approaches of securing the API keys.

1. The Native C++ template

In the latest versions of Android Studio, we have support for native code i.e. for the C and C++. 

We can find that by default we will be having native-lib.cpp file and the CMakeLists.txt file added to our project under the cpp directory. The native-lib.cpp file is the file that contains our API keys. We can add our API keys in the file as below:

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

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

Following is the description of the above code:

  • Here, we have to follow the combination of PackageName_ActivityName_MethodName.
  • In the above example, com_coolmonktechie_myapplication is the package name, APIKeyLibrary is the file name and getAPIKey is the method that is used to get the API keys from the native code.
  • We can directly return the API key but normally people use some encryption technique to encrypt the API key. So, the NewStringUTF() method is used to do the UTF-8 encoding.

Now, to use our API key in the Activity or in any file, go to the Activity or the file where we want to use our API keys. To load the native code that we have written, we need to call the System.loadLibrary(“native-lib”) method in the init block.

init {
        System.loadLibrary("native-lib")
}

Now, declare a Kotlin external function with the same name as used in the native code.

external fun getAPIKey(): String

Finally, we can get the API key by calling:

APIKeyLibrary.getAPIKey()

That’s all! We have secured our API key.

2. CMake

Apart from using the Native C++ template, we can use the CMake to secure the API keys.

CMake is used to control the software compilation process using simple platform and compiler independent configuration files, and generate native makefiles and workspaces that can be used in the compiler environment of our choice.

After installing the required tools, Under the app/src/main directory, create a directory named cpp. In the cpp directory, create a native file where we want to store our API keys. So, create a file named api-keys.cpp and add the below code:

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

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

In the app/ directory, we need to add one text file named CMakeLists.txt file. It is a CMake build script. Add the below content into it:

# For more information about using CMake with Android Studio, read the
# documentation: https://d.android.com/studio/projects/add-native-code.html

# Sets the minimum version of CMake required to build the native library.

cmake_minimum_required(VERSION 3.4.1)

# Creates and names a library, sets it as either STATIC
# or SHARED, and provides the relative paths to its source code.
# We can define multiple libraries, and CMake builds them for us.
# Gradle automatically packages shared libraries with our APK.

add_library( # Sets the name of the library.
api-keys

# Sets the library as a shared library.
        SHARED

# Provides a relative path to our source file(s).
src/main/cpp/api-keys.cpp )

# Searches for a specified prebuilt library and stores the path as a
# variable. Because CMake includes system libraries in the search path by
# default, we only need to specify the name of the public NDK library
# we want to add. CMake verifies that the library exists before
# completing its build.

find_library( # Sets the name of the path variable.
log-lib

# Specifies the name of the NDK library that
# we want CMake to locate.
        log )

# Specifies libraries CMake should link to our target library. We
# can link multiple libraries, such as libraries we define in this
# build script, prebuilt third-party libraries, or system libraries.

target_link_libraries( # Specifies the target library.
native-lib

# Links the target library to the log library
# included in the NDK.
        ${log-lib} )

Now, We have to specify the path of our CMakeLists file in the build.gradle file. So, add the below code in build.gradle file:

android {
    ...
    defaultConfig {
        ...
    }
    buildTypes {
        ...
    }
    externalNativeBuild {
        cmake {
            path 'CMakeLists.txt'
        }
    }
}

Now, to access the API keys from our Activity or file.

3. ndk-build

To compile the native code present in the project, Android Studio supports the ndk-build. Here, we will be having an Android.mk build file that is used by ndk-build.

The Android.mk file is present inside the jni/ directory. It describes our sources and shared libraries to the build system. The basic purpose of the Android.mk file is to define project-wide settings that are not defined by the build system or by Application.mk or by environment variables. Also, the syntax of the Android.mk file allows us to group our sources into modules.

To use ndk-build, Under the app/src/main directory, create a directory named jni. Here we will be having our .mk files and the native code file. In the jni directory that we have created in the previous step, add one file named Android.mk and add the below lines of code to it:

LOCAL_PATH := $(call my-dir)

include $(CLEAR_VARS)

LOCAL_MODULE    := api-keys
LOCAL_SRC_FILES := api-keys.c

include $(BUILD_SHARED_LIBRARY)

Following is the description of the above code:

  • The LOCAL_PATH variable indicates the location of the source file. my-dir is a macro function provided by the build system to return the current directory.
  • The CLEAR_VARS is used to clear many LOCAL_XXX variables for us like LOCAL_MODULE, LOCAL_SRC_FILES, etc. It doesn’t clear LOCAL_PATH.
  • The LOCAL_MODULE variable stores the name of the module that we want to build. The module name must be unique and we shouldn’t use any space in the module name.
  • The LOCAL_SRC_FILES variable contains a list of C or C++ files that are present in the module.
  • The BUILD_SHARED_LIBRARY variable is used to tie everything together. It determines what to build, and how to do it. It collects the information that we defined in LOCAL_XXX variables since the most recent include.

In the jni directory, create another file named Application.mk file and add the below code:

APP_ABI := all

The Application.mk file is used to specify the project-wide settings for the ndk-build.

The variable APP_ABI is used to specify the ABIs whose code should be generated by the build system. By default, the build system generates the code for all non-deprecated ABIs.

The last file to be added in the jni directory is our native code file. So, in the jni directory, add one file named api-keys.c and add the below code into it:

#include <jni.h>

//For first API key
JNIEXPORT jstring JNICALL
Java_com_coolmonktechie_myapplication_APIKeyLibrary_getAPIKey(JNIEnv *env, jobject instance) {

    return (*env)->  NewStringUTF(env, "YOUR_API_GOES_HERE");

}

After adding the required files in your jni directory, our next aim is to provide the path of our Android.mk file in the build.gradle file.

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

Now, to access the API keys from your Activity or file.

That’s all about in this article.

Conclusion

In this article, We understood how to secure our API keys with the help of the Android Native Development Kit. We saw three methods of doing so i.e. the ndk-build method, the CMake method and the easiest one i.e. using the native c++ template in our Application project..

Thanks for reading ! I hope you enjoyed and learned about Android Secure API Keys Concepts and Methods. Reading is one thing, but the only way to master it is to do it yourself.

Please follow and subscribe us on this blog and and support us in any way possible. Also like and share the article with others for spread valuable knowledge.

If you have any comments, questions, or think I missed something, feel free to leave them below in the comment box.

Thanks again Reading. HAPPY READING !!???

Loading

Leave a Comment