【问题标题】:how to securely store encryption keys in android?如何在android中安全地存储加密密钥?
【发布时间】:2018-03-16 15:46:26
【问题描述】:

我想知道如何在 Android 中安全地存储 加密密钥?保护加密和密钥的最佳方案是什么?

【问题讨论】:

  • 您是否正在考虑对磁盘上的某些内容进行加密,或者加密文件与您的应用程序一起存储?在第一种情况下,在运行时创建密钥并将其放入 Keystore。在第二种情况下 - 不要将密钥放在应用程序中。最好的办法是从服务器下载它,但这样做仍然会留下一个拦截窗口。两者都确实如此,如果有人足够努力,他们就会得到它。最好的办法是不要在客户端电话上放置您不希望用户看到的任何信息。
  • 请给我一个在密钥库中存储密钥的例子
  • 您是否询问用于向服务器或其他用户发送数据的加密密钥(然后您可以在您的 apk 中安全地存储非对称公钥或下载它),用于加密/解密数据的本地加密密钥在您的设备中,还是要加密与您的服务器的通信通道?
  • 我问的是本地数据加密密钥。
  • 然后使用Android Keystore。它是为此目的而设计的。您在寻找什么问题?教程?一些例子?

标签: java android security encryption keystore


【解决方案1】:

在您的 cmets 中,您需要使用本地密钥对当前 Android 版本和旧版本的数据进行加密

Android Keystore 旨在生成和保护您的密钥。但它不适用于 18 以下的 API 级别,并且在 API 级别 23 之前有一些限制。

您将需要一个随机对称加密密钥,例如 AES。 AES 密钥用于加密和解密您的数据。我将根据 Android API 级别总结您的选项以安全地生成和存储它。

  • API 级别 。向用户请求密码,从密码中导出加密密钥,缺点是应用程序启动时需要提示输入密码。它不存储在设备中的加密密钥。每次使用密码启动应用程序时计算一次

  • API 级别 >=18 。使用默认加密提供程序(不使用 AndroidKeystore)生成随机 AES 密钥。在 Android Keystore 中生成 RSA 密钥对,并使用 RSA 公钥加密 AES 密钥。将加密的 AES 密钥存储到 Android SharedPreferences 中。应用启动时,使用 RSA 私钥解密 AES 密钥

  • API 级别 >=23:支持 AES 的 Android 密钥库。使用 Android Keystore 生成随机 AES 密钥。可以直接使用。

加密可以使用AES/CBC/PKCS7Padding算法。它还需要一个随机初始化向量 (IV) 来加密您的数据,但它可以是公开的。

替代方案:

  • API 级别 >14:Android Key Chain:KeyChain 是系统范围的凭证存储。您可以使用应用程序可以使用的私钥安装证书。如上面第二种情况所示,使用预安装的密钥加密/解密您的 AES 密钥。

  • 外部令牌:受保护的密钥未存储在设备中。您可以使用包含允许您加密 AES 密钥的私钥/公钥对的外部令牌。可以使用蓝牙或 NFC 访问令牌

【讨论】:

  • 感谢分享有用的安卓密码学信息。
  • 感谢您详尽的回答!谈到 API 级别 ">=18
  • @southerton,我认为您可以获得足够安全的 AES 机密,将 HKDF 等密钥派生函数应用于 RSA 密钥或只是对其进行散列(SHA256 将提供 32 个随机字节)。查看这篇帖子的讨论crypto.stackexchange.com/questions/50118/…
  • @pedrofb 查看文档developer.android.com/training/articles/… 我们可以看到 API 23 下不支持“AES/CBC/PKCS7Padding”,那么我们如何在 API 23 下使用它呢?
  • @rahulsahni,AndroidKeyStore 提供程序不支持,但您仍然可以将 AES 与默认加密提供程序一起使用
【解决方案2】:

您不能将加密密钥放在您的 apk 文件中。您可能希望将其保存在远程服务器中并使用服务器解密。或者,您可能会通过对密钥进行编码并将其保存在不明显的位置来使其他人难以理解。但这并没有万无一失的解决方案。

