📌  相关文章
📜  使用 Android NDK 保护 API 密钥

📅  最后修改于: 2022-05-13 01:58:45.103000             🧑  作者: Mango

使用 Android NDK 保护 API 密钥

作为 Android 开发人员,我们创建 Android 应用程序并依赖各种第三方库(SDK 工具),因为我们不能自己做所有事情。您可能必须使用 Google 地图或其他 Google Play 服务。由于这些服务中的大多数都是付费的,因此在我们的应用程序代码中保护这些库的 API 密钥变得至关重要。你们中的一些人本可以使用 XML 或 Gradle 来保护您的 API 密钥,但逆向工程可能很容易揭示它们。因此,在本博客中,您将了解如何使用 Android 原生开发工具包来保护 API 密钥。我们将整篇文章组织成以下主题,以便于理解:

  1. 当前问题的建议解决方案
  2. 使用 Android 实现这一目标的最佳方法是什么? (以三种不同的方式)
  3. 最后的想法

即使您使用 Android NDK 技术来保护 API 密钥,您仍然可以通过逆向工程获得它们,但关键是我们只是添加了额外的层来保护 API 密钥,因为有总比没有好,而且我们有一切。

当前问题

我们在开发 Android 应用程序时使用了各种第三方库来加快开发过程。我们只是根据自己的需求调用这些库中的一些函数,不关心这些函数的代码。但是,为了调用这些函数,我们需要每个用户唯一的 API 密钥。其中一些库是付费的,如果您的 API 密钥被盗,您可能会面临高昂的支付成本和一系列其他问题。当我们第一次开始学习 Android 开发时,我们曾经将我们的 API 密钥放在字符串.xml 或 gradle 文件中。


    SomeSecureAPI
    YOUR_AWESOME_KEY

这种策略的困难在于任何人都可以对 API 密钥进行逆向工程。使用的另一种方法是 gradle 文件方法。在 gradle.properties 文件中,我们添加 API 密钥如下:

# the gradle.properties file

# Your API Key
someAPIKey = "YOUR_API_KEY"

然后应将 API 密钥作为 buildConfigField 导入 build.gradle 文件中。

buildTypes {
    debug {
        buildConfigField 'String', "someAPIKey", someAPIKey
        resValue 'string', "YOUR_API_KEY", someAPIKey
    }
}

但是,通过对代码进行逆向工程,任何人都可以获得 API 密钥。结果,这两种技术都未能成功保护 API 密钥。我们需要一种可以使用的具体方法,这样即使在对代码进行逆向工程之后,也没有人可以访问所需的 API 密钥。让我们共同努力寻找解决方案。

解决方案建议

在上一节中,我们讨论了这个问题。那么,我们应该怎么做才能确保即使经过逆向工程也没有人可以获取API密钥呢?在我们的应用程序代码中使用本地语言是解决上述困难的一种方法。 Android Native Development Kit (NDK) 将使用本地语言(如 C 或 C++)编写的代码转换为 a.so 文件。这样做的好处是它包含二进制数,即 0 和 1。所以,即使经过逆向工程,你最终会得到 0 和 1,而且不可能弄清楚 0 和 1 中写的是什么。当然有从 0 和 1 获取代码的方法,但正如我之前所说,我们只是在代码中添加了更多的安全层。

使用 Android 实现这一目标的最佳方法是什么? (以三种不同的方式)

借助 Android Native Development Kit,我们可以在 Android (NDK) 中支持原生语言。 JNI(Java Native Interface)也可以在 Android 上使用。 JNI 为从Java或 Kotlin 代码创建的字节码与本地代码(例如 C 或 C++ 代码)交互指定了一种方式。因此,我们可以使用 Android NDK 保护 API 密钥。根据我们使用的 Android Studio 版本,我们提供了三种保护 API 密钥的选项:

  1. 使用 Native C++ 模板是一个很好的入门方法。
  2. 使用 CMake 制作项目
  3. 使用 ndk-build 命令

图片#1: android studio 插件

  1. LLBD: Android Studio 使用它来调试项目中的本机代码。
  2. 原生开发工具包 (NDK):原生开发工具包 (NDK) 用于使用 C 和 C++ 进行编程,它们是 Android 的原生语言。
  3. CMake是一个开源系统,无论操作系统或编译器如何,它都可以管理构建过程。
  4. 现在我们已经完成了工具的下载,让我们继续讨论保护 API 密钥的方法。

使用 Native C++ 模板是入门的好方法

我们现在在最新版本的 Android Studio 中支持原生代码,即 C 和 C++。要在您的项目中包含 Native C++,请按照以下步骤操作:

第1步:

在 Android Studio 中,使用 Native C++ 模板创建一个新项目,如下所示:

图片#2:选择模板

第2步:

填写项目的详细信息,然后单击下一步。

图片#3:选择事物

第 3 步:

您会注意到 native-lib.cpp 文件和 CMakeLists.txt 文件已默认添加到您的项目的 cpp 目录中。

图片#4 :发短信

native-lib.cpp 文件是您的 API 密钥的存储位置。您可以按以下格式将 API 密钥添加到文件中:

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

以下是相关代码的描述:

在这种情况下,您必须使用 PackageName ActivityName MethodName 的组合。上述例子中,包名是com.geeksforgeeks.application,文件名是APIKeyLibrary,从原生代码中获取API密钥的方法是getAPIKey。您可以直接返回 API 密钥,但是,大多数人使用某种加密机制来加密 API 密钥。 UTF-8 编码是通过 NewStringUTF()函数完成的。

