Android上的AES解密太慢而无法使用.NDK会更快吗?其他想法?

par*_*par 7 java performance android aes

我使用内置的Cipher类在Android上实现了AES/CTR.对于我的目的来说,解密似乎太慢了,128KB的块在仿真器上解密大约需要6秒,而在三星Galaxy硬件上需要2.6秒.

我想知道是否使用NDK构建OpenSSL并调用其方法会更快.有人对这个有经验么?部分我想要相信Cipher("AES/CTR/NoPadding")方法只是本机OpenSSL调用的包装器,因为支持Android的Linux操作系统应该安装了libcrypto.如果是这种情况,那么尝试使用NDK只会浪费时间,因为不会出现性能提升.

我没有费心在iOS上计算时间,但即使3Gs硬件也解密得如此之快,以至于10MB解密似乎对最终用户来说是即时的.我发现很难相信Android的实现确实更糟糕,但也许这就是现实.

如果这真的是我面临的问题,那么是否有人对其他实施策略有任何想法会为最终用户提供难以察觉的响应(在10Mb文件上)?我办公室的另一位开发人员用一种诙谐的方式建议我只使用XOR加密,这让我想要自己面对,但我认为(安全问题除外),如果我这样做,它会起作用.

谢谢!

这里有一些简化的代码供参考:

public class ResourceDecryptor {
    private static ThreadLocal<Cipher>      mCipher;
    private byte[]                          mIV = new byte[ 8 ];
    private SecretKeySpec                   mKey;
    private String                          mResourcePath;

    private static final int                kAESBlockSize = 16;

    public ResourceDecryptor( String resourcePath, String decryptionKey ) throws UnsupportedOperationException {
        // initialization of mKey, mIV, & mResourcePath, elided 

        // store mCipher as a thread local because Cipher.getInstance() is so slow,
        // ResourceDecryptor is a static object that persists for the app lifetime
        // so this leak is intentional and ok.
        mCipher = new ThreadLocal<Cipher>() {
            protected Cipher initialValue() {
                try { return Cipher.getInstance( "AES/CTR/NoPadding" ); } catch ( Exception e ) { }

                return null;
            }
        };
    }

    public ByteBuffer read( long offset, int length ) throws GeneralSecurityException, IOException {
        Cipher                      cipher;
        byte[]                      data, iv;
        FileInputStream             input;
        int                         prefix, readLength;

        input = null;
        prefix = (int)( offset % kAESBlockSize );
        readLength = ( prefix + length + kAESBlockSize - 1 ) / kAESBlockSize * kAESBlockSize;
        data = new byte[ readLength ];
        iv = new byte[ 16 ];

        try {
            input = new FileInputStream( mResourcePath );
            input.skip( offset -= prefix );

            if ( input.read( data ) != readLength ) throw new IOException( "I/O error: unable to read " + readLength + " bytes from offset " + offset );

            System.arraycopy( mIV, 0, iv, 0, 8 );

            offset /= kAESBlockSize;

            iv[  8 ] = (byte)( offset >> 56 & 0xff );
            iv[  9 ] = (byte)( offset >> 48 & 0xff );
            iv[ 10 ] = (byte)( offset >> 40 & 0xff );
            iv[ 11 ] = (byte)( offset >> 32 & 0xff );
            iv[ 12 ] = (byte)( offset >> 24 & 0xff );
            iv[ 13 ] = (byte)( offset >> 16 & 0xff );
            iv[ 14 ] = (byte)( offset >>  8 & 0xff );
            iv[ 15 ] = (byte)( offset       & 0xff );

            if ( ( cipher = mCipher.get() ) == null ) throw new GeneralSecurityException( "Unable to initialize Cipher( \"AES/CTR/NoPadding\" )" );
            cipher.init( Cipher.DECRYPT_MODE, mKey, new IvParameterSpec( iv ) );

            long startTime = System.currentTimeMillis();
            data = cipher.doFinal( data );
            System.out.println( "decryption of " + data.length + " bytes took " + ( ( System.currentTimeMillis() - startTime ) / 1000.0 ) + "s" );

            // cipher.doFinal() takes 5.9s on Samsung Galaxy emulator for 128kb block
            // cipher.doFinal() takes 2.6s on Samsung Galaxy hardware for 128kb block
        } finally {
            if ( input != null ) try { input.close(); } catch ( Exception e ) { }
        }

        // the default order of ByteBuffer is BIG_ENDIAN so it is unnecessary to explicitly set the order()

        return ByteBuffer.wrap( data, prefix, length );
    }
}
Run Code Online (Sandbox Code Playgroud)

Ebo*_*ike 7

是的,在一个包含的函数中像那样的繁重提升正是NDK会发光的地方.请记住,Java是被解释的,并且在2.2之前的Android上,没有JIT,所以每次都会解释每条指令 - 这是一个巨大的开销.

即使使用JIT,每个数组访问都会进行隐式边界检查,因此会产生大量的开销.

如果用C++编写这个函数,速度会快得多.

  • 快速更新:我们确实使用NDK进行本机AES解密,并且完美运行.我们的应用程序现在非常快. (4认同)
  • @preetha我很抱歉,但我目前正在使用这个特殊的代码进行NDA,并且不能发布它.通常我不会发布我在问题中写的代码,但它提到的速度很慢,因此无法使用,所以没有人对它有任何问题.我可以告诉你,我们的努力非常*直截了当.如果你使用NDK构建一个hello-world应用程序,你将会有90%的存在.其余部分是开放编译(不太强硬,谷歌会帮助)然后你必须在C中编写解密例程(提示提示)*非常*类似于我上面发布的代码. (2认同)