java - 在 SecretKey 上调用 .getEncoded() 返回 null

标签 java android encryption realm

我使用以下代码生成 AES key :

KeyGenParameterSpec.Builder builder = new KeyGenParameterSpec.Builder("db_enc_key", KeyProperties.PURPOSE_ENCRYPT | KeyProperties.PURPOSE_DECRYPT);

        KeyGenParameterSpec keySpec = builder
                .setKeySize(256)
                .setBlockModes("CBC")
                .setEncryptionPaddings("PKCS7Padding")
                .setRandomizedEncryptionRequired(true)
                .setUserAuthenticationRequired(true)
                .setUserAuthenticationValidityDurationSeconds(5 * 60)
                .build();

        KeyGenerator keyGen = KeyGenerator.getInstance("AES", "AndroidKeyStore");
        keyGen.init(keySpec);

        SecretKey sk = keyGen.generateKey();

但每次我尝试通过 sk.getEncoded() 获取 key 的 byte[] 版本时,该方法都会返回 null。文档说它应该返回编码后的 key ,如果 key 不支持编码,则返回 null,但我认为 key 不支持编码。

我需要 byte[] 因为我想加密一个 Realm 数据库(为此我需要将 2 个 AES-256 key 组合为字节数组)[ https://realm.io/docs/java/latest/#encryption]

官方文档使用 SecureRandom,但也指出这是一种愚蠢的做法,而且 key 永远不会被存储。因此,我想使用 KeyStore 来安全地存储两个单独的 AES-256 key 。

P.S.:代码只是测试代码,不是最终产品,所以对编码风格的任何评论都是无用的。我目前只是想让一个工作版本继续运行。

编辑:所以我尝试了下面的代码,它成功地生成了一个 AES key (虽然只有 16 个字节的长度):

SecretKey sk1 = KeyGenerator.getInstance("AES").generateKey();

当我对它使用 getEncoded() 方法时,我什至会得到字节数组,所以我自然而然地继续使用以下代码将它保存到 KeyStore:

KeyStore.SecretKeyEntry entry = new KeyStore.SecretKeyEntry(sk1);
KeyStore.ProtectionParameter pp = new KeyProtection.Builder(KeyProperties.PURPOSE_DECRYPT | KeyProperties.PURPOSE_ENCRYPT).build();
keyStore.setEntry("db_enc_key_test", entry, pp);

这也有效。因此,我尝试通过 KeyStore.Entry entry2 = keyStore.getEntry("db_enc_key_test", null); 从 keystore 中读取 key ,效果也很好。但是当我调用 entry2.getEncoded() 时,该方法再次返回 null。这是 keystore 问题吗?

edit2:所以我刚刚发现,在 keystore 中生成(并显然保存到)的对称 key 在 Android M 中是不可导出的,这似乎是有意为之的,这让我遇到了一些问题,因为我需要 key 本身来加密 Realm 数据库。

这里有一些 Realm 开发人员推荐最佳实践吗?

最佳答案

您无法检索编码 key 这一事实是设计使然,因为 keystore 应该是唯一知道它的人。但是,您可以使用双层 key :

  1. 生成一个随 secret 钥并将其存储在 keystore 中。

  2. 生成 Realm 使用的“真实” key ,并使用 keystore 中的 key 对其进行加密。

  3. 现在您有一些完全随机的文本,可以存储在例如 SharedPreferences 或磁盘文件中。

  4. 每当人们想要打开 Realm 时,读取磁盘上的加密 key ,使用 Keystore 对其进行解密,现在您就可以使用它来打开 Realm。

这里的这个 repo 使用相同的技术以安全的方式保存用户数据:https://github.com/realm/realm-android-user-store

这可能是您要学习的类(class):https://github.com/realm/realm-android-user-store/blob/master/app/src/main/java/io/realm/android/CipherClient.java它还通过各种 Android 版本处理回退(Keystore 有很多怪癖)。

关于java - 在 SecretKey 上调用 .getEncoded() 返回 null,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/42348944/

相关文章:

Java Mail 方法尚未实现 mimemessage

java - Kotlin调用提供的Unit类型的参数

java - 计算二维数组中的特定条件

android - 为什么对于某些 recyclerview 项目,textview ellipsize 检查在 recyclerview 滚动期间返回 true,即使它是 false?

android - 加密从移动应用程序发送的数据,这可能吗?

java - 使用 Jackson 反序列化递归 Map<String,Object> 后避免类型安全警告

android - 如何在Android中使用自定义日历 View 以及如何设置提醒提醒?

android - 如果有人知道你的 SHA-1 证书指纹 :, "dangerous"是怎么回事?

python - Xor 加密/解密 Python 2.7.5

php - 在 PHP 中解密 A256GCM 加密的 JWT