一、序

之前了解AES加解密的进程中,阅读了一篇文章:《AES简介》
除了从这篇文章中学习了AES原理之外,还了解到有“查表”的完成,
所以下载从其文章链接的源码下来,在Android渠道上试用了一下,的确比Android SDK自带的完成要快许多。
但是文章作者所完成的源码只要128bits版别,所以在github上联系了作者,然后被告知openssl上有完整的128bits和256bits的版别。

FastAES:比SDK快10倍的AES加解密实现

openssl的代码是C言语,Android渠道运用的话需求封装一下。
其实之前已经完成了,集成在 github.com/BillyWei01/… ,但没有发布到MavenCentral。
最近 FastKV 更新了,支持注入自定义加密,所以抽取AES加解密部分,单独发布,并将其命名为FastAES(也懒得再想其他命名了)。
FastKV的加解密替换成FastAES之后,的确比之前的加解密快许多!

二、运用

2.1 导入

dependencies {
    implementation 'io.github.billywei01:fastaes:1.1.2'
}

现在aar包只编译了 ‘armeabi-v7a’, ‘arm64-v8a’ 两种abi架构。
假如需更多架构可以自行下载源码编译,或许联系我添加。

2.2 运用

    byte[] cipher = FastAES.encrypt(data, key, iv);
    byte[] plain = FastAES.decrypt(cipher, key, iv);

以上接口完成的是 “AES/CBC/PKCS7Padding” 形式。

encrypt或许在内存不足时抛反常;
decrypt或许在内存不足,或许输入的密文不合法时抛出反常。
运用时需酌情处理反常。
不过假如的确是内存不足,那其他地方或许也会抛OOM。

三、性能

测验数据:1000个长度在100字节以内的随机数组。
测验设备:HUAWEI P30 Pro。
测验成果:

耗时(ms)
FastAES 1
SDK AES 24
KeyStore AES 20036

和Android SDK的AES完成对比,要快一个数量级。
文章标题说的快10倍,说的是大概的量级,关于不同长度输入,距离有所浮动。
假如随机数组长度更长一些,距离会稍微缩小,但还是差一个数量级。

关于为什么SDK供给的AES不如openssl的查表完成,我也不清楚。
推测是SDK的AES需求根据字面量 “AES/CBC/PKCS7Padding” 去找算法供给方(Provider), 路由进程包含检索和目标创建等,这方面耗费不少;
然后便是openssl的查表完成的确比SDK的AES中心完成的确要快一些?

需求指出的是,查表的完成,需求一个10K左右的表;
关于现在的手机设备来说,10K的空间应该还好。

四、KeyStore加密

这一末节是题外话,但也值得讲一讲。

从上面列出的测验成果来看,用KeyStore做AES加解密,速度究极慢。
或许是因为加密进程要在TTE上履行的原因?
这方面我也不太清楚,有了解的朋友可以和大家共享一下。
但不管什么原因,KeyStore的确不适合用来加密很多数据。

一种折中的方式是通过KeyStore来生成密钥。

public class KeyStoreHelper {
    private static final String ALIAS = "KeyStoreHelper";
    public static byte[] getKey(byte[] seed) {
        try {
            String algorithm = KeyProperties.KEY_ALGORITHM_HMAC_SHA256;
            KeyStore keyStore = KeyStore.getInstance("AndroidKeyStore");
            keyStore.load(null);
            Key key = keyStore.getKey(ALIAS, null);
            if (key== null) {
                // Generate the key and save to KeyStore, next time we can get it from KeyStore.
                KeyGenerator keyGenerator = KeyGenerator.getInstance(algorithm, "AndroidKeyStore");
                keyGenerator.init(new KeyGenParameterSpec.Builder(ALIAS, KeyProperties.PURPOSE_SIGN).build());
                key = keyGenerator.generateKey();
            }
            Mac mac = Mac.getInstance(algorithm);
            mac.init(key);
            byte[] digest = mac.doFinal(seed);
            return Arrays.copyOf(digest, 16);
        } catch (Exception e) {
            FastKVLogger.INSTANCE.e("Cipher", e);
        }
        return new byte[16];
    }
}

KeyGenParameterSpec 是Android M (Android 6.0) 供给的API,
用其构建的随机的Key会保存在KeyStore中,在下次启动时, keyStore.getKey能获取到其生成的Key。
如上的完成,输入seed,会得到随机的byte数组(可以作为AES的key);
下次启动输入相同的seed, 会得到相同的输出。
也便是,可用 KeyStore对固定的seed核算HMAC(或许AES也行),用其核算成果作为AES的key,再选一个更快的AES完成来加密。
如此,既利用了KeyStore的保密性,又避免了直接利用其加密的效率问题。

需求注意的是,KeyStore保存的Key会在APP卸载时一并删除;
卸载重装后,用相同的ALIAS获取到的Key会不一样。
也便是,KeyStore的Key,可以用来加密生命周期在装置期间的数据(比方内部目录的数据),不可以用来加密生命周期更长的数据(比方云端的数据),不然卸载重装后解密不了。

五、总结

通过封装openssl的AES查表完成,得到了一个比Android SDK供给的加密更快的完成。
关于需求频频加解密,且对速度有一定要求的场景,可以用之改进。

项目已上传Github: github.com/BillyWei01/…

欢迎各位朋友点赞、收藏、投star !