pal*_*ogt 5 security android encryption-symmetric
我想使用Android 密钥库对大型(多 MB)数据文件进行对称 AES 加密。
我已经编写了演示代码,可以使用密钥库加密/解密多 KB 文件,但是当文件大小变得太大时,它就会开始下降。此最大大小因设备而异,范围可以从 ~80KB 到 ~1MB。在我测试过的每台 Android-M 设备(包括模拟器)上,似乎都有一个最大大小,超过该大小后加密就会失败。
当它失败时,它会默默地失败——但是密文大小通常比应有的小很多(当然不能解密)。
由于它在多个设备上如此普遍,要么我做错了什么(可能!),要么对密钥库中可以加密的内容存在某种未记录的限制。
我在 Github 上编写了一个演示应用程序来显示问题(这里,特别是这个文件)。您可以运行应用程序 GUI 来手动解决问题,或者运行仪器测试来解决问题。
任何有关此问题的帮助或文档指示将不胜感激!
作为参考,我正在生成这样的对称密钥:
KeyGenerator keyGenerator = KeyGenerator.getInstance(KeyProperties.KEY_ALGORITHM_AES, "AndroidKeyStore");
keyGenerator.init(
new KeyGenParameterSpec.Builder(KEY_ALIAS, KeyProperties.PURPOSE_ENCRYPT|KeyProperties.PURPOSE_DECRYPT)
.setBlockModes(KeyProperties.BLOCK_MODE_GCM)
.setEncryptionPaddings(KeyProperties.ENCRYPTION_PADDING_NONE)
.build()
);
SecretKey key = keyGenerator.generateKey();
SecretKeyFactory factory = SecretKeyFactory.getInstance(key.getAlgorithm(), "AndroidKeyStore");
KeyInfo keyInfo= (KeyInfo)factory.getKeySpec(key, KeyInfo.class);
logger.debug("isInsideSecureHardware: {}", keyInfo.isInsideSecureHardware());
Run Code Online (Sandbox Code Playgroud)
我这样加密:
KeyStore keyStore= KeyStore.getInstance("AndroidKeyStore");
keyStore.load(null);
KeyStore.SecretKeyEntry keyEntry= (KeyStore.SecretKeyEntry)keyStore.getEntry(KEY_ALIAS, null);
Cipher cipher= getCipher();
cipher.init(Cipher.ENCRYPT_MODE, keyEntry.getSecretKey());
GCMParameterSpec params= cipher.getParameters().getParameterSpec(GCMParameterSpec.class);
ByteArrayOutputStream byteStream= new ByteArrayOutputStream();
DataOutputStream dataStream= new DataOutputStream(byteStream);
dataStream.writeInt(params.getTLen());
byte[] iv= params.getIV();
dataStream.writeInt(iv.length);
dataStream.write(iv);
dataStream.write(cipher.doFinal(plaintext));
Run Code Online (Sandbox Code Playgroud)
更新:
根据 的建议user2481360,Artjom B.我更改为在明文进入密码时对其进行分块,如下所示:
ByteArrayInputStream plaintextStream= new ByteArrayInputStream(plaintext);
final int chunkSize= 4*1024;
byte[] buffer= new byte[chunkSize];
while (plaintextStream.available() > chunkSize) {
int readBytes= plaintextStream.read(buffer);
byte[] ciphertextChunk= cipher.update(buffer, 0, readBytes);
dataStream.write(ciphertextChunk);
}
int readBytes= plaintextStream.read(buffer);
byte[] ciphertextChunk= cipher.doFinal(buffer, 0, readBytes);
dataStream.write(ciphertextChunk);
Run Code Online (Sandbox Code Playgroud)
这似乎解决了密文完全错误的问题。我现在可以使用非常大的纯文本大小。
但是,根据大小,有时数据不会往返。例如,如果我使用 1MB,则往返明文在末尾会丢失几个字节。但如果我用1MB+1B就可以了。我的理解AES/GCM是输入明文不必具有特殊的大小(与块长度对齐等)。
小智 1
将数据切分成小块然后调用update,当到达最后一块时调用doFinal