initialize {
        mySystem.loadLibrary("native-lib")
}

第4步:

创建一个与本机函数。

external fun getTheAPI(): String

步骤#5:

最后,您可以通过拨打以下号码获取API密钥:

KeyLib.getTheAPI()

如果您使用的是不支持 Native C++ 模板的旧版 Android Studio,则需要升级。之后,您可以使用 CMake。这两种方法的区别在于,使用 CMake,您必须手动添加文件,而使用前一种方法,所有内容都是自动添加的。

使用 CMake 制作项目

除了使用 Native C++ 模板之外,我们还可以利用 CMake 来保护 API 密钥,这意味着我们可以手动将文件添加到我们的项目中。

使用 CMake 保护 API 密钥的步骤如下:

下载并安装博客的先决条件部分中列出的基本工具。在 app/src/main 目录下创建一个 cpp 目录。在您希望保留 API 密钥的 cpp 目录中创建本机文件。创建一个名为 api-keys.cpp 的文件并将以下代码粘贴到其中

C++
#include 
#include 
extern "C" JNIEXPORT jstring CALLJNI
Java_com_geeksforgeeks_application_APIKeyLibrary_getAPIKey(JNIEnv* env, jobject /* this is it */) {
    std::string api_key = "YOUR_KEY_API";
    return env->NewStringUTF(api_key.c_str());
}


C++
#include 
// AI Key First
JNIEXPORT jstring JNICALL
Java_com_mindorks_myapplication_APIKeyLibrary_getAPIKey(JNIEnv *env, jobject instance) {
    return (*env)->  NewStringUTF(env, "YOUR_KEY");
}


在您的 app/ 目录中创建一个名为 CMakeLists.txt 的文本文件。这是 CMake 的构建脚本。用下列信息填空:

# 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.
# You can define multiple libraries, and CMake builds them for you.
# Gradle automatically packages shared libraries with your APK.

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

# Sets the library as a shared library.
        SHARED

# Provides a relative path to your 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, you only need to specify the name of the public NDK library
# you 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
# you want CMake to locate.
        log )

# Specifies libraries CMake should link to your target library. You
# can link multiple libraries, such as libraries you 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} 

现在必须在 build.gradle 文件中指定 CMakeLists 文件的位置。因此,在 build.gradle 文件中,添加以下代码:

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

您现在可以从您的活动或文件中访问 API 密钥,方法是重复之前方法中的步骤(步骤 5、6 和 7),即本机 C++ 模板方法。如果您的 Android Studio 版本不支持 CMake,您还可以使用 NDK 加密您的 API 密钥。您可以在此处使用 ndk-build 过程。让我们看看如何实现这一点。

使用 ndk-build 命令

Android Studio 使用 ndk-build 命令编译项目中的原生代码。您将拥有一个 Android.mk 构建文件,ndk-build 将使用该文件。 Android.mk 文件可以在 jni/ 文件夹中找到。它告诉构建系统你的源代码和共享库。 Android.mk 文件的主要函数是声明构建系统、Application.mk 或环境变量未涵盖的项目范围设置。 Android.mk 文件的语法还允许您将源代码组织到模块中。

LOCAL_PATH := $(call dir_mine)

include $(CLEAR_VARS)

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

include $(BUILD_SHARED_LIBRARY)

在 app/src/main 目录中创建一个名为 jni 的目录。我们的.mk 文件和本机代码文件将存储在这里。在您在上一步中创建的 jni 目录中创建一个名为 Android.mk 的文件,并向其中添加以下代码行: 源文件的位置由 LOCAL PATH 变量指示。构建系统提供了一个名为 my-dir 的宏函数,它返回当前目录。

  • CLEAR VARS 为您清除各种 LOCAL XXX 变量,例如 LOCAL MODULE、LOCAL SRC FILES 等。本地路径未清除。
  • 您要构建的模块的名称存储在 LOCAL MODULE 变量中。模块名称必须唯一,名称中不得使用空格。
  • 在模块中找到的 C 或 C++ 文件列表存储在 LOCAL SRC FILES 变量中。
  • 变量 BUILD SHARED LIBRARY 用于连接所有内容。

在 jni 目录中,创建一个名为 Application.mk 的新文件,并将以下代码粘贴到其中:

APP_ABI := all

ndk-build 的项目范围选项在 Application.mk 文件中指定。变量 APP ABI 用于指定构建系统应为其生成代码的 ABI。默认情况下,构建系统会为所有未弃用的 ABI 构建代码。最后但同样重要的是,将您的本机代码文件放在 jni 目录中。因此,在 jni 目录中创建一个名为 api-keys.c 的文件,并将以下代码粘贴到其中:

C++

#include 
// AI Key First
JNIEXPORT jstring JNICALL
Java_com_mindorks_myapplication_APIKeyLibrary_getAPIKey(JNIEnv *env, jobject instance) {
    return (*env)->  NewStringUTF(env, "YOUR_KEY");
}

我们的下一个目标是在将基本文件添加到您的 jni 目录之后,在 build.gradle 文件中指定我们的 Android.mk 文件的位置。

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

结论

我们在本文中学习了如何使用 Android Native Development Kit 来加密我们的 API 密钥。我们在项目中观察到了三种方法:ndk-build 技术、CMake 方法和最简单的方法,即使用本机 C++ 模板。