【问题标题】:Store Client Certificate and key (.pem) in Android securely在 Android 中安全地存储客户端证书和密钥 (.pem)
【发布时间】:2020-09-24 10:47:25
【问题描述】:

我正在开发一个供内部使用的 Android 应用程序,用于向我们的服务器发送测试请求。身份验证是通过基本 + 客户端证书。我现在正在做的是将证书和密钥文件存储在资产/证书中,通过输入获取密钥和基本身份验证的密码,然后保存在 SharedPreferences 中。 这可能不安全。我最关心的是两个 .pem 文件。我读了很多关于将它们保存在 Android Keystore 中的内容,但对此不太确定。是否可以“安装”证书并在之后删除 pem 文件? 在寻找解决方案时,我发现大部分时间都是应用程序自行生成证书的问题。但是我有两个 .pem 文件,需要使用它们来发送请求。

将证书存储在资产中,我担心它会被逆向工程。

现在的示例代码:

SecurityContext context = SecurityContext.defaultContext;
ByteData cert = await rootBundle.load("assets/certs/xy.cert.pem");
context.useCertificateChainBytes(cert.buffer.asUint8List());
ByteData key = await rootBundle.load("assets/certs/xy.key.pem");
final storage = FlutterSecureStorage();
String passphrase = await storage.read(key: "passphrase");
String basic = await storage.read(key: "basic");
context.usePrivateKeyBytes(key.buffer.asUint8List(), password:passphrase);
HttpClient client = new HttpClient(context: context);
var uri = "https://url.com";
var method = 'POST';
var request = await client.openUrl(method,Uri.parse(uri));
request.headers.set('Content-Type', 'application/json; charset=utf-8');
request.headers.set("Authorization", basic);

工作正常,但我想确保它不能从其他应用程序中提取。

编辑:

可能应该提到它: 我正在使用颤振,但不知何故并没有让它变得更容易。

【问题讨论】:

  • Keystore 是官方推荐的方式!
  • 我忘了说,我正在使用颤振。我可能看不到它,但是根据我的研究,除了使用仅支持字符串键值对的flutter_secure_storage之外,我找不到任何在密钥库中存储某些内容的示例实现。所以我必须从证书 ByteData 中获取一个字符串。我不是安全专家,因此不确定这是否仍然安全。

标签: android security certificate


【解决方案1】:

ANDROID 共享首选项或 ANDROID KEYSTORE?

我现在正在做的是将证书和密钥文件存储在资产/证书中,通过输入获取密钥和基本身份验证的密码,然后保存在 SharedPreferences 中。这可能不安全。

是的,如果您不加密它们,则根本不安全。

Android Shared Preferences

SharedPreferences 对象指向包含键值对的文件,并提供简单的方法来读取和写入它们。每个 SharedPreferences 文件都由框架管理,可以是私有的,也可以是共享的。

使用 Android 密钥库加密共享首选项

我最关心的是这两个 .pem 文件。我阅读了很多关于将它们保存在 Android 密钥库中的信息,但对此不太确定

要加密共享首选项,我们需要使用 EncryptedSharedPreferences 类并将加密密钥存储在 Android 密钥库中,正如您已经阅读的那样。

Android Hardware-backed Keystore

片上系统 (SoC) 中可信执行环境的可用性为 Android 设备提供了一个机会,可以为 Android 操作系统、平台服务甚至第三方应用提供硬件支持的强大安全服务.

为了帮助您更安全地处理数据,Android 为您提供了Security Library:

安全库提供与读取和写入静态数据以及密钥创建和验证相关的安全最佳实践的实现。

来自 Android 文档的示例:

他们只提供Kotlin 或Java 中的示例,没有Flutter,因此我在这里复制粘贴Kotlin 的示例。

写:

// Although you can define your own key generation parameter specification, it's
// recommended that you use the value specified here.
val keyGenParameterSpec = MasterKeys.AES256_GCM_SPEC
val masterKeyAlias = MasterKeys.getOrCreate(keyGenParameterSpec)

// Creates a file with this name, or replaces an existing file
// that has the same name. Note that the file name cannot contain
// path separators.
val fileToWrite = "my_sensitive_data.txt"
val encryptedFile = EncryptedFile.Builder(
    File(DIRECTORY, fileToWrite),
    context,
    masterKeyAlias,
    EncryptedFile.FileEncryptionScheme.AES256_GCM_HKDF_4KB
).build()

val fileContent = "MY SUPER-SECRET INFORMATION"
        .toByteArray(StandardCharsets.UTF_8))
