gpt4 book ai didi

android - 将 AES 与 AndroidKeyStore 结合使用

转载 作者:太空狗 更新时间:2023-10-29 13:16:15 25 4
gpt4 key购买 nike

Android M 通过 AndroidKeyStore 提供 AES 支持,但是我找不到生成 key 的任何组合,该 key 提供了一种无需用户密码/设备锁定的完全加密和解密方法。看来我目前的方法适合这些要求,因为 keystore 正在存储我的 key ,我可以加载 key 并执行加密,如果我从加密过程中保留 IV,我可以解密数据。

不幸的是,在现实世界的用例中,我无法在以后不将其写入磁盘的情况下保留 IV 进行解密,也许这就是我应该做的?

我浏览了 SDK 中更新的 key 存储和相关测试,但找不到任何可以用作示例的测试用例。这些示例似乎也没有实际使用 AndroidKeyStore 生成的 SecretKeys 而没有将它们绑定(bind)到设备锁定/指纹。

我已经创建了一个存储库来尝试突出显示我所做的事情以及一些解释我的问题所在的评论。相关代码也包含在下面。

为清楚起见,我的问题只是如何生成一个 AndroidKeyStore 支持的 AES SecretKey,它允许我通过密码输入/输出流加密和解密,而无需将 IV 写入磁盘或使用指纹/设备锁定方法?

https://github.com/ToxicBakery/AES-Testing/blob/master/app/src/androidTest/java/com/toxicbakery/app/aes/AesTest.java

    final String suchAlphabet = "abcdefghijklmnopqrstuvwxyz";

KeyStore keyStore = KeyStore.getInstance(ANDROID_KEY_STORE);
keyStore.load(null);

/*
KEY GENERATION
*/

// Define the key spec
KeyGenParameterSpec aesSpec = new KeyGenParameterSpec.Builder(ALIAS, KeyProperties.PURPOSE_ENCRYPT | KeyProperties.PURPOSE_DECRYPT)
.setBlockModes(KeyProperties.BLOCK_MODE_CBC)
.setEncryptionPaddings(KeyProperties.ENCRYPTION_PADDING_PKCS7)
.setKeySize(128)
.build();

// Create the secret key in the key store
KeyGenerator keyGenerator = KeyGenerator.getInstance(KeyProperties.KEY_ALGORITHM_AES, ANDROID_KEY_STORE);
keyGenerator.init(aesSpec);
keyGenerator.generateKey();

Cipher cipher;
SecretKey secretKey;

/*
ENCRYPTION
*/

// Load the secret key and encrypt
secretKey = ((KeyStore.SecretKeyEntry) keyStore.getEntry(ALIAS, null)).getSecretKey();
cipher = Cipher.getInstance(TRANSFORMATION);
cipher.init(Cipher.ENCRYPT_MODE, secretKey);

ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
CipherOutputStream cipherOutputStream = new CipherOutputStream(outputStream, cipher);
cipherOutputStream.write(suchAlphabet.getBytes());
cipherOutputStream.flush();
cipherOutputStream.close();

/*
DECRYPTION
*/

// Load the secret key and decrypt
secretKey = ((KeyStore.SecretKeyEntry) keyStore.getEntry(ALIAS, null)).getSecretKey();

// The following two lines attempt to represent real world usage in that the previous line loaded
// the key from the store and the next two lines attempt to create the cipher and then initialize
// the cipher such that an IV can be extracted as it does not seem that you can use the spec or the
// parameters. Interestingly, the following two lines only 'half' such that a-p fail to decrypt and
// q-z decrypt successfully 100% of the time. Leaving the lines commented results an in a successful
// decryption of the alphabet but this is not a usable scenario
//
// cipher = Cipher.getInstance("AES/CBC/PKCS7Padding");
// cipher.init(Cipher.ENCRYPT_MODE, secretKey);

IvParameterSpec ivParameterSpec = new IvParameterSpec(cipher.getIV());
cipher = Cipher.getInstance(TRANSFORMATION);
cipher.init(Cipher.DECRYPT_MODE, secretKey, ivParameterSpec);

byte[] in = new byte[suchAlphabet.getBytes().length];
ByteArrayInputStream inputStream = new ByteArrayInputStream(outputStream.toByteArray());
CipherInputStream cipherInputStream = new CipherInputStream(inputStream, cipher);
IOUtils.readFully(cipherInputStream, in);
cipherInputStream.close();

/*
VERIFY
*/

String muchWow = new String(in);
assertEquals(suchAlphabet, muchWow);

最佳答案

上面 Alex 的回答是最好的,但请注意还有另一种选择:自己将 IV 设置为固定值,或者您可以始终推导出的值。使用相同的 IV 多次加密给定的数据是不安全的,因此 AndroidKeyStore 不鼓励这样做。但是,如果您确定要这样做,可以使用 setRandomizedEncryptionRequired,例如:

KeyGenParameterSpec aesSpec = new KeyGenParameterSpec.Builder(ALIAS, //...
// ...
.setRandomizedEncryptionRequired(false)
.build()

允许您提供 IV,然后在 Cipher init 调用中您可以添加第三个参数,一个 IvParameterSpec 对象。例如:

cipher.init(Cipher.ENCRYPT_MODE, secretKey, new IvParameterSpec(myIv));

解密也一样,使用相同的IV值,就能正确解密。

重申一下,不推荐这种方法。除非您确切地理解为什么这是一个坏主意并且有非常具体的理由知道它在您的情况下没问题,否则最好让 keystore 生成随机 IV。通常不难找到一个地方来存储带有密文的 IV。

关于android - 将 AES 与 AndroidKeyStore 结合使用,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/34440854/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com