How To Secure Secret API Key In Android Studio

Reverse Engineering Is Increasing Day By Day Due To Which Normal Applications Are Not Safe Enough. In This Post , We Will Learn How To Secure Your App's Secret Strings , API From Being Reverse Engineered.


Hide Secret API Key In Android Studio

Generally We Declare API Key In A Variable , Then Call It When It Is Required. But Doing This Makes Easy For Reverse Engineers To Extract The Exact API Key (Or Any Import Data / Strings) Which Any Developer Never Wants. Then The Extracted Detail Can Be Used For Any Wrong Purpose Or One Can Abuse The API Which Can Leads To Down The Service. To Make Sure This Would Never Happen With You , You Need To Hide / Secure Your Important Strings Including API Keys , User , Pass Or Any Secret JSON URL.

Creating A Simple API App In Android Studio

Lets Create An Application Using Android Studio Which Will Show An API String On Button Press . Create An Empty Activity , Name The Project As HideAPIKey , Choose com.nd.hideapikey As Package Name And Select Java As The Language.

Lets Create activity_main.xml As Follows :

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:background="@color/white"
    tools:context=".MainActivity">

    <TextView
        android:id="@+id/textView"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Secure API Key Tutorial\nNarendraDwivedi.Org"
        android:textColor="@color/black"
        android:textSize="30sp"
        android:textStyle="bold"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintHorizontal_bias="0.274"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent"
        app:layout_constraintVertical_bias="0.065" />

    <Button
        android:id="@+id/btnshow"
        android:layout_width="210dp"
        android:layout_height="67dp"
        android:backgroundTint="@color/black"
        android:text="Show API Key"
        android:textColor="@color/white"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintHorizontal_bias="0.124"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toBottomOf="@+id/textView"
        app:layout_constraintVertical_bias="0.168" />

    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Click The Button To Show API"
        android:id="@+id/txtoutput"
        android:textColor="@color/black"
        android:textSize="15sp"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintHorizontal_bias="0.115"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toBottomOf="@+id/btnshow"
        app:layout_constraintVertical_bias="0.122" />


</androidx.constraintlayout.widget.ConstraintLayout>
activity_main.xml

Now Initialize Button & TextView

package com.nd.hideapikey;

import androidx.appcompat.app.AppCompatActivity;

import android.os.Bundle;
import android.widget.Button;
import android.widget.TextView;

public class MainActivity extends AppCompatActivity {
    Button btnshow;
    TextView txtoutput;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        btnshow=findViewById(R.id.btnshow);
        txtoutput=findViewById(R.id.txtoutput);
    }
}
Lets Create A Method Using Which A Sample API Will Be Shown In txtoutput And This Method Will Be Called on Clicking Button btnshow

package com.nd.hideapikey;

import androidx.appcompat.app.AppCompatActivity;

import android.os.Bundle;
import android.view.View;
import android.widget.Button;
import android.widget.TextView;

public class MainActivity extends AppCompatActivity {
    Button btnshow;
    TextView txtoutput;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        btnshow=findViewById(R.id.btnshow);
        txtoutput=findViewById(R.id.txtoutput);

        btnshow.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                showresult();
            }
        });
    }
    public void showresult()
    {
        String apikey="https://link.narendradwivedi.org/";
        txtoutput.setText("The API Key Is "+apikey);
    }
}
Lets Build The App , Install & Run It.

After Clicking The Button , The Application Shows https://link.narendradwivedi.org/ As API Key.

Reverse Engineering Application To Find API Key (Non Secured App)

Now Lets Try To Extract The API Key Of This Application By Reverse Engineering It.


The Application Got Easily Reversed And I Extracted The API Key Very Easily. So Now You May Have Understood That Declaring The Important Secrets (Like API) Without Protecting Is Unsafe. Now Lets Learn How To Secure These Important Secrets.

How To Secure API Key In Java (Android Studio)

To Secure Your API Key (Or Any Other Secrets) , Open The Desired Project In Android Studio. In Our Case We Have Opened This Same Project (HideAPIKey).
Now Make Sure That You Have Installed NDK & CMake. If It Is Not Installed In Your System , Install It From SDK Manager Of Android Studio


Now Change The Project View From Android To Project


Create A New Directory In main And Name It jni [Right Click On main -> New -> Directory]
In jni Directory , We Need To Create Three Files
  • Android.mk [Right Click On main -> New ->File]. Enter Following Code In It And Save It
LOCAL_PATH := $(call my-dir)

include $(CLEAR_VARS)

LOCAL_MODULE := keys
LOCAL_SRC_FILES := keys.c

include $(BUILD_SHARED_LIBRARY)
  • Application.mk [Right Click On main -> New ->File]
APP_ABI := all
  • keys.c [Right Click On main -> New ->File]
#include <jni.h>

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

return (*env)-> NewStringUTF(env, "aHR0cHM6Ly9saW5rLm5hcmVuZHJhZHdpdmVkaS5vcmcv");
}
In keys.c File , Replace com_nd_hideapikey With Your Package Name. Here MainActivity Indicate The Name Of The Activity In Which We Need The API , So Replace It According To Your Project. getAPIKey Is The Name Of The Method We Are Creating Here Which We Will Call To Get The API. Here aHR0cHM6Ly9saW5rLm5hcmVuZHJhZHdpdmVkaS5vcmcv Is The Encrypted Base64 Of Secret API Which We Are Protecting. Go to https://base64.narendradwivedi.org And Enter The Secret String/ API Key Which You Want To Protect And Click On Encode Button. Replace aHR0cHM6Ly9saW5rLm5hcmVuZHJhZHdpdmVkaS5vcmcv With Your Base64



