我使用以下代码生成 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 :
生成一个随 secret 钥并将其存储在 keystore 中。
生成 Realm 使用的“真实” key ,并使用 keystore 中的 key 对其进行加密。
现在您有一些完全随机的文本,可以存储在例如 SharedPreferences 或磁盘文件中。
每当人们想要打开 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/