Java 为什么安卓24+;上的AES加密/解密速度要慢3倍以上;?

Java 为什么安卓24+;上的AES加密/解密速度要慢3倍以上;?,java,android,performance,encryption,cryptography,Java,Android,Performance,Encryption,Cryptography,您可以跳到TL;DR 我们有一个应用程序,它强烈依赖AES加密和解密。我们希望支持尽可能多的设备,但其中一些设备(特别是蹩脚的平板电脑,我不仅仅指中文没有名字,还有一些三星或联想的低端平板电脑)的加密和解密速度很慢 我们在应用程序中使用了安卓23,我们能够确定某种级别,低于该级别,我们的应用程序对最终用户来说将无法正常工作(他们将不得不等待太长时间才能看到内容)。我们不得不排除很多平板电脑与我们的应用程序一起使用,但我们可以接受 最近,我们的一些依赖项开始需要更新版本的Android。例如,我们

您可以跳到TL;DR

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

我们在应用程序中使用了安卓23,我们能够确定某种级别,低于该级别,我们的应用程序对最终用户来说将无法正常工作(他们将不得不等待太长时间才能看到内容)。我们不得不排除很多平板电脑与我们的应用程序一起使用,但我们可以接受

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

因此决定将项目转移到一个更新的安卓系统。除了对我们的加密/解密机制产生的性能影响外,它进行得相当顺利。突然,速度慢了很多。我们认为“足够好用”的平板电脑速度非常慢

TL;DR

我已经开始调查在从Android 23迁移到Android 26的过程中发生了什么,这将导致AES加密/解密的性能大幅下降

我创建了一个应用程序,它可以作为一种基准。通过做一个简单的改变:

  • 编译DK23->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上获得:

有安卓23配置的
master
分支和安卓26配置的
master_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;
}
/**
*使用给定密钥解密给定数据
*
*@param data要解密的数据
*@返回已解密的字节
*/
公共静态字节[]解密(字节[]数据,字节[]密钥,字节[]iv){
if(key==null | | iv==null){
抛出新的断言错误(“解密:未指定密钥或iv”);
}
//确保钥匙是AES256
字节[]bookKeyData=新字节[32];
字节[]f;
System.arraycopy(键,0,bookKeyData,0,键.长度);
试一试{
PaddedBufferedBlockCipher cipher=新的PaddedBufferedBlockCipher(新的CBCBlockCipher(新的AESFastEngine());
cipher.init(false,新参数swithiv(新键参数(bookKeyData),iv));
int outputSize=cipher.getOutputSize(data.length);
exputf=新字节[cipher.getOutputSize(outputSize)];
int processed=cipher.processBytes(data,0,data.length,exputf,0);
如果(已处理<输出大小){
已处理+=cipher.doFinal(突发,已处理);
}
返回Arrays.copyOfRange(exputf,0,已处理);
}捕获(例外e){
e、 printStackTrace();
}
返回null;
}

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

我终于找到了解决办法

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

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

因此,它在以下情况下对我起了作用:

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

在library项目和main项目中。

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

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

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

目前,库依赖于Bouncy Castle实现的字节码执行,这要慢得多。还要注意,Bouncy Castle不太喜欢调试环境,所以