Android M通过AndroidKeyStore提供AES支持但是我找不到生成密钥的任何组合,该密钥提供完全加密和解密的方法而无需用户密码/设备锁定。我的当前方法似乎适合这些要求,因为密钥存储区正在存储我的密钥,我可以加载密钥并执行加密,如果我从加密过程中保留IV,我可以解密数据。
不幸的是,在现实世界的用例中,我无法在以后将其写入IV以进行解密,而无需将其写入磁盘,也许这就是我应该做的事情?
我已经浏览了SDK中更新的密钥库和相关测试,但无法找到我可以用作示例的任何测试用例。这些示例似乎也没有实际使用AndroidKeyStore生成的SecretKeys而不将它们绑定到设备锁定/指纹。
我已经创建了一个存储库来尝试突出我所做的工作以及一些解释我的问题所在位置的注释。相关代码也包含在下面。
为清楚起见,我的问题是如何生成AndroidKeyStore支持的AES SecretKey,它允许我通过密码输入/输出流加密和解密,而无需将IV写入磁盘或使用指纹/设备锁定方法?
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);
答案 0 :(得分:1)
在密文旁边保存IV,然后使用IV解密密文。为什么在你的真实场景中你不能持久地保存IV?
答案 1 :(得分:1)
亚历克斯上面的回答是最好的,但请注意还有另一种选择:将自己设置为固定值,或者可以持续推导出的值。使用相同的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值的解密执行相同操作,并且它将正确解密。
重申一下,这种方法 NOT WOMMENDED 。除非你明白为什么这是一个坏主意并且有非常具体的理由知道它在你的情况下没问题,否则最好让密钥库生成一个随机的IV。通常不难找到用密文存储IV的地方。