Har*_*ani 25 security encryption android sharedpreferences
SharedPreferences存储在Android应用中的常见位置是:
/data/data/<package name>/shared_prefs/<filename.xml>
Run Code Online (Sandbox Code Playgroud)
具有root权限的用户可以导航到此位置并可以更改其值.保护它的需要非常重要.
我们可以通过多少种方式加密整个shared_pref's xml文件?
我们都知道我们可以加密并保存shared_pref's xml文件中的数据,但这不仅100%安全,因此需要用密钥加密整个文件.需要帮助了解加密整个xml文件的各种方法.这是一般性问题,这里讨论的各种加密方法可以帮助所有开发人员保护应用程序.
Boj*_*man 43
您应该注意Android的共享首选项是基于XML键值的.你无法改变这个事实(因为它会破坏它的解析器),最多你可以加密密钥和值,所以root用户可以阅读,但不会有任何想法他正在阅读.
为此,您可以使用这样的简单加密
String masterKeyAlias = MasterKeys.getOrCreate(MasterKeys.AES256_GCM_SPEC);
SharedPreferences sharedPreferences = EncryptedSharedPreferences.create(
"secret_shared_prefs",
masterKeyAlias,
context,
EncryptedSharedPreferences.PrefKeyEncryptionScheme.AES256_SIV,
EncryptedSharedPreferences.PrefValueEncryptionScheme.AES256_GCM
);
// use the shared preferences and editor as you normally would
SharedPreferences.Editor editor = sharedPreferences.edit();
Run Code Online (Sandbox Code Playgroud)
这就是你如何使用它
String masterKeyAlias = MasterKeys.getOrCreate(MasterKeys.AES256_GCM_SPEC);
SharedPreferences sharedPreferences = EncryptedSharedPreferences.create(
"secret_shared_prefs",
masterKeyAlias,
context,
EncryptedSharedPreferences.PrefKeyEncryptionScheme.AES256_SIV,
EncryptedSharedPreferences.PrefValueEncryptionScheme.AES256_GCM
);
// use the shared preferences and editor as you normally would
SharedPreferences.Editor editor = sharedPreferences.edit();
Run Code Online (Sandbox Code Playgroud)
你应该知道强硬,__CODE__从来没有建立安全,这只是一种坚持数据的简单方法.
您应该也知道我使用的加密并不是最安全的,但它很简单.
有几个库可以提供更好的加密,就像这些
但他们都认为文件的格式仍然是XML,而且它是基于键值的.你无法改变这一事实.见下文.
String masterKeyAlias = MasterKeys.getOrCreate(MasterKeys.AES256_GCM_SPEC);
SharedPreferences sharedPreferences = EncryptedSharedPreferences.create(
"secret_shared_prefs",
masterKeyAlias,
context,
EncryptedSharedPreferences.PrefKeyEncryptionScheme.AES256_SIV,
EncryptedSharedPreferences.PrefValueEncryptionScheme.AES256_GCM
);
// use the shared preferences and editor as you normally would
SharedPreferences.Editor editor = sharedPreferences.edit();
Run Code Online (Sandbox Code Playgroud)
如果安全性是一个问题,除了__CODE__基于键值和XML格式的事实之外,您还需要完全避免它.
F.M*_*sir 16
如果您想支持Android 5.0(API级别21)及以上
使用以下实现:
implementation "androidx.security:security-crypto:1.0.0-rc04"
Run Code Online (Sandbox Code Playgroud)
或者从这个来源获取最新的。
然后
首先创建主密钥如下:
val masterKey = MasterKey.Builder(context)
.setKeyScheme(MasterKey.KeyScheme.AES256_GCM)
.build()
Run Code Online (Sandbox Code Playgroud)
创建后shared preferences如下:
val sharedPreferences = EncryptedSharedPreferences.create(
context,
"secret_shared_prefs",
masterKey,
EncryptedSharedPreferences.PrefKeyEncryptionScheme.AES256_SIV,
EncryptedSharedPreferences.PrefValueEncryptionScheme.AES256_GCM
)
Run Code Online (Sandbox Code Playgroud)
然后像平常一样使用它,例如:
with(sharedPreferences.edit()) {
putString(Values.SP_USER_ID, personId)
putString(Values.SP_USER_NAME, binding.editTextTextPersonName.text.toString())
apply()
}
Run Code Online (Sandbox Code Playgroud)
小智 13
Google已将其EncryptedSharedPreferences作为androidx的一部分发布,我相信这应该是加密首选项的首选方法。
请参阅https://developer.android.com/reference/androidx/security/crypto/EncryptedSharedPreferences
完整答案(API 级别 23+)。首先你需要来自 androidx 的加密货币。
implementation "androidx.security:security-crypto:1.0.0-alpha02"
Run Code Online (Sandbox Code Playgroud)
注意:SharedPreferences 和 EncryptedSharedPreferences 之间存在显着的性能差异。您应该注意到EncryptedSharedPreferences.create(...)不是那么快,因此您应该必须存储一个实例。
然后您必须使用它来检索EncryptedSharedPreferences。
public SharedPreferences getEncryptedSharedPreferences(){
String masterKeyAlias = MasterKeys.getOrCreate(MasterKeys.AES256_GCM_SPEC);
SharedPreferences sharedPreferences = EncryptedSharedPreferences.create(
"secret_shared_prefs_file",
masterKeyAlias,
context,
EncryptedSharedPreferences.PrefKeyEncryptionScheme.AES256_SIV,
EncryptedSharedPreferences.PrefValueEncryptionScheme.AES256_GCM
);
return sharedPreferences;
}
Run Code Online (Sandbox Code Playgroud)
您只需使用“标准方式”之类的偏好即可。保存它:
getEncryptedSharedPreferences().edit()
.putString("ENCRYPTDATA", text)
.apply()
Run Code Online (Sandbox Code Playgroud)
检索偏好值。
getEncryptedSharedPreferences().getString("ENCRYPTDATA", "defvalue")
Run Code Online (Sandbox Code Playgroud)
您需要在 API 23 下处理 Verisons
fun providesSharedPreference(): SharedPreferences {
var sharedPreferences: SharedPreferences
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
sharedPreferences = EncryptedSharedPreferences.create(
application,
Constant.SHARED_PREFERENCE_NAME,
getMasterKey(),
EncryptedSharedPreferences.PrefKeyEncryptionScheme.AES256_SIV,
EncryptedSharedPreferences.PrefValueEncryptionScheme.AES256_GCM
)
} else {
sharedPreferences =
application.getSharedPreferences(
Constant.SHARED_PREFERENCE_NAME,
Context.MODE_PRIVATE
)
}
return sharedPreferences
}
private fun getMasterKey(): MasterKey {
return MasterKey.Builder(application)
.setKeyScheme(MasterKey.KeyScheme.AES256_GCM)
.build()
}
Run Code Online (Sandbox Code Playgroud)
您应该加密数据并写入SharedPreferences.如果要获取此数据,则应从SharedPreferences进行解密.你需要以下帮助类
public class Encryption {
private final Builder mBuilder;
private Encryption(Builder builder) {
mBuilder = builder;
}
public static Encryption getDefault(String key, String salt, byte[] iv) {
try {
return Builder.getDefaultBuilder(key, salt, iv).build();
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
return null;
}
}
private String encrypt(String data) throws UnsupportedEncodingException, NoSuchAlgorithmException, NoSuchPaddingException, InvalidAlgorithmParameterException, InvalidKeyException, InvalidKeySpecException, BadPaddingException, IllegalBlockSizeException {
if (data == null) return null;
SecretKey secretKey = getSecretKey(hashTheKey(mBuilder.getKey()));
byte[] dataBytes = data.getBytes(mBuilder.getCharsetName());
Cipher cipher = Cipher.getInstance(mBuilder.getAlgorithm());
cipher.init(Cipher.ENCRYPT_MODE, secretKey, mBuilder.getIvParameterSpec(), mBuilder.getSecureRandom());
return Base64.encodeToString(cipher.doFinal(dataBytes), mBuilder.getBase64Mode());
}
public String encryptOrNull(String data) {
try {
return encrypt(data);
} catch (Exception e) {
e.printStackTrace();
return "";
}
}
public void encryptAsync(final String data, final Callback callback) {
if (callback == null) return;
new Thread(new Runnable() {
@Override
public void run() {
try {
String encrypt = encrypt(data);
if (encrypt == null) {
callback.onError(new Exception("Encrypt return null, it normally occurs when you send a null data"));
}
callback.onSuccess(encrypt);
} catch (Exception e) {
callback.onError(e);
}
}
}).start();
}
private String decrypt(String data) throws UnsupportedEncodingException, NoSuchAlgorithmException, InvalidKeySpecException, NoSuchPaddingException, InvalidAlgorithmParameterException, InvalidKeyException, BadPaddingException, IllegalBlockSizeException {
if (data == null) return null;
byte[] dataBytes = Base64.decode(data, mBuilder.getBase64Mode());
SecretKey secretKey = getSecretKey(hashTheKey(mBuilder.getKey()));
Cipher cipher = Cipher.getInstance(mBuilder.getAlgorithm());
cipher.init(Cipher.DECRYPT_MODE, secretKey, mBuilder.getIvParameterSpec(), mBuilder.getSecureRandom());
byte[] dataBytesDecrypted = (cipher.doFinal(dataBytes));
return new String(dataBytesDecrypted);
}
public String decryptOrNull(String data) {
try {
return decrypt(data);
} catch (Exception e) {
e.printStackTrace();
return null;
}
}
public void decryptAsync(final String data, final Callback callback) {
if (callback == null) return;
new Thread(new Runnable() {
@Override
public void run() {
try {
String decrypt = decrypt(data);
if (decrypt == null) {
callback.onError(new Exception("Decrypt return null, it normally occurs when you send a null data"));
}
callback.onSuccess(decrypt);
} catch (Exception e) {
callback.onError(e);
}
}
}).start();
}
private SecretKey getSecretKey(char[] key) throws NoSuchAlgorithmException, UnsupportedEncodingException, InvalidKeySpecException {
SecretKeyFactory factory = SecretKeyFactory.getInstance(mBuilder.getSecretKeyType());
KeySpec spec = new PBEKeySpec(key, mBuilder.getSalt().getBytes(mBuilder.getCharsetName()), mBuilder.getIterationCount(), mBuilder.getKeyLength());
SecretKey tmp = factory.generateSecret(spec);
return new SecretKeySpec(tmp.getEncoded(), mBuilder.getKeyAlgorithm());
}
private char[] hashTheKey(String key) throws UnsupportedEncodingException, NoSuchAlgorithmException {
MessageDigest messageDigest = MessageDigest.getInstance(mBuilder.getDigestAlgorithm());
messageDigest.update(key.getBytes(mBuilder.getCharsetName()));
return Base64.encodeToString(messageDigest.digest(), Base64.NO_PADDING).toCharArray();
}
public interface Callback {
void onSuccess(String result);
void onError(Exception exception);
}
private static class Builder {
private byte[] mIv;
private int mKeyLength;
private int mBase64Mode;
private int mIterationCount;
private String mSalt;
private String mKey;
private String mAlgorithm;
private String mKeyAlgorithm;
private String mCharsetName;
private String mSecretKeyType;
private String mDigestAlgorithm;
private String mSecureRandomAlgorithm;
private SecureRandom mSecureRandom;
private IvParameterSpec mIvParameterSpec;
public static Builder getDefaultBuilder(String key, String salt, byte[] iv) {
return new Builder()
.setIv(iv)
.setKey(key)
.setSalt(salt)
.setKeyLength(128)
.setKeyAlgorithm("AES")
.setCharsetName("UTF8")
.setIterationCount(1)
.setDigestAlgorithm("SHA1")
.setBase64Mode(Base64.DEFAULT)
.setAlgorithm("AES/CBC/PKCS5Padding")
.setSecureRandomAlgorithm("SHA1PRNG")
.setSecretKeyType("PBKDF2WithHmacSHA1");
}
private Encryption build() throws NoSuchAlgorithmException {
setSecureRandom(SecureRandom.getInstance(getSecureRandomAlgorithm()));
setIvParameterSpec(new IvParameterSpec(getIv()));
return new Encryption(this);
}
private String getCharsetName() {
return mCharsetName;
}
private Builder setCharsetName(String charsetName) {
mCharsetName = charsetName;
return this;
}
private String getAlgorithm() {
return mAlgorithm;
}
private Builder setAlgorithm(String algorithm) {
mAlgorithm = algorithm;
return this;
}
private String getKeyAlgorithm() {
return mKeyAlgorithm;
}
private Builder setKeyAlgorithm(String keyAlgorithm) {
mKeyAlgorithm = keyAlgorithm;
return this;
}
private int getBase64Mode() {
return mBase64Mode;
}
private Builder setBase64Mode(int base64Mode) {
mBase64Mode = base64Mode;
return this;
}
private String getSecretKeyType() {
return mSecretKeyType;
}
private Builder setSecretKeyType(String secretKeyType) {
mSecretKeyType = secretKeyType;
return this;
}
private String getSalt() {
return mSalt;
}
private Builder setSalt(String salt) {
mSalt = salt;
return this;
}
private String getKey() {
return mKey;
}
private Builder setKey(String key) {
mKey = key;
return this;
}
private int getKeyLength() {
return mKeyLength;
}
public Builder setKeyLength(int keyLength) {
mKeyLength = keyLength;
return this;
}
private int getIterationCount() {
return mIterationCount;
}
public Builder setIterationCount(int iterationCount) {
mIterationCount = iterationCount;
return this;
}
private String getSecureRandomAlgorithm() {
return mSecureRandomAlgorithm;
}
public Builder setSecureRandomAlgorithm(String secureRandomAlgorithm) {
mSecureRandomAlgorithm = secureRandomAlgorithm;
return this;
}
private byte[] getIv() {
return mIv;
}
public Builder setIv(byte[] iv) {
mIv = iv;
return this;
}
private SecureRandom getSecureRandom() {
return mSecureRandom;
}
public Builder setSecureRandom(SecureRandom secureRandom) {
mSecureRandom = secureRandom;
return this;
}
private IvParameterSpec getIvParameterSpec() {
return mIvParameterSpec;
}
public Builder setIvParameterSpec(IvParameterSpec ivParameterSpec) {
mIvParameterSpec = ivParameterSpec;
return this;
}
private String getDigestAlgorithm() {
return mDigestAlgorithm;
}
public Builder setDigestAlgorithm(String digestAlgorithm) {
mDigestAlgorithm = digestAlgorithm;
return this;
}
}}
Run Code Online (Sandbox Code Playgroud)
然后,您可以通过加密数据来编写SharedPreferences,如下所示
Encryption encryption = Encryption.getDefault("Key", "Salt", new byte[16]);
SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(getApplicationContext());
SharedPreferences.Editor editor = preferences.edit();
editor.putString("token", encryption.encryptOrNull(userModel.getToken()));
editor.apply()
Run Code Online (Sandbox Code Playgroud)
您最终可以通过以下方式读取SharedPreferences数据.这样,敏感信息将更安全,同时保持在手机的硬件级别
final SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(getApplicationContext());
Encryption encryption = Encryption.getDefault("Key", "Salt", new byte[16]);
String token = encryption.decryptOrNull(preferences.getString("token",""));
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
39636 次 |
| 最近记录: |