第3章 加密引擎选型:AES/GCM、RSA/EC、ChaCha20性能对比、密钥长度与模式选择、Android原生加密API边界
加密引擎选型是数据保护组件设计的核心决策。本章从组件边界出发,分析 AES/GCM、RSA/EC、ChaCha20 三种引擎在 Android 环境下的性能特征、密钥长度与模式选择策略,并界定 Android 原生加密 API 的业务边界,为架构师提供可落地的选型框架。
引擎性能对比
| 特性 | AES/GCM | ChaCha20/Poly1305 | RSA/EC (Elliptic Curve) |
|---|---|---|---|
| 对称/非对称 | 对称加密 | 对称加密 | 非对称加密 |
| 算法类型 | 分组加密 (128-bit block) | 流加密 | 公钥加密/密钥协商 |
| 典型密钥长度 | 128, 192, 256 bit | 256 bit | 2048-4096 bit (RSA) / 256-521 bit (EC) |
| 认证模式 | GCM (AEAD) | Poly1305 (AEAD) | 无内建AEAD,需配合CBC+HMAC或OAEP |
| 硬件加速 | ARMv8 AES指令 (40-60 MB/s) | 无专用指令 (15-30 MB/s) | 无通用加速 (慢10-100x) |
| IV/Nonce大小 | 12 byte (推荐) | 12 byte | 无 |
| 主要用途 | 大数据量对称加密/存储 | 移动端低功耗加解密 | 密钥交换/数字签名/证书 |
| Android API 级别限制 | API 19+ (KeyGenParameterSpec API 23+) | API 21+ (Conscrypt Provider) | API 1+ (RSA), API 23+ (EC KeyStore) |
决策原则:对称加密首选 AES/GCM(硬件加速稳定);追求极低延迟与电池友好场景(如 IoT、实时通信)可选 ChaCha20;RSA/EC 仅用于密钥封装、签名和身份认证,不直接加密业务数据。
密钥长度与模式选择
| 引擎 | 推荐密钥长度 | 加密模式 | 填充方案 | 备注 |
|---|---|---|---|---|
| AES/GCM | 256 bit (默认) | GCM (AEAD) | 不需要填充 | IV 12 byte,tag 16 byte;注意 IV 不可重复 |
| ChaCha20/Poly1305 | 256 bit | XChaCha20 (扩展 nonce) 或标准 | 不需要填充 | Nonce 12 byte (标准) 或 24 byte (XChaCha20) |
| RSA/EC | RSA 3072 bit / EC P-256 | ECB (单块) / ECDH (密钥交换) | OAEP (推荐) 或 PKCS#1 v1.5 | 禁止使用 ECB 模式加密多个数据块;EC 不直接加密 |
实践建议:对于对称加密统一使用 AES/GCM/NoPadding,密钥通过 KeyGenParameterSpec.Builder 的 setKeySize(256) 指定;非对称加密仅用于密钥封装(RSA/ECIES)或签名(ECDSA)。
Android 原生加密 API 边界
Android 提供 javax.crypto.Cipher 作为加密操作入口,底层通过 Provider 架构解析算法字符串(如 "AES/GCM/NoPadding")。边界条件包括:
- 算法字符串格式:
"算法/模式/填充",如"ChaCha20"在 API 21+ 被映射为 Conscrypt Provider 的流模式。 - 密钥材料存储:建议使用
Android KeyStore生成和存储密钥,禁止将密钥明文写入 SharedPreferences 或文件中。 - KeyGenParameterSpec 限制:API 23+ 支持
setBlockModes("GCM")和setEncryptionPaddings("NoPadding"),但ChaCha20不能通过 KeyStore 生成密钥,只能通过KeyGenerator或直接传入字节。 - Conscrypt Provider 优先级:系统默认 Provider 可能因厂商定制而缺失 ChaCha20,建议显式指定
Cipher.getInstance("ChaCha20", "Conscrypt")。 - NV/Nonce 管理:GCM 要求每次加密使用唯一 IV,推荐使用
SecureRandom生成 12 byte 并随密文存储。
风险点:使用 RSA/ECB/PKCS1Padding 加密长数据会导致 IllegalBlockSizeException;GCM 的 GCMParameterSpec tag 长度若小于 128 bit 可能被降级攻击。
信任链路与密钥生命周期
从密钥生成到销毁构成一条完整的信任链路:
- 密钥生成:通过
KeyPairGenerator或KeyGenerator在 KeyStore 内创建,保证私钥不可导出。 - 密钥使用:
Cipher.init()时指定KeyStore.SecretKeyEntry或PrivateKeyEntry。 - 密钥备份/迁移:使用 AES 包装密钥(Key Wrapping)后导出,要求包装密钥本身在 TEE 中管理。
- 密钥销毁:调用
KeyStore.deleteEntry();若密钥在 TEE 中,需等待系统重启或调用厂商专用 API。
选型权衡矩阵
| 场景 | 推荐引擎 | 密钥长度 | 模式 | 理由 |
|---|---|---|---|---|
| 本地数据库字段加密 | AES/GCM | 256 bit | GCM/NoPadding | AEAD 保证完整性,硬件加速快 |
| 实时语音/视频加密 | ChaCha20 | 256 bit | ChaCha20 | 流加密低延迟,无填充开销 |
| HTTPS 证书/API 签名 | RSA 或 EC | 3072 bit (RSA) / P-256 (EC) | OAEP/ECDSA | 兼容性优先,EC 性能更优 |
| 密钥交换与会话密钥生成 | ECDHE + AES/GCM | P-256 + 256 bit | ECDH + GCM | 前向安全性,业界标准 |
| 离线内容保护(DRM) | AES/GCM | 128 bit (合规) | GCM/NoPadding | 兼容 Widevine L3 要求 |
落地步骤
- 确定目标场景:是存储加密、传输加密还是密钥封装?据此选择对称或非对称。
- 建立性能基线:在目标设备上(至少 Android 10+ 和低端机)跑 AES/GCM、ChaCha20 的加密 1MB 数据耗时,记录 5 次测量中位数。
- 配置 KeyStore:使用
KeyGenParameterSpec.Builder设置setKeySize(256)、setBlockModes("GCM"),并在setIsStrongBoxBacked(true)如果设备支持。 - 实现 IV 管理:加密时将 IV 拼接在密文前(prefix 12 byte),解密时先提取 IV。
- 集成单元测试:覆盖密钥生成、加解密、签名验证、密钥删除全流程,包括异常注入(如篡改 IV、tag 错误)。
- 验证兼容性:在 API 26、30、33 模拟器上运行 CTS-like 测试,确认无 Provider 缺失。
验收标准
| 检查项 | 通过条件 | 验证方法 |
|---|---|---|
| 密钥不可导出 | KeyStore 私钥/secret 无法通过 getEncoded() 获取 | 尝试调用,应抛出 KeyStoreException |
| IV 唯一性 | 对同一密钥加密 10⁶ 次无重复 IV | 使用 SecureRandom 生成 IV,统计分析 |
| AEAD 完整性 | 篡改密文后解密抛出 AEADBadTagException | 单元测试中修改最后一个密文字节 |
| 性能基线 | AES/GCM 加密 1MB < 50ms (中端设备) | 基准测试脚本,记录5次中位数 |
| Provider 回退 | 所有引擎在 API 21-33 上均正常工作 | 多版本模拟器测试,失败时打印异常堆栈 |
| 密钥轮换 | 旧密钥加密的数据能被新密钥解密并存档 | 实现打包密钥机制,验证迁移路径 |