You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

卸载应用后Android KeyStore条目是否丢失?附密钥操作代码

解决Android KeyStore重装应用后无法获取密钥条目的问题

问题根源

你遇到的问题核心在于Android KeyStore中应用创建的密钥与应用的安装实例强绑定。当卸载应用时,系统会自动清理该应用在KeyStore中的所有密钥数据;重装应用后相当于一个全新的实例,自然找不到之前创建的密钥条目。

解决方案

根据不同的使用场景,有两种可行的处理方式:

1. 允许密钥参与系统备份(依赖系统备份恢复场景)

如果你的用户依赖系统备份/恢复流程来迁移应用数据,可以在生成密钥时添加setBackupAllowed(true)参数(API 23及以上支持)。这样当用户通过系统备份恢复应用时,密钥会被同步回来。

修改你的密钥生成代码如下:

KeyPairGenerator kpg = KeyPairGenerator.getInstance(KeyProperties.KEY_ALGORITHM_RSA, "AndroidKeyStore");
kpg.initialize(new KeyGenParameterSpec.Builder(Constants.KEY_STORE_ALIAS_NAME, 
        KeyProperties.PURPOSE_ENCRYPT | KeyProperties.PURPOSE_DECRYPT)
        .setKeySize(Constants.ASYMMETRIC_KEY_LENGTH)
        .setBlockModes(KeyProperties.BLOCK_MODE_ECB)
        .setEncryptionPaddings(KeyProperties.ENCRYPTION_PADDING_RSA_PKCS1)
        .setBackupAllowed(true) // 允许密钥被纳入系统备份
        .build());
keyPair = kpg.generateKeyPair();

注意:这种方式仅在用户使用系统备份恢复应用时有效,手动卸载重装且未恢复备份的话,密钥依然会丢失。

2. 导出私钥并安全备份(手动重装后需恢复密钥场景)

如果需要在手动重装后也能恢复密钥,就得在生成密钥时允许私钥导出,然后将导出的私钥加密后存储到安全位置(比如加密文件、用户可信云存储等),重装后再导入到KeyStore中。

第一步:修改密钥生成参数,允许导出私钥
KeyPairGenerator kpg = KeyPairGenerator.getInstance(KeyProperties.KEY_ALGORITHM_RSA, "AndroidKeyStore");
kpg.initialize(new KeyGenParameterSpec.Builder(Constants.KEY_STORE_ALIAS_NAME, 
        KeyProperties.PURPOSE_ENCRYPT | KeyProperties.PURPOSE_DECRYPT)
        .setKeySize(Constants.ASYMMETRIC_KEY_LENGTH)
        .setBlockModes(KeyProperties.BLOCK_MODE_ECB)
        .setEncryptionPaddings(KeyProperties.ENCRYPTION_PADDING_RSA_PKCS1)
        .setUserAuthenticationRequired(false) // 确保无验证时可导出私钥
        .build());
keyPair = kpg.generateKeyPair();
第二步:导出私钥并加密存储
// 将私钥导出为PKCS#8格式字节数组
byte[] privateKeyBytes = keyPair.getPrivate().getEncoded();

// 用安全的对称密钥加密私钥(示例,实际需使用更严谨的加密逻辑)
SecretKey backupSymKey = generateSecureSymmetricKey(); // 自定义生成备份用对称密钥
Cipher cipher = Cipher.getInstance(KeyProperties.KEY_ALGORITHM_AES + "/" + KeyProperties.BLOCK_MODE_GCM + "/" + KeyProperties.ENCRYPTION_PADDING_NONE);
cipher.init(Cipher.ENCRYPT_MODE, backupSymKey);
byte[] encryptedPrivateKey = cipher.doFinal(privateKeyBytes);

// 将加密后的私钥和备份对称密钥的安全信息(比如加密后的备份密钥)存储到安全文件
saveToEncryptedFile(encryptedPrivateKey, ...);
第三步:重装后导入私钥到KeyStore
// 从安全文件读取加密后的私钥和备份密钥信息
byte[] encryptedPrivateKey = readFromEncryptedFile(...);
SecretKey backupSymKey = restoreBackupSymKey(...); // 恢复备份用对称密钥

// 解密私钥
Cipher cipher = Cipher.getInstance(KeyProperties.KEY_ALGORITHM_AES + "/" + KeyProperties.BLOCK_MODE_GCM + "/" + KeyProperties.ENCRYPTION_PADDING_NONE);
cipher.init(Cipher.DECRYPT_MODE, backupSymKey);
byte[] privateKeyBytes = cipher.doFinal(encryptedPrivateKey);

// 将私钥导入KeyStore
KeyFactory keyFactory = KeyFactory.getInstance(KeyProperties.KEY_ALGORITHM_RSA);
PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(privateKeyBytes);
PrivateKey privateKey = keyFactory.generatePrivate(keySpec);

KeyStore ks = KeyStore.getInstance("AndroidKeyStore");
ks.load(null);
ks.setEntry(Constants.KEY_STORE_ALIAS_NAME, 
        new KeyStore.PrivateKeyEntry(privateKey, null), 
        new KeyProtection.Builder(KeyProperties.PURPOSE_DECRYPT)
                .setBlockModes(KeyProperties.BLOCK_MODE_ECB)
                .setEncryptionPaddings(KeyProperties.ENCRYPTION_PADDING_RSA_PKCS1)
                .build());

额外注意事项

  • 优先使用系统备份方案,尽量避免导出私钥——导出私钥会降低密钥的安全性,仅在必要时使用第二种方案。
  • 针对Android 10(API 29)及以上版本,系统对KeyStore的权限和备份策略有更严格的限制,需确保密钥参数符合系统要求。
  • 永远不要明文存储私钥,必须加密后再进行持久化操作。

内容的提问来源于stack exchange,提问作者Hithendra Nath

火山引擎 最新活动