If You Want To Protect Multiple Contents , Then You Have To Create Multiple Methods. You Can Copy The Code From JNIEXPORT ..... Java_com_... newMethod2 ... ..... To return ... } And Can Append In The Above Code To Create New Methods. (Make Sure To Change Method Names )

So Our Project Looks Like This


Lets Again Change The Project View From Project To Android. We Need To Add Some Entries In build.gradle (Module) [ After buildTypes ]

ndkVersion '25.0.8775105'
    externalNativeBuild {
        ndkBuild {
            path 'src/main/jni/Android.mk'
        }
    }
Here We Need To Specify The ndkversion (In My Case , The Version Is 25.0.8775105). Also We Need To Specify The Path Of Android.mk File. If All Cases , The Path Will Be Same If You Have Followed This Tutorial.
To Find Your NDK Version , Open SDK Manager , Go To SDK Tools And Check Show Package Detail Option

NDK Version

In The Same build.gradle (Module) File , Change minifyEnabled From false To true Which Will Minify The Code Resulting Which Understanding The Method , String , Variable ETC Names During Reverse Engineering Will Be Difficult.
Now Sync The Gradle File To Save The Changes.

The Complete Code For build.gradle (Module) File :

plugins {
    id 'com.android.application'
}

android {
    compileSdk 32

    defaultConfig {
        applicationId "com.nd.hideapikey"
        minSdk 21
        targetSdk 32
        versionCode 1
        versionName "1.0"

        testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
    }

    buildTypes {
        release {
            minifyEnabled true
            proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
        }
    }
    
    ndkVersion '25.0.8775105'
    externalNativeBuild {
        ndkBuild {
            path 'src/main/jni/Android.mk'
        }
    }

    compileOptions {
        sourceCompatibility JavaVersion.VERSION_1_8
        targetCompatibility JavaVersion.VERSION_1_8
    }
}

dependencies {

    implementation 'androidx.appcompat:appcompat:1.4.2'
    implementation 'com.google.android.material:material:1.6.1'
    implementation 'androidx.constraintlayout:constraintlayout:2.1.4'
    testImplementation 'junit:junit:4.13.2'
    androidTestImplementation 'androidx.test.ext:junit:1.1.3'
    androidTestImplementation 'androidx.test.espresso:espresso-core:3.4.0'
}

Retrieving API Key In Our Application

Open The Activity In Which We Have To Get The API Key. In Our Case It Is MainActivity.java
Create A Static Block And Load keys Library
 static {
        System.loadLibrary("keys");
    }
After That , We Need To Call The Method Which We Have Created In keys.c File Using Native Function.
public native String getAPIKey();
Now Call getAPIKey() In showresult() Method. The API Key Was Stored In Base64 Format , So Will Need To Decode It.

String apikey = new String(Base64.decode(getAPIKey(),Base64.DEFAULT));
Make Sure To Import Base64 In Project [ Using import android.util.Base64; ]

Complete MainActivity.java Code :

package com.nd.hideapikey;

import androidx.appcompat.app.AppCompatActivity;

import android.os.Bundle;
import android.util.Base64;
import android.view.View;
import android.widget.Button;
import android.widget.TextView;

public class MainActivity extends AppCompatActivity {
    Button btnshow;
    TextView txtoutput;
    static {
        System.loadLibrary("keys");
    }
    public native String getAPIKey();
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        btnshow=findViewById(R.id.btnshow);
        txtoutput=findViewById(R.id.txtoutput);

        btnshow.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                showresult();
            }
        });
    }
    public void showresult()
    {
        String apikey = new String(Base64.decode(getAPIKey(),Base64.DEFAULT));
        txtoutput.setText("The API Key Is "+apikey);
    }
}
Lets Build The App , Install & Run It.


The Application Is Executing Successfully & Providing Correct API Value.

Reverse Engineering Application To Find API Key (Secured App)

After Protecting The API Key , Lets Try To Extract API Key By Reverse Engineering The APK



Here It Is Impossible To Get The API Key. In This Way , We Have Successfully Protected Our Application's Secret API Key.

Download Android Studio Project Of Hiding API Key [Android Studio , JAVA]

Download This Whole Project Discussed In This Post

Download Project

FAQ

Why To Hide API Key

The API Keys Can Be Used To Make Unwanted Changes To Your Account. It Can Also Used To Abuse  [Malicious Massive Request] The Service Of API Key Provider

Can I Use Any Other Name Instead Of jni

No , You Cannot. You Must Have To Use The Directory Name As jni

How To Encode And Decode String As Base64

By Visiting https://base64.narendradwivedi.org , You Can Encode Data As Base64. You Can Also Decode It

Can I Protect String Instead Of API In Android Application

Yes , You Can Protect Any Strings Also By Following The Steps Mentioned In This Post

Conclusion

I Hope You Have Learnt The Method To Protect Android Application's API / String From Being Extracted By Reverse Engineering. If You Have Any Doubt , Comment Below.