【讨论】:

  • androidKeyStore 呢?
  • 如果加密密钥可以是运行时生成的随机String,可以使用KeyStore。但如果持久存储,则可以从具有 root 访问权限的设备读取。
【解决方案3】:

没有办法将您的私有 api 密钥安全地保存到代码中。但是你可以使用 NDK 安全地保存私钥。从 NDK 获取密钥并非易事。 Secue Key With NDK Example

【讨论】:

  • C++ 不能被反编译,但可以被反汇编,这比 java 略逊一筹。它也不安全:(
【解决方案4】:

听起来你想要EncryptedSharedPreferencesEncryptedFile。这两个都使用AndroidKeyStore。下面的代码 sn-ps 实际上回答了“如何使用 AndroidKeystore 加密文件或存储加密密钥?”的问题

确保在您的应用程序build.gradle 文件中包含implementation "androidx.security:security-crypto:1.0.0-rc02"

来自EncryptedSharedPreferences

加密密钥和值的SharedPreferences 的实现。

String masterKeyAlias = MasterKeys.getOrCreate(MasterKeys.AES256_GCM_SPEC);

SharedPreferences sharedPreferences = EncryptedSharedPreferences.create(
  "secret_shared_prefs",
  masterKeyAlias,
  context,
  EncryptedSharedPreferences.PrefKeyEncryptionScheme.AES256_SIV,
  EncryptedSharedPreferences.PrefValueEncryptionScheme.AES256_GCM
);

// use the shared preferences and editor as you normally would
SharedPreferences.Editor editor = sharedPreferences.edit();

您可以像这样存储加密密钥:

// generate random symmetric key
KeyGenerator keyGen = KeyGenerator.getInstance("AES");
keyGen.init(256);
SecretKey symkey = keyGen.generateKey();


String alias = "your encryption key";

// store symmetric key
byte[] encodedSymmetricKey = symkey.getEncoded();
SharedPreferences.Editor edit = sharedPreferences.edit();
String base64EncodedSymmetricKey = new String(Base64.getEncoder().encode(encodedSymmetricKey));
edit.putString(alias, base64EncodedSymmetricKey);
edit.commit();

// retrieve symmetric key
String raw = sharedPreferences.getString(alias, null);
byte[] symKey = Base64.getDecoder().decode(raw);
SecretKeySpec spec = new SecretKeySpec(symKey, "AES");

assert(spec.equals(symkey));

// use your encryption key

虽然使用EncryptedFile 会好得多。


来自EncryptedFile

用于创建和读取加密文件的类。

String masterKeyAlias = MasterKeys.getOrCreate(MasterKeys.AES256_GCM_SPEC);

File file = new File(context.getFilesDir(), "secret_data");
EncryptedFile encryptedFile = EncryptedFile.Builder(
  file,
  context,
  masterKeyAlias,
  EncryptedFile.FileEncryptionScheme.AES256_GCM_HKDF_4KB
).build();

// write to the encrypted file
FileOutputStream encryptedOutputStream = encryptedFile.openFileOutput();

// read the encrypted file
FileInputStream encryptedInputStream = encryptedFile.openFileInput();

注意:
主密钥一旦创建,就是不变的。因此,即使在手机重启后,您的应用仍然能够加密/解密文件。

【讨论】:

    【解决方案5】:

    您可以使用 Android Keystore 系统来存储和检索敏感信息。 阅读这篇 5 分钟的文章,了解它是如何工作的。 Using the Android Keystore system to store and retrieve sensitive information

    【讨论】:

    • 我可以在哪里保存静脉输液?
    • 据我的经验,你需要有相同的IV才能解密。因此,当加密完成时,您将拥有 IV,将其转换为 Base64 编码并通过放置一些分隔符附加加密数据并存储您的最终加密文本,因此当您解密时,您将获得 Base64 编码的 IV 附加您加密的文本,将其与您在加密期间放置的分隔符进行拆分,您将获得 Base64 编码形式的 IV,对其进行解码并用于解密。
    猜你喜欢
    • 2014-10-18
    • 2015-07-04
    • 2015-11-24
    • 2014-06-24
    • 1970-01-01
    • 1970-01-01
    • 2015-07-21
    • 2012-01-11
    相关资源
    最近更新 更多