如何加密SharedPreferences中Firebase Auth登录凭据并安全解密?
我来帮你一步步解决这个问题,涉及加密存储、密钥安全和Jetpack方案的Java实现,咱们挨个说清楚:
1. 加密存储与解密登录的核心实现
首先你需要一套加解密逻辑来处理邮箱和密码,推荐用AES对称加密算法(加解密共用同一密钥)。先写一个简单的加密工具类,后续再解决密钥的安全存储问题:
import javax.crypto.Cipher; import javax.crypto.spec.SecretKeySpec; import android.util.Base64; public class AESEncryptor { private static final String ALGORITHM = "AES"; private final SecretKeySpec secretKey; public AESEncryptor(byte[] key) { this.secretKey = new SecretKeySpec(key, ALGORITHM); } // 加密明文数据 public String encrypt(String data) throws Exception { Cipher cipher = Cipher.getInstance(ALGORITHM); cipher.init(Cipher.ENCRYPT_MODE, secretKey); byte[] encryptedBytes = cipher.doFinal(data.getBytes()); return Base64.encodeToString(encryptedBytes, Base64.DEFAULT); } // 解密加密后的数据 public String decrypt(String encryptedData) throws Exception { Cipher cipher = Cipher.getInstance(ALGORITHM); cipher.init(Cipher.DECRYPT_MODE, secretKey); byte[] decodedBytes = Base64.decode(encryptedData, Base64.DEFAULT); byte[] decryptedBytes = cipher.doFinal(decodedBytes); return new String(decryptedBytes); } }
修改你的存储代码,加密后再存入SharedPreferences:
if (Cb_RememberMe.isChecked()) { try { // 这里的密钥后续会从安全存储获取,先预留调用 byte[] key = getKeyFromSecureStore(); AESEncryptor encryptor = new AESEncryptor(key); String encryptedEmail = encryptor.encrypt(email); String encryptedPassword = encryptor.encrypt(password); getSharedPreferences(PREFS_NAME, MODE_PRIVATE) .edit() .putString(PREF_USERNAME, encryptedEmail) .putString(PREF_PASSWORD, encryptedPassword) .apply(); } catch (Exception e) { e.printStackTrace(); // 处理加密异常,比如提示用户"记住我"功能暂时不可用 } }
解密后直接用于Firebase登录:
SharedPreferences prefs = getSharedPreferences(PREFS_NAME, MODE_PRIVATE); String encryptedEmail = prefs.getString(PREF_USERNAME, null); String encryptedPassword = prefs.getString(PREF_PASSWORD, null); if (encryptedEmail != null && encryptedPassword != null) { try { byte[] key = getKeyFromSecureStore(); AESEncryptor encryptor = new AESEncryptor(key); String email = encryptor.decrypt(encryptedEmail); String password = encryptor.decrypt(encryptedPassword); // 调用Firebase登录接口 FirebaseAuth.getInstance().signInWithEmailAndPassword(email, password) .addOnCompleteListener(task -> { if (task.isSuccessful()) { // 登录成功逻辑 } else { // 登录失败逻辑,比如清除无效的存储数据 } }); } catch (Exception e) { e.printStackTrace(); prefs.edit().remove(PREF_USERNAME).remove(PREF_PASSWORD).apply(); } }
把密钥存在SharedPreferences和明文存密码没有本质区别,完全不安全。Android提供了KeyStore这个系统级安全存储,密钥会被系统加密后存储,只有你的应用能访问,甚至在Root设备上也有一定防护能力。
下面是Java代码实现从KeyStore生成并获取AES密钥:
import android.security.keystore.KeyGenParameterSpec; import android.security.keystore.KeyProperties; import java.security.KeyStore; import javax.crypto.KeyGenerator; import javax.crypto.SecretKey; public class KeyStoreHelper { private static final String KEYSTORE_ALIAS = "firebase_login_key"; private static final String KEYSTORE_PROVIDER = "AndroidKeyStore"; // 获取或创建密钥(不存在则自动生成) public static SecretKey getOrCreateKey() throws Exception { KeyStore keyStore = KeyStore.getInstance(KEYSTORE_PROVIDER); keyStore.load(null); // 检查密钥是否已存在 if (keyStore.containsAlias(KEYSTORE_ALIAS)) { return (SecretKey) keyStore.getKey(KEYSTORE_ALIAS, null); } // 生成符合安全标准的AES密钥 KeyGenerator keyGenerator = KeyGenerator.getInstance( KeyProperties.KEY_ALGORITHM_AES, KEYSTORE_PROVIDER); KeyGenParameterSpec spec = new KeyGenParameterSpec.Builder( KEYSTORE_ALIAS, KeyProperties.PURPOSE_ENCRYPT | KeyProperties.PURPOSE_DECRYPT) .setBlockModes(KeyProperties.BLOCK_MODE_CBC) .setEncryptionPaddings(KeyProperties.ENCRYPTION_PADDING_PKCS7) .setUserAuthenticationRequired(false) // 不需要生物识别验证时设为false .build(); keyGenerator.init(spec); return keyGenerator.generateKey(); } // 对应之前预留的getKeyFromSecureStore方法 public static byte[] getKeyFromSecureStore() throws Exception { SecretKey secretKey = getOrCreateKey(); return secretKey.getEncoded(); } }
3. Jetpack Security的Java实现:官方推荐的简化方案
Jetpack Security封装了EncryptedSharedPreferences,帮你搞定加解密和密钥管理,不用自己写加密工具类,更省心可靠。
首先添加依赖(在app/build.gradle的dependencies块中):
implementation "androidx.security:security-crypto:1.1.0-alpha06"
然后用Java代码创建加密版的SharedPreferences:
import androidx.security.crypto.EncryptedSharedPreferences; import androidx.security.crypto.MasterKey; import android.content.Context; import android.content.SharedPreferences; public class SecurePrefsHelper { public static SharedPreferences getEncryptedSharedPreferences(Context context) throws Exception { // 用KeyStore管理的MasterKey,自动处理密钥生成与存储 MasterKey masterKey = new MasterKey.Builder(context) .setKeyScheme(MasterKey.KeyScheme.AES256_GCM) .build(); // 创建加密的SharedPreferences return EncryptedSharedPreferences.create( context, "encrypted_firebase_prefs", // 加密存储的文件名 masterKey, EncryptedSharedPreferences.PrefKeyEncryptionScheme.AES256_SIV, EncryptedSharedPreferences.PrefValueEncryptionScheme.AES256_GCM ); } }
修改你的存储和登录代码,直接用这个加密存储:
// 存储时 if (Cb_RememberMe.isChecked()) { try { SharedPreferences encryptedPrefs = SecurePrefsHelper.getEncryptedSharedPreferences(this); encryptedPrefs.edit() .putString(PREF_USERNAME, email) .putString(PREF_PASSWORD, password) .apply(); } catch (Exception e) { e.printStackTrace(); } } // 读取并登录时 try { SharedPreferences encryptedPrefs = SecurePrefsHelper.getEncryptedSharedPreferences(this); String email = encryptedPrefs.getString(PREF_USERNAME, null); String password = encryptedPrefs.getString(PREF_PASSWORD, null); if (email != null && password != null) { FirebaseAuth.getInstance().signInWithEmailAndPassword(email, password) .addOnCompleteListener(/* 你的登录回调逻辑 */); } } catch (Exception e) { e.printStackTrace(); }
总结
- 密钥绝对不能存在SharedPreferences,优先用Android KeyStore或Jetpack Security的MasterKey管理
- 官方推荐用Jetpack Security的
EncryptedSharedPreferences,简化开发且更符合安全规范 - 解密后的明文邮箱密码可以直接传给Firebase的
signInWithEmailAndPassword接口使用
内容的提问来源于stack exchange,提问作者Ben
相关产品推荐
相关产品推荐

