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);