首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >特定Android设备/操作系统上的增量加密问题

特定Android设备/操作系统上的增量加密问题
EN

Stack Overflow用户
提问于 2020-04-08 10:37:37
回答 2查看 493关注 0票数 3

我正在使用增量加密,结合安卓KeyStore提供商。

代码语言:javascript
复制
val cipher = Cipher.getInstance(TRANSFORMATION)
cipher.init(Cipher.ENCRYPT_MODE, getSecretKey())

val chunks = textToEncrypt.chunked(CHUNK_SIZE)
val encryptedChunks: MutableList<ByteArray?> = mutableListOf()

chunks.forEachIndexed { index, chunk ->
     if (index == chunks.size - 1) {
            encryptedChunks.add(cipher.doFinal(chunk.toByteArray(StandardCharsets.UTF_8)))
     } else {
            encryptedChunks.add(cipher.update(chunk.toByteArray(StandardCharsets.UTF_8)))
     }
}

val result = encryptedChunks.filterNotNull().reduce { acc, item -> acc.plus(item) }

以下是我使用的常量:

代码语言:javascript
复制
const val TRANSFORMATION = "AES/GCM/NoPadding"
const val CHUNK_SIZE = 32768 // 32KiB

现在,这段代码已经在30多个不同的设备上进行了大量测试,除了一部手机(Xperia和Android7.0)之外,没有任何问题。对于这款手机来说,如果输入(textToEncrypt)足够小,可以在一个块中加密所有内容,那么就可以了,但是如果它更大(通常在100 not左右),那么它就无法加密数据。我得到的是:

代码语言:javascript
复制
Caused by javax.crypto.IllegalBlockSizeException
   at android.security.keystore.AndroidKeyStoreCipherSpiBase.engineDoFinal(AndroidKeyStoreCipherSpiBase.java:491)
   at javax.crypto.Cipher.doFinal(Cipher.java:2056)

Caused by android.security.KeyStoreException: Memory allocation failed
   at android.security.KeyStore.getKeyStoreException(KeyStore.java:685)
   at android.security.keystore.KeyStoreCryptoOperationChunkedStreamer.update(KeyStoreCryptoOperationChunkedStreamer.java:132)
   at android.security.keystore.AndroidKeyStoreCipherSpiBase.engineUpdate(AndroidKeyStoreCipherSpiBase.java:338)
   at javax.crypto.Cipher.update(Cipher.java:1683)

注意到:只有对于这个设备,cipher.update()ENCRYPT_MODE返回null,这就是为什么在我的代码中我允许返回null,然后放弃它们来形成加密的数据。这意味着cipher.doFinal应该一次返回整个加密数据。

编辑:所以很显然只有这款手机的块大小不是很好:它不能是32 8Kb,但是8KB工作正常。

EN

回答 2

Stack Overflow用户

发布于 2020-04-16 22:27:09

扩展了奥利维尔的答案。他确定异常是由mMainDataStreamer.update()抛出的。如果您查看AndroidKeyStoreCipherSpiBase类,您将看到mMainDataStreamerKeyStoreCryptoOperationChunkedStreamer类的一个实例。这里有一个有趣的部分:

代码语言:javascript
复制
// Binder buffer is about 1MB, but it's shared between all active transactions of the process.
// Thus, it's safer to use a much smaller upper bound.
private static final int DEFAULT_MAX_CHUNK_SIZE = 64 * 1024;

在我们的示例中,使用了这个默认的最大块大小。DEFAULT_MAX_CHUNK_SIZE为块大小设置了上限。如果将较大的块传递给cipher.update()方法,它们将被切割成块的DEFAULT_MAX_CHUNK_SIZE。正如您所看到的,即使是Android的开发人员也没有新的精确的安全块大小,不得不猜测自己(在您的例子中,失败)。

但是,请注意,粘结剂缓冲区用于将这些块传递给加密过程并从中返回结果。它的尺寸只有1MB左右。

也许这个设备上有一个非常小的粘合剂缓冲器?你可以试着用这个答案来调查它:https://stackoverflow.com/a/25666064/3249257

在未来的设备上,您可以使用:

代码语言:javascript
复制
IBinder.getSuggestedMaxIpcSizeBytes()

https://developer.android.com/reference/android/os/IBinder#getSuggestedMaxIpcSizeBytes()

票数 4
EN

Stack Overflow用户

发布于 2020-04-11 16:16:48

我花了一些时间分析这个问题。我找不到真正的原因,但至少我找到了一些元素,所以它们就在这里。

多部份加工

首先,您说您使用了32 it的块大小,但这并不是真的。将字符串拆分为32 to块(32768 字符),然后将每个块转换为字节数组。由于一个字符的UTF-8表示可以从1到4个字节,所以字节数组通常大于32 of (除非您只有ASCII字符)。

您应该首先将字符串转换为字节数组,然后将其拆分为32 it块。只有这样才能保证传递给密码API的缓冲区的大小。

客户端错误

现在,关于你得到的堆叠痕迹。与乍看之下的情况相反,错误并不发生在doFinal()中,而是发生在update()中。当您调用update()时,调用将被委托给AndroidKeyStoreCipherSpiBase.engineUpdate()。有趣的部分是:

代码语言:javascript
复制
try {
    flushAAD();
    output = mMainDataStreamer.update(input, inputOffset, inputLen);
} catch (KeyStoreException e) {
    mCachedException = e;
    return null;
}

它调用mMainDataStreamer.update(),这将失败,并使用代码KM_ERROR_MEMORY_ALLOCATION_FAILED抛出一个KeyStoreException。但是异常会被捕获,存储在mCachedException中,并返回null。这就是为什么当你打电话给null时,你会得到update()

当您调用doFinal()时,它调用AndroidKeyStoreCipherSpiBase.engineDoFinal()

代码语言:javascript
复制
protected final byte[] engineDoFinal(byte[] input, int inputOffset, int inputLen)
        throws IllegalBlockSizeException, BadPaddingException {
    if (mCachedException != null) {
        throw (IllegalBlockSizeException)
            new IllegalBlockSizeException().initCause(mCachedException);
    }

该方法看到存在缓存的异常并抛出它(包装在IllegalBlockSizeException中,这与实际问题完全无关)。

密钥存储错误

现在真正的问题是。加密/解密的实际工作由Keystore服务执行,Keystore服务是用C++编写的一个单独的进程。AES的相关部分是在operation.cpp中。

该文件中返回了许多KM_ERROR_MEMORY_ALLOCATION_FAILED错误。顾名思义,代码意味着内存分配失败。因此,由于某种原因,Keystore似乎无法分配缓冲区。很难理解为什么。

结论

由于真正的原因是神秘的,我建议保持一个小的缓冲区大小,并改变分裂程序,如前面所述。

票数 2
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/61098717

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档