encryptedFile.openFileOutput().apply {
    write(fileContent)
    flush()
    close()
}

阅读

// Although you can define your own key generation parameter specification, it's
// recommended that you use the value specified here.
val keyGenParameterSpec = MasterKeys.AES256_GCM_SPEC
val masterKeyAlias = MasterKeys.getOrCreate(keyGenParameterSpec)

val context = applicationContext
val fileToRead = "my_sensitive_data.txt"
val encryptedFile = EncryptedFile.Builder(
    File(DIRECTORY, fileToRead),
    context,
    masterKeyAlias,
    EncryptedFile.FileEncryptionScheme.AES256_GCM_HKDF_4KB
).build()

val inputStream = encryptedFile.openFileInput()
val byteArrayOutputStream = ByteArrayOutputStream()
var nextByte: Int = inputStream.read()
while (nextByte != -1) {
    byteArrayOutputStream.write(nextByte)
    nextByte = inputStream.read()
}

val plaintext: ByteArray = byteArrayOutputStream.toByteArray()

逆向工程

将证书存储在资产中,我担心它会被逆向工程。

无论你如何存储它们,它们都可以被逆向工程,即使使用我上面提到的方法。

在这种情况下,攻击者将求助于使用检测框架并在运行时挂钩到检索和解密 pem 证书的代码,然后将它们提取到他的命令和控制服务器,然后他可以从那里自动攻击您的后端。

Instrumentation 框架的一个示例是Frida:

将您自己的脚本注入黑盒进程。挂钩任何功能、监视加密 API 或跟踪私有应用程序代码,无需源代码。编辑,点击保存,立即查看结果。所有这些都无需编译步骤或程序重新启动。

请记住,您希望在移动应用中保密的任何内容实际上都是公开的,因为它只需要攻击者使用大量开源工具来简化此任务。

可能的解决方案

工作正常,但我想确保它不能从其他应用程序中提取。

正如您已经意识到从您的移动应用程序中提取 pem 文件并非不可能,您只能让它变得更加困难。

您的问题的一个可能解决方案是让您的后端知道他何时可以高度信任请求确实来自您的移动应用程序,从而允许它拒绝从您的移动应用程序中提取的 pem 文件的请求从其他地方使用。

我建议您阅读我针对问题提出的answer如何保护移动应用的 API REST?。

我要求您首先了解 谁 与 什么 访问您的后端之间的区别,因为这是开发人员之间的常见误解,导致他们相信用户身份验证足以证明 what 正在向后端发出请求,而实际上它只是证明 谁 是请求中的用户。话虽如此,我仍然建议使用足以满足移动应用用例及其用户群的强大用户身份验证解决方案。

在您清楚了解该差异后,您可以继续阅读其余答案,看看保护 API 服务器部分中的建议是否足够,或者您需要选择移动设备应用证明概念,如可能的更好解决方案部分所述。

您想加倍努力吗?

在回答安全问题时,我总是喜欢参考 OWASP 基金会的宝贵工作:)

对于移动应用

OWASP Mobile Security Project - Top 10 risks

OWASP 移动安全项目是一个集中资源,旨在为开发人员和安全团队提供构建和维护安全移动应用程序所需的资源。通过该项目,我们的目标是对移动安全风险进行分类并提供开发控制以减少其影响或被利用的可能性。

OWASP - Mobile Security Testing Guide:

移动安全测试指南 (MSTG) 是移动应用安全开发、测试和逆向工程的综合手册。

对于 APIS

OWASP API Security Top 10

OWASP API 安全项目旨在通过强调不安全 API 的潜在风险并说明如何降低这些风险,为软件开发人员和安全评估人员提供价值。为了实现这一目标,OWASP API 安全项目将创建和维护一份 API 安全风险前 10 名文档,以及一个文档门户,用于在创建或评估 API 时提供最佳实践。

【讨论】:

    【解决方案2】:

    这确实是一个配置问题。您真的不希望您的所有应用程序都使用相同的密钥进行身份验证——一旦泄露,所有应用程序安装都会受到损害。

    如果您真的想使用基于证书的身份验证,您最好在首次使用时生成一个密钥/证书并使用它。不过说起来容易做起来,所以如果您真的需要强身份验证,您可能需要考虑使用 Android 中可用的 FIDO2 API 之类的东西。

    【讨论】:

      猜你喜欢
      • 2021-02-09
      • 2015-04-20
      • 2015-11-24
      • 1970-01-01
      • 2021-12-02
      • 1970-01-01
      • 2012-08-26
      • 1970-01-01
      • 2015-07-21
      相关资源
      最近更新 更多