【问题标题】:Calling .getEncoded() on SecretKey returns null在 SecretKey 上调用 .getEncoded() 返回 null
【发布时间】:2017-02-20 15:53:32
【问题描述】:

我使用以下代码生成 AES 密钥:

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() 获取密钥的 byte[] 版本时,该方法都会返回 null。文档说它应该返回编码的密钥,如果密钥不支持编码,则返回 null,但我认为密钥不支持编码。

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

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

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

编辑:所以我尝试了以下代码,它成功生成了一个 AES 密钥(虽然只有 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); 从密钥库中读取密钥,这也很有效。但是当我调用 entry2.getEncoded() 时,该方法再次返回 null。这是密钥库问题吗?

edit2:所以我刚刚发现,在密钥库中生成(并且显然保存到)密钥库中的对称密钥在 Android M 中是不可导出的,这似乎是有意的,这给我带来了一些问题,因为我需要加密领域数据库的密钥。

这里有一些领域开发者推荐最佳实践?

【问题讨论】:

    标签: java android encryption realm


    【解决方案1】:

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

    1) 生成随机密钥并将其存储在 Keystore 中。

    2) 生成 Realm 使用的“真实”密钥,并使用来自 Keystore 的密钥对其进行加密。

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

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

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

    这可能是您追求的课程:https://github.com/realm/realm-android-user-store/blob/master/app/src/main/java/io/realm/android/CipherClient.java 它还可以处理各种 Android 版本的回退(Keystore 有很多怪癖)。

    【讨论】:

    • 是的,这听起来是目前最好的解决方法。谢谢。
    • 您能否详细说明如何“生成 Realm 使用的“真实”密钥并使用密钥库中的密钥对其进行加密”。您是仅使用随机时间戳/字符串还是生成新的/单独的 SecretKey?如果您要生成额外的 SecretKey:您是如何做到的?
    • 你找到答案了吗?他们是怎么做到的?
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2017-08-07
    • 2023-03-29
    • 2023-04-05
    • 2021-02-15
    • 2014-03-09
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多