如何保护 Android 应用程序的 API 密钥
保护 Android 应用程序的 API 密钥可以确保用户信息的隐私并防止对 API 的未授权访问。这一点很重要,每个应用程序都应该确保其 API 密钥的安全性,以防止服务被滥用。
注意: 黑客总是有办法对应用程序进行逆向工程并获取密钥,我们只能尽最大努力增加他们的难度。
这里展示两种简单且有效方法来最大限度地保护存储在应用程序中的密钥。
第一种方法:使用 Properties 文件
1. 将所有密钥存储在 Properties 文件中。创建一个 "app.properties" 文件,其中 "app" 可以是任何名称,比如 "sagar.properties":
API_KEY = "1234567890"
API_KEY_PROD = "12345678901"
- 将此文件粘贴在项目的顶层,可以在那里看到 gradle.properties 文件。
- 现在,转到模块级 build.gradle 文件并访问此 Properties 文件:
val localProps = Properties()
val localPropertiesFile = File(rootProject.rootDir,"sagar.properties")
if (localPropertiesFile.exists() && localPropertiesFile.isFile) {
localPropertiesFile.inputStream().use {
localProps.load(it)
}
}
注意:这里使用的是 Kotlin Gradle 文件。
- 通常在顶层创建这个对象,即在 android 块之外。这样可以在 buildTypes 和 productFlavours 块中轻松访问 localProps。
- 主要目标是在 Kotlin 代码中使用我们定义的密钥,而不是在 Gradle 文件中,那么应该如何做呢?
- 在 Android 中,可以生成一些名为 BuildConfig 或 String Resource 文件的文件,可以在 Kotlin 代码或清单中使用它们。
首先启用这些生成功能:
buildFeatures {
...
buildConfig = true
resValues = true // (本例中不需要) 仅当您想为 Res 文件生成内容时使用
}
- 将为每个 productFlavour 或 buildType 定义需要在生成的 BuildConfig 文件中出现的特定密钥:
buildTypes {
release {
...
buildConfigField("String", "API_KEY", localProps.getProperty("API_KEY_PROD"))// 如果有的话
}
debug {
...
buildConfigField("String", "API_KEY", localProps.getProperty("API_KEY"))
}
}
- 通过这个 buildConfigField 方法,向 BuildConfig 文件添加了一个 String 变量 API_KEY。
- 现在,清理并重新构建项目。生成的文件夹包含一个 BuildConfig 文件,可以在模块代码的任何位置访问它:
public final class BuildConfig { // 生成的文件
...
// 来自构建类型:debug
public static final String API_KEY = "1234567890";
}
使用示例:
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
BuildConfig.API_KEY // 这样使用
}
第二种方法:使用 CMake
- 安装 NDK 和 CMake 转到 Android Studio 设置并下载相关项目。
- 安装 CMake Highlighter
- 在 local.properties 文件中添加 NDK 位置(ndk.dir 已弃用,因此您也可以跳过此步骤):
sdk.dir=C\:\\Users\\aaa\\AppData\\Local\\Android\\Sdk
ndk.dir=C\:\\Users\\aaa\\AppData\\Local\\Android\\Sdk\\ndk\\28.0.12433566
// 指定您的位置和版本号,您可以从相同路径的文件资源管理器中看到
- 在应用程序目录中创建 "cpp" 文件夹。转到项目视图,右键单击应用程序目录,创建新目录并将其命名为 cpp。
- 创建 CMakeLists.txt 文件 右键单击 cpp 文件夹 -> New -> File -> CMakeLists.txt 确保给出准确的名称。
- 在 CMakeLists 中添加以下代码:
cmake_minimum_required(VERSION 3.30.5) // 定义您的 CMake 版本
add_library(
native-lib
SHARED
libnative-lib.cpp
)
这是 C++ 代码和 Kotlin/JAVA 代码之间的桥梁,表示 native-lib 是可以在 JAVA 代码中访问的共享库名称,而 "libnative-lib.cpp" 是被共享的文件名。
- 现在需要创建这个 "libnative-lib.cpp" 文件来编写一些 C++ 代码。在同一个 cpp 文件夹中创建此文件。
- 现在,在其中编写 C++ 代码。如果您熟悉 C++,这很容易,这里是一个模板和解释:
#include <jni.h>
#include <string>
std::string getData(int x) { // 根据参数获取密钥的函数
std::string app_secret = "Null";
if (x == 1) app_secret = "123456789";
if (x == 2) app_secret = "abcdefg";
// 要保护的参数数量可以增加。
return app_secret;
}
extern "C" jstring
Java_com_sagar_demo_MainActivity_getApiKey(
JNIEnv *env,
jobject /* this */,
jint id
) {
std::string app_secret = "Null";
app_secret = getData(id);
return env->NewStringUTF(app_secret.c_str());
}
这里需要注意的重要事项是函数名 Java_com_sagar_demo_MainActivity_getApiKey。此函数的名称应与您想要访问该函数的文件的名称完全相同。在包名为 com.sagar.demo 的 MainActivity 中使用名为 getApiKey 的函数访问该函数。因此,它以 "Java" 为前缀按相同顺序排列:Java_com_sagar_demo_MainActivity_getApiKey。
- 在模块的 build.gradle 中添加以下代码(在 android 块内):
externalNativeBuild {
cmake {
path = File("cpp","CMakeLists.txt")
version = "3.30.5"
}
}
android.ndkVersion = "28.0.12433566"
- 在添加 build.gradle 配置后,将能够在 MainActivity 中访问该函数。
在 MainActivity 中添加:
companion object {
init {
System.loadLibrary("native-lib")// 加载库
}
}
private external fun getApiKey(id: Int): String //实现将来自 C++ 文件
//添加您在 C++ 文件中创建的更多函数。
- 现在,将能够像使用普通 Kotlin/Java 函数一样使用 getApiKey 函数。