将客户端证书和密钥(.pem)安全地存储在Android中



我正在开发一个Android应用程序,供内部使用,以向我们的服务器发送测试请求。身份验证是通过基本+客户端证书进行的。我现在正在做的是将证书和密钥文件存储在assets/cert中,通过输入获得密钥的密码短语和基本身份验证,然后保存在SharedPreferences中。这可能不安全。我最关心的是这两个.pem文件。我读了很多关于将它们保存在Android密钥库中的文章,但对此并不确定。之后是否可以"安装"证书并删除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);

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

编辑:

可能应该提一下:我正在处理颤振,但不知何故,这并没有让它变得更容易。

ANDROID共享偏好还是ANDROID密钥存储

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

如果不加密它们,"是"根本不安全。

Android共享偏好

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

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

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

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

安卓硬件支持的Keystore

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

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

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

Android文档中的示例:

他们只提供了KotlinJava中的例子,没有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文件从其他地方使用的请求。

我建议您阅读我对问题的回答如何确保移动应用程序的API REST安全

我要求您首先了解什么正在访问您的后端之间的区别,因为这是开发人员中常见的误解,导致他们认为用户身份验证足以证明是什么/strong>谁是请求中的用户。话虽如此,我仍然建议使用强大的用户身份验证解决方案,这些解决方案足以满足移动应用程序的使用情况和用户基础

在你清楚地意识到这一差异后,你可以继续阅读答案的其余部分,看看保护API服务器部分中的建议是否足够,或者你需要采用可能的更好解决方案中描述的移动应用验证概念。

你想多走一步吗

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

对于移动应用程序

OWASP移动安全项目-十大风险

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

OWASP-移动安全测试指南:

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

对于APIS

OWASP API安全前10名

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

这确实是一个资源调配问题。你并不真的希望你的所有应用程序都用同一个密钥进行身份验证——一旦密钥泄露,所有应用程序的安装都会受到威胁。

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

最新更新