为什么Android 24 +上的AES加密/解密速度超过3倍?

您可以跳到 TL;DR

我们有一个应用程序,它强烈依赖于AES加密和解密。我们希望支持尽可能多的设备,但其中一些(特别是蹩脚的平板电脑,我不仅指中文无名,还意味着三星或联想的一些低端平板电脑)加密和解密速度很慢。

我们在应用程序中使用了Android 23,并且我们能够识别出某种级别,低于该级别,我们的应用程序将无法很好地为最终用户工作(他们必须等待太长时间才能显示内容)。我们不得不排除与应用程序一起使用的很多平板电脑,但是,我们能够忍受这一点。

最近,我们的一些依赖项开始需要较新版本的Android。例如,我们希望切换到Facebook Core SDK,而不是完整的Facebook SDK以节省一些空间。但它取决于Android支持包v25,我们将无法构建它,因为proguard拒绝处理源代码。

因此,决定将该项目转移到更新的Android上。除了它对我们的加密/解密机制的性能影响之外,它还非常顺利。突然间,它慢了很多。我们评价为“工作足够好”的平板电脑非常慢。

TL;DR

我已经开始调查从Android 23迁移到Android 26期间发生的事情,这将导致AES加密/解密的性能大幅下降。

我创建了一个应用程序,它可以作为一种基准测试。通过进行简单的更改:

  • compileSdkVersion 23->26
  • targetSdkVersion 23->26
  • compile 'com.android.support:appcompat-v7:VERSION' 23.4.0 -> 26.+

性能下降是巨大的。

以下是其中一个平板电脑的示例结果:

Android 23: 136959 B/s
Android 26: 34419 B/s

这几乎慢了4倍。我可以在我必须测试的所有设备上重现这些结果。当然,在新的高性能设备上,它几乎看不见,但在旧设备上,它很清楚。

我已经在网上搜索了有关此内容的任何详细信息,但我没有发现任何内容。我真的非常感谢有人能对这个问题有所了解。

我真的希望我在某个地方犯了一个错误,但我无法找到它。

对于加密/解密,我们使用海绵城堡库。

我的Crypto Tester应用程序的来源可以在GitHub上找到:https://github.com/krstns/cryptoTester

有一个分支使用Android 23配置,分支使用Android 26配置。mastermaster_26

为了完整起见,我将在这里粘贴用于解密的方法:

/**
 * Decrypt the given data with the given key
 *
 * @param data The data to decrypt
 * @return The decrypted bytes
 */
public static byte[] decrypt(byte[] data, byte[] key, byte[] iv) {
    if (key == null || iv == null) {
        throw new AssertionError("DECRYPT: Key or iv were not specified.");
    }

    // make sure key is AES256
    byte[] bookKeyData = new byte[32];
    byte[] outBuf;
    System.arraycopy(key, 0, bookKeyData, 0, key.length);

    try {
        PaddedBufferedBlockCipher cipher = new PaddedBufferedBlockCipher(new CBCBlockCipher(new AESFastEngine()));
        cipher.init(false, new ParametersWithIV(new KeyParameter(bookKeyData), iv));
        int outputSize = cipher.getOutputSize(data.length);
        outBuf = new byte[cipher.getOutputSize(outputSize)];
        int processed = cipher.processBytes(data, 0, data.length, outBuf, 0);
        if (processed < outputSize) {
            processed += cipher.doFinal(outBuf, processed);
        }
        return Arrays.copyOfRange(outBuf, 0, processed);

    } catch (Exception e) {
        e.printStackTrace();
    }

    return null;
}

哦,还有..是的。我知道这是CBC,我知道为什么不应该使用它等。目前,它是故意完成的。这不是问题的主题,所以让我们不要去那里。


答案 1

你似乎直接使用海绵城堡。SpongyCastle是BouncyCastle(BC)的Android版本。然而,BC是加密算法和周围实用程序API的纯软件实现。

如果你真的想加快AES计算速度,你应该使用Java Security API,例如使用javax.crypto.Cipher。这将允许在支持它的平台上进行硬件加速和本机代码执行。一般来说,这将是所有平台,因为主要的加密功能是在较新的平台上使用OpenSSL库实现的。

通常,建议仅在提供的加密提供程序中无法提供所需功能时才使用 Bouncy Castle 的“轻量级”API(例如您正在使用的软件 AES 实现)。对于AES / CBC等算法来说,情况绝对不是这样。

目前,您的库依赖于 Bouncy Castle 实现的字节码执行,这要慢得多。另请注意,Bouncy Castle不太喜欢调试环境,因此请确保它在测试性能时没有延迟运行 - 如果可能的话,没有调试器支持。


答案 2

我终于找到了解决方案。

当我试图在SpongyCastle GitHub上创建一个问题时,我注意到有比...好吧,愚蠢的我没有早点调查过这个问题。1.54

只是一个警告,它并没有在我的主项目中立即起作用。加密/解密机制是库项目的一部分,然后包含在我的主项目中。请记住还要更新您的主项目,否则它仍然会很慢。

所以它对我有用之后:

  • 将海绵城堡版本更改为1.56
  • 更改为compileSdkVersion26
  • 更改为buildToolsVersion26.0.2
  • 更改为targetSdkVersion26

在库项目和主项目中。


推荐