如何通过Android KeyStore生成、保存并复用AES对称密钥?
解决方案:用Android KeyStore持久化AES密钥
嘿,我刚好踩过这个坑!你之前直接生成的AES密钥是存在内存里的,应用一重启自然就没了。要实现重启后能拿到同一个密钥,核心是把密钥安全存在Android KeyStore中——KeyStore会把密钥加密存在系统安全区域,只有你的应用能访问,而且重启设备都不会丢。
下面是具体的实现步骤,直接就能用:
1. 封装KeyStore工具类,搞定密钥的生成与获取
先写一个工具类,负责从KeyStore里捞已有的AES密钥,没有的话就生成新的存进去:
import android.security.keystore.KeyGenParameterSpec import android.security.keystore.KeyProperties import java.security.KeyStore import javax.crypto.KeyGenerator import javax.crypto.SecretKey class KeyStoreManager { // 给你的密钥起个唯一别名,别乱改,改了就找不到旧密钥了 private val KEY_ALIAS = "my_app_secure_aes_key" private val keyStore = KeyStore.getInstance("AndroidKeyStore").apply { load(null) // 初始化KeyStore } // 获取AES密钥:有就拿,没有就生成并存进KeyStore fun getAesKey(): SecretKey { // 先尝试从KeyStore取已有密钥 val existingKey = keyStore.getKey(KEY_ALIAS, null) as? SecretKey existingKey?.let { return it } // 没有的话生成新密钥,配置好参数后存入KeyStore val keyGenerator = KeyGenerator.getInstance( KeyProperties.KEY_ALGORITHM_AES, "AndroidKeyStore" ) val keySpec = KeyGenParameterSpec.Builder( KEY_ALIAS, // 密钥用途:加密+解密 KeyProperties.PURPOSE_ENCRYPT or KeyProperties.PURPOSE_DECRYPT ) .setBlockModes(KeyProperties.BLOCK_MODE_GCM) // 强推GCM模式,自带防篡改校验 .setEncryptionPaddings(KeyProperties.ENCRYPTION_PADDING_NONE) .setUserAuthenticationRequired(false) // 不需要生物验证就设false,要的话改成true .setKeySize(256) // AES-256,安全性拉满 .build() keyGenerator.init(keySpec) return keyGenerator.generateKey() } }
要是你用Java,逻辑完全一样,语法调整下就行:
import android.security.keystore.KeyGenParameterSpec; import android.security.keystore.KeyProperties; import java.security.KeyStore; import javax.crypto.KeyGenerator; import javax.crypto.SecretKey; public class KeyStoreManager { private static final String KEY_ALIAS = "my_app_secure_aes_key"; private final KeyStore keyStore; public KeyStoreManager() throws Exception { keyStore = KeyStore.getInstance("AndroidKeyStore"); keyStore.load(null); } public SecretKey getAesKey() throws Exception { // 先取已有密钥 SecretKey existingKey = (SecretKey) keyStore.getKey(KEY_ALIAS, null); if (existingKey != null) { return existingKey; } // 生成新密钥并存入KeyStore KeyGenerator keyGenerator = KeyGenerator.getInstance( KeyProperties.KEY_ALGORITHM_AES, "AndroidKeyStore" ); KeyGenParameterSpec keySpec = new KeyGenParameterSpec.Builder( KEY_ALIAS, KeyProperties.PURPOSE_ENCRYPT | KeyProperties.PURPOSE_DECRYPT ) .setBlockModes(KeyProperties.BLOCK_MODE_GCM) .setEncryptionPaddings(KeyProperties.ENCRYPTION_PADDING_NONE) .setUserAuthenticationRequired(false) .setKeySize(256) .build(); keyGenerator.init(keySpec); return keyGenerator.generateKey(); } }
2. 在加密逻辑里替换成这个密钥
以后再也不用自己临时生成AES密钥了,直接调用getAesKey()就行,和你之前的加解密逻辑无缝衔接:
// 示例加密代码 val keyStoreManager = KeyStoreManager() val aesKey = keyStoreManager.getAesKey() // 下面就是你原来的加解密逻辑,完全不用改 val cipher = Cipher.getInstance("AES/GCM/NoPadding") cipher.init(Cipher.ENCRYPT_MODE, aesKey) // ... 剩下的加密操作该咋写咋写
3. 关于RAM存储的补充
如果有些场景你需要临时密钥,不想持久化,那可以用一个单例类把密钥存在内存里:
object TempKeyHolder { var tempAesKey: SecretKey? = null }
但要注意,这种方式下应用一重启密钥就没了,只适合临时用的场景。你的核心需求是重启后能拿到同一个密钥,所以优先用Android KeyStore的方案。
几个关键提醒
- 密钥别名
KEY_ALIAS一定要固定死,别动态生成,不然每次都会创建新密钥,旧数据就解不开了 - 一定要用
GCM块模式,比ECB/CBC安全太多,还自带数据完整性校验,防止加密数据被篡改 - 如果要更高的安全性,可以把
setUserAuthenticationRequired(true)打开,这样只有用户验证指纹/密码后才能用密钥,但要注意适配不同Android版本的生物验证逻辑 - KeyStore的密钥和你的应用签名绑定,只要签名不变,卸载重装后依然能拿到之前的密钥(Android 6.0+基本都支持)
这样弄完之后,不管你杀应用还是重启手机,调用getAesKey()都会拿到同一个AES密钥,之前加密的数据就能正常解密啦!
内容的提问来源于stack exchange,提问作者Alexandre Blanchet
相关产品推荐
相关产品推荐

