加解密基础:AES、RSA 与计算复杂度
说实话,做 Android 开发这么多年,我最怕的就是听到「加解密」三个字。为什么?因为一旦用错,轻则性能崩盘,重则 ANR 挂掉。但你想想看,主线程加解密本身就是高风险操作——要是连算法特性都不摸清,那坑可就踩定了。
这一章,我带你把 AES 和 RSA 这两大主流算法剥开来看。不讲虚的,直接说人话:它们怎么加密、怎么解密、为什么慢、以及你该怎么选。
核心结论:AES 快但密钥分发麻烦,RSA 慢但适合密文安全传输。绝大多数 App 用「RSA 加密 AES 密钥,AES 加密业务数据」的混合方案。
AES:对称加密的扛把子
AES(Advanced Encryption Standard),说白了就是一把钥匙开一把锁。加密和解密用同一个密钥,所以速度飞快。我做过压测,在手机上 1MB 数据走 AES-128 大概只要 0.5~2ms,几乎不影响主线程。
但问题来了:你怎么把密钥安全地传给对方?这是对称加密的死穴。
AES 的工作模式
实际项目中,AES 有很多种模式,最常见的是 ECB 和 CBC。我当年第一次用 AES 就吃了 ECB 的亏——同样明文加密后得到相同密文,等于白加密。后来我改用 CBC,加个随机 IV,才彻底放心。
| 模式 | 特点 | 是否推荐 |
|---|---|---|
| ECB | 简单、并行、但相同明文输出相同密文 | 不推荐 |
| CBC | 需要 IV,密文前后依赖,抗分析 | 推荐 |
| GCM | 自带认证,防止篡改 | 强烈推荐 |
避坑指南:我曾经在项目里用 ECB 模式加密用户 ,上线后被安全团队一顿锤。从此以后,我所有对称加密都用 GCM 模式——既能保密又能认证,一举两得。
RSA:非对称加密的皇冠
RSA 是公钥加密、私钥解密。你只管把公钥贴出去,别人用它加密数据,只有你手里的私钥能解开。但是你想想看,RSA 的数学原理——大素数乘积分解——天生就慢。
我测过在低端机上用 2048 位 RSA 加密 256 字节数据,耗时 20~50ms。如果数据量再大一点,主线程直接跪。
密钥长度与性能
| 密钥长度 | 加密耗时(256字节) | 解密耗时 | 安全性 |
|---|---|---|---|
| 1024位 | ~8ms | ~30ms | 已不安全 |
| 2048位 | ~15ms | ~80ms | 当前推荐 |
| 4096位 | ~50ms | ~300ms | 过度安全 |
我的习惯:RSA 只用来加密短数据,比如 AES 密钥(32 字节)。如果非要加密大文件,那最好切成小块用 AES,再用 RSA 保护 AES 密钥。嗯,这就是经典的「数字信封」方案。
解密过程:那些让你 ANR 的坑
解密比加密开销更大,尤其是 RSA 解密(需要私钥做模幂运算)。我见过不少新手在主线程直接调 Cipher.doFinal(),结果就是 ANR。
咱们用代码说话。以下是一个错误示例:
// 错误做法:在主线程直接解密大文件
public String decryptOnMain(String encryptedBase64) {
Cipher cipher = Cipher.getInstance("RSA/ECB/PKCS1Padding");
cipher.init(Cipher.DECRYPT_MODE, privateKey);
byte[] result = cipher.doFinal(Base64.decode(encryptedBase64)); // 这里阻塞
return new String(result);
}
为什么会导致 ANR?因为 doFinal() 内部会执行大整数模幂运算,在低端机上可能超过 5 秒。而 Android 主线程的 ANR 阈值是 5 秒。
正确的做法是把解密扔到子线程:
// 推荐做法:异步解密
ExecutorService executor = Executors.newSingleThreadExecutor();
executor.execute(() -> {
try {
Cipher cipher = Cipher.getInstance("RSA/ECB/OAEPWithSHA-256AndMGF1Padding");
cipher.init(Cipher.DECRYPT_MODE, privateKey);
byte[] result = cipher.doFinal(encryptedBytes);
// 主线程回调更新UI
} catch (Exception e) {
// 处理异常
}
});
经验:我做过一个即时通讯 App,之前聊天消息解密放在主线程,每次收到新消息界面就卡半秒。后来移到线程池,顺滑多了。你看,问题往往不在算法本身,而在你怎么用。
计算复杂度归纳:一张表看懂
讲到最后,我习惯把所有关键点塞进一张表里。面试时、写代码前瞟一眼就行。
| 算法 | 加密复杂度 | 解密复杂度 | 密钥长度 | 典型场景 |
|---|---|---|---|---|
| AES-128 | O(n) 快 | O(n) 快 | 128位 | 批量数据加密 |
| AES-256 | O(n) 稍慢 | O(n) 稍慢 | 256位 | 敏感数据加密 |
| RSA-2048 | O(n³) 中 | O(n³) 慢 | 2048位 | 密钥交换、数字签名 |
| RSA-4096 | O(n³) 慢 | O(n³) 很慢 | 4096位 | 极高安全需求 |
你想想看:AES 是线性复杂度,数据量翻倍,耗时也几乎翻倍。RSA 则是三次方复杂度,密钥长度从 2048 升到 4096,耗时飙 8 倍。这就是为什么我绝不用 RSA 加密大块数据的原因。
总结一下:AES 快但密钥分发难,RSA 慢但能解决密钥分发。实际项目中,我们用 RSA 加密 AES 密钥,AES 加密业务数据。记住一个原则:凡是在主线程调 doFinal() 前,先想想它会不会超过 5 毫秒。超过的话,乖乖丢线程池。