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

时间:2020-06-05 09:05:07

标签: android security certificate

我正在开发一个供内部使用的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);

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

编辑:

可能应该提到它: 我正在处理颤振,但这并不能使它变得更容易。

2 个答案:

答案 0 :(得分:1)

ANDROID共享的偏好设置或ANDROID密钥存储区?

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

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

Android Shared Preferences

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

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

我主要关心两个.pem文件。我读过很多关于将它们保存在Android Keystore中的信息,但是对此并不太确定

要对共享首选项进行加密,我们需要使用类 EncryptedSharedPreferences 并将加密密钥存储在Android Keystore中,就像您已经了解的那样。

Android Hardware-backed Keystore

片上系统(SoC)中受信任执行环境的可用性为Android设备提供了向Android OS,平台服务甚至第三方应用程序提供硬件支持的强大安全服务的机会。

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

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

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证书的代码中,然后将其提取到其命令和控制服务器,然后他可以从该服务器对您的攻击自动进行攻击后端。

Frida是一种检测框架的示例:

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

请记住,您想要在移动应用程序中保密的任何事情实际上都是公开的,因为它只需要攻击者使用使此任务容易的大量开源工具即可。

可能的解决方案

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

由于您已经意识到自己不可能从移动应用程序中提取pem文件,因此只能使其更加困难。

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

我建议您阅读我对问题如何保护移动应用程序的API REST的answer

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

在您清楚地知道了区别之后,您可以继续阅读其余的答案,并查看保护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安全风险”文档,以及用于创建或评估API的最佳实践的文档门户。

答案 1 :(得分:0)

这实际上是配置问题。您真的不希望所有应用程序都使用相同的密钥进行身份验证-一旦泄漏,所有应用程序的安装都会受到威胁。

如果您确实要使用基于证书的身份验证,则最好在首次使用时生成密钥/证书并使用它。说起来容易些,但是,如果您确实需要强身份验证,那么您可能要考虑使用类似Android的FIDO2 API这样的东西。