3、常见加密操作及其性能影响:对称加密、非对称加密、哈希
加密操作在 Android 开发里太常见了。登录要加密、支付要加密、本地存个敏感数据也得加密。但说实话,很多人只调了 API,没考虑过性能开销。我见过不少 ANR,就是因为加密操作直接扔在主线程上跑。今天咱就掰开揉碎聊聊三类核心加密:对称、非对称、哈希。它们各自什么特点?性能差多少?怎么用才不会卡死 UI?
3.1 对称加密:性能之王,但密钥分发是难题
对称加密,说白了就是加解密用同一把钥匙。它的优点是快,非常快。硬件指令集(比如 AES-NI)能直接把加解密速度拉到 GB 级别。所以你想想看,如果要加密大量数据,对称加密几乎是不二之选。
我个人习惯首选 AES,密钥长度至少 256 位。模式上推荐 GCM(Galois/Counter Mode),既能加密又能校验完整性,不用额外再算哈希。嗯,这里要注意:ECB 模式千万别用,它会把相同明文块加密成相同密文块,安全上就是个筛子。
// AES-256-GCM 加密示例
val secretKey = SecretKeySpec(keyBytes, "AES")
val cipher = Cipher.getInstance("AES/GCM/NoPadding")
cipher.init(Cipher.ENCRYPT_MODE, secretKey)
val iv = cipher.iv // 保存 IV,解密时需要
val ciphertext = cipher.doFinal(plaintext)
// 注意:doFinal 是耗时操作,务必在 IO 线程执行
3.2 非对称加密:安全之盾,但性能开销大
非对称加密使用密钥对:公钥加密,私钥解密。安全等级高,但代价是性能比对称加密慢了 2~3 个数量级。RSA 加密 256 字节的数据,在普通手机上可能要 10~50ms,而 AES 加密同样数据只需要微秒级。
为什么会这样?因为 RSA 依赖大数模幂运算,密钥越长计算越重。2048 位的 RSA 已经算底线了,4096 位更安全但更慢。ECC(椭圆曲线加密)在同安全强度下比 RSA 快不少,密钥也更短,我建议你优先考虑 ECC。
我记得有一次,一个金融 App 在登录时用 RSA 加密密码,直接在 UI 线程执行。结果用户连续输错三次密码后,界面完全卡死——主线程被 RSA 阻塞了足足 200ms。从那以后,我强制要求所有非对称加密必须用协程异步处理。
LaunchedEffect 或 rememberCoroutineScope 中不加调度器直接运行。建议使用 Dispatchers.IO 或自定义线程池。
// RSA 加密示例(异步执行)
suspend fun rsaEncrypt(data: ByteArray, publicKey: PublicKey): ByteArray =
withContext(Dispatchers.IO) {
val cipher = Cipher.getInstance("RSA/ECB/OAEPWithSHA-256AndMGF1Padding")
cipher.init(Cipher.ENCRYPT_MODE, publicKey)
cipher.doFinal(data) // 可能耗时 10~50ms
}
3.3 哈希:数据指纹,不可逆的利器
哈希算法不是加密,它不可逆。它的作用是给数据算一个固定长度的"指纹"。只要原始数据变了哪怕一个比特,哈希值就完全不同。你想想看,这特性用来做啥?密码存储、数据完整性校验、文件去重,都是经典场景。
SHA-256 是目前最通用的选择,安全强度够,性能也适中。MD5 和 SHA-1 已经被破解了,别再用了。不过哈希虽然比非对称加密快,但也不是零成本的——对大文件做哈希,照样可能卡住 UI。
我之前做过一个文件上传功能,需要对 100MB 的文件算 SHA-256 做完整性校验。如果直接在主线程算,UI 会卡死好几秒。后来改用 Dispatchers.IO 分块读取,每读 8KB 就更新一次进度,体验好了很多。
// SHA-256 哈希示例(分块读取大文件)
suspend fun sha256File(file: File): String = withContext(Dispatchers.IO) {
val digest = MessageDigest.getInstance("SHA-256")
val buffer = ByteArray(8192)
FileInputStream(file).use { fis ->
var bytesRead = fis.read(buffer)
while (bytesRead != -1) {
digest.update(buffer, 0, bytesRead)
bytesRead = fis.read(buffer)
}
}
digest.digest().joinToString("") { "%02x".format(it) }
}
3.4 性能对比与ANR风险分析
三种操作在 Android 设备上的性能差异很明显。我整理了一份典型数据,供你参考。测试环境:骁龙 8 Gen 2,单线程,数据量 1KB。
| 操作类型 | 代表算法 | 耗时(1KB) | ANR风险 | 主要瓶颈 |
|---|---|---|---|---|
| 对称加密 | AES-256-GCM | 0.05 ~ 0.2 ms | 低 (但大数据量需注意) |
密钥派生、IV 生成 |
| 非对称加密 | RSA-2048 | 10 ~ 50 ms | 高 (必须异步) |
模幂运算、密钥生成 |
| 哈希 | SHA-256 | 0.2 ~ 0.8 ms | 中 (大文件风险高) |
文件 I/O、分块读取 |
从表格能看出:对称加密最安全,哈希最通用,非对称加密最危险(指 ANR 风险)。 但这不是说对称加密就可以随便在主线程跑。如果你加密 100MB 文件,AES 也要几十毫秒,同样会卡。
我个人有个原则:任何加密操作都不放在主线程。 不管是 AES 还是哈希,只要数据量超过 1KB,一律走 Dispatchers.IO。Compose 的协程配合 withContext 写起来很顺手,别偷懒。
核心结论:
- 对称加密(AES)是性能基准,适合大数据量加密,但密钥分发需额外保护。
- 非对称加密(RSA/ECC)最安全,但性能开销大,是 ANR 重灾区——必须异步。
- 哈希(SHA-256)不可逆,适合校验和存储,大文件时注意分块处理。
高频踩坑点:
- 把 RSA 加密直接写在
LaunchedEffect里——它默认跑在Dispatchers.Main。 - 对超大文件做哈希时不分块,导致堆内存暴涨、GC 频繁引发卡顿。
- 对称加密用了 ECB 模式或固定 IV——安全漏洞直接拉满。
我个人推荐的最佳实践:
在 Compose 中封装一个 rememberCryptoScope(),内部绑定 Dispatchers.IO,所有加密操作统一通过它执行。这样既不会忘切线程,测试时也容易 mock。