使用 Android NDK 保护 API 密钥
作为 Android 开发人员,我们创建 Android 应用程序并依赖各种第三方库(SDK 工具),因为我们不能自己做所有事情。您可能必须使用 Google 地图或其他 Google Play 服务。由于这些服务中的大多数都是付费的,因此在我们的应用程序代码中保护这些库的 API 密钥变得至关重要。你们中的一些人本可以使用 XML 或 Gradle 来保护您的 API 密钥,但逆向工程可能很容易揭示它们。因此,在本博客中,您将了解如何使用 Android 原生开发工具包来保护 API 密钥。我们将整篇文章组织成以下主题,以便于理解:
- 当前问题的建议解决方案
- 使用 Android 实现这一目标的最佳方法是什么? (以三种不同的方式)
- 最后的想法
即使您使用 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 密钥的选项:
- 使用 Native C++ 模板是一个很好的入门方法。
- 使用 CMake 制作项目
- 使用 ndk-build 命令
- LLBD: Android Studio 使用它来调试项目中的本机代码。
- 原生开发工具包 (NDK):原生开发工具包 (NDK) 用于使用 C 和 C++ 进行编程,它们是 Android 的原生语言。
- CMake是一个开源系统,无论操作系统或编译器如何,它都可以管理构建过程。
- 现在我们已经完成了工具的下载,让我们继续讨论保护 API 密钥的方法。
使用 Native C++ 模板是入门的好方法
我们现在在最新版本的 Android Studio 中支持原生代码,即 C 和 C++。要在您的项目中包含 Native C++,请按照以下步骤操作:
第1步:
在 Android Studio 中,使用 Native C++ 模板创建一个新项目,如下所示:
第2步:
填写项目的详细信息,然后单击下一步。
第 3 步:
您会注意到 native-lib.cpp 文件和 CMakeLists.txt 文件已默认添加到您的项目的 cpp 目录中。
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 is a tool that allows you to control the software compilation process using simple platform and compiler-independent configuration files, as well as generate native makefiles and workspaces for usage in your preferred compiler environment.
使用 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++ 模板。