【问题标题】:Why is AES encryption / decryption more than 3x slower on Android 24+?为什么 AES 加密/解密在 Android 24+ 上慢 3 倍以上?
【发布时间】:2018-03-27 15:09:59
【问题描述】:

您可以跳到 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 倍。我可以在我必须测试的所有设备上重现这些结果。当然,在新的高性能设备上几乎看不到它,但在旧设备上却很明显。

我已经在网上搜索了有关这方面的任何详细信息,但我一无所获。我真的很感激有人能阐明这个问题。

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

对于加密/解密,我们使用 SpongyCastle 库。

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

master 分支采用 Android 23 配置,master_26 分支采用 Android 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,我知道为什么不应该使用它等等。目前,它是故意完成的。这不是问题的主题,所以我们不要去那里。

【问题讨论】:

  • 加解密算法是否应该使用硬件加速?
  • 我不知道,我认为这是由操作系统/库在幕后完成的。
  • 136959 B/s (136 kB/s) 本身就有点糟糕。也许您需要调查两台机器的一般问题。从餐巾纸的背面看,这对于 200 MHz 处理器来说可能没问题。您应该开始比较每字节周期数 (cpb)。这将回避处理器速度并检查算法和实现效率。您应该在 32 位 ARM 上的软件目标为 30 到 60 cpb,在 64 位 ARM 上的硬件目标为 2 到 5 cpb。 900 MHz Beaglebone (A-32) 达到 45 cpb 或 20 MB/s。 2.0 GHz Opeteron (Aarch64) 达到 1.0 cpb 和 1.8 GB/s。
  • @jww 很可能只是我的计算是废话;)但是这些值显示了性能下降的规模,即使它们可能不是 B/s。我将对此进行进一步调查。如果解密 10MB 需要几秒钟,它不可能是 136kB/s。对此感到抱歉。

标签: java android performance encryption cryptography


【解决方案1】:

您似乎直接使用 SpongyCastle。 SpongyCastle 是 BouncyCastle (BC) 的 Android 版本。然而,BC 是加密算法和周边实用 API 的纯软件实现。

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

通常建议仅在提供的密码学providers 中没有所需功能时才使用 Bouncy Castle“轻量级”API(例如您正在使用的软件 AES 实现)。对于 AES/CBC 等算法,这绝对不是

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

【讨论】:

  • 非常感谢马丁!太好了,我不知道,我认为它会在任何可能的地方工作!
  • 我只是想补充一点,随着@MaartenBodewes 提出的更改,我的基准测试速度提高了 38 倍。相当大的性能提升!
【解决方案2】:

我终于找到了解决办法。

当我尝试在 SpongyCastle GitHub 上创建问题时,我注意到有比 1.54 更新的版本...真傻我没有早点调查。

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

所以它在之后对我有用:

  • 将海绵城堡版本更改为1.56
  • compileSdkVersion 更改为26
  • buildToolsVersion 更改为26.0.2
  • targetSdkVersion 更改为26

在库项目和主项目中。

【讨论】:

  • 你能分辨出海绵城堡发生了什么变化,让它变得更快了吗?
  • 不,没有线索。我没有时间进一步调查它。有趣的是,在我的测试应用程序中,我唯一要做的就是更改版本号,它就开始全速运行了!
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-09-22
  • 2012-04-08
  • 2011-10-26
  • 2011-12-20
相关资源
最近更新 更多