在SecretKey上调用.getEncoded()会返回null [英] Calling .getEncoded() on SecretKey returns null

查看:3281
本文介绍了在SecretKey上调用.getEncoded()会返回null的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

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

but everytime I try to get the byte[] version of the key via sk.getEncoded(), the method returns null. The documentation says that it should return the encoded key, or null if the key does not support encoding, but I don't think that the key doesn't support encoding.

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

I need the byte[] because I want to encrypt a realm database (for which I need to combine 2 AES-256 keys as byte-arrays) [https://realm.io/docs/java/latest/#encryption]

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

The official documentation uses SecureRandom, but also states that this is a silly way of doing this and that the key is never stored. Therefore, I wanted to use the KeyStore to securely store the two separate AES-256 keys.

PS:代码只是一个测试代码而不是最终产品,所以对编码风格的任何评论都是无用的。我目前只是试图让一个正在运行的版本。

P.S.: The code is only a test code and not the final product, so any comment on coding style is useless. I'm currently just trying to get a working version going.

编辑:所以我尝试了以下代码,它成功生成了一个AES密钥(尽管只有16个字节的长度):

edit: So I tried the following code, which successfully generates an AES key (though only 16 bytes of length):

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

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

When I use the getEncoded() method on it, I'll even get the byte array, so naturally I went on and saved it to the KeyStore with the following code:

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。这是一个密钥库问题吗?

Which also works. So I tried to read the key from the keystore via KeyStore.Entry entry2 = keyStore.getEntry("db_enc_key_test", null); which worked as well. But when I call entry2.getEncoded() the method returns null again. Is this a keystore problem?

edit2:我刚刚发现,在密钥库中生成(并显然保存到)密钥库的对称密钥在Android M中是不可移植的,这似乎是因为我需要密钥本身来加密领域数据库,所以我觉得这有点问题。

edit2: So I just found out, that symmetric keys generated in (and apparently saved to) the keystore are unexportable in Android M, which seems to be intended, which puts me in a bit of a problem, as I need the key itself to encrypt the realm database.

这里有一些领域的开发者推荐一个最好的-practice?

Some realm-developer here to recommend a best-practice?

推荐答案

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

That you cannot retrieve the encoded key is by design as the Keystore should be the only one knowing it. However you can use a double layered key:

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

1) Generate a random key and store it in the Keystore.

2)生成Realm使用的真实密钥,并使用密钥库中的密钥加密它。

2) Generate the "real" key used by Realm and encrypt it using the key from the Keystore.

3)现在你有一些完全随机的文本可以是存储在例如SharedPreferences或磁盘上的文件中。

3) Now you have some completely random text that can be stored in e.g SharedPreferences or in a file on disk.

4)每当人们想要打开Realm时,读取磁盘上的加密密钥,使用密钥库解密它,现在你可以用它打开领域。

4) Whenever people wants to open the Realm, read the encrypted key on disk, decrypt it using the Keystore and now you can use it to open the Realm.

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

This repo here uses the same technique to save User data in a secure way: 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有很多怪癖)。

This is probably the class you are after: https://github.com/realm/realm-android-user-store/blob/master/app/src/main/java/io/realm/android/CipherClient.java It also handle fallback through the various Android versions (the Keystore has quite a few quirks).

这篇关于在SecretKey上调用.getEncoded()会返回null的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