使用带有PreferenceActivity或PreferenceFragment的SharedPreferences自定义子类

我正在使用SharedPreferences的自定义子类来encryption我在应用程序中保存的设置,类似于第二个响应中所做的: 在Android应用程序中存储用户设置的最合适的方法是什么

我必须保存的偏好数量正在增长。 在我只是使用自定义视图来更新这些首选项之前,但这将变得繁琐,我想使用PreferenceActivity或PreferenceFragment来代替。 问题是,似乎没有办法让这两个类中的任何一个使用我的子类来访问我的数据,这意味着它从默认的首选项文件中获取的数据将会是无用的,因为它没有被解密。

我发现有些人已经创build了Preference的自定义实现来encryption那里的数据,但是我不希望这样做,因为数据已经在我的SharedPreferences子类中被encryption/解密了,我想保留它办法。 我也一直在寻找PreferenceActivity和PreferenceManager的源代码,我不知道最好的方法来处理这个。

有没有其他人有幸能完成这样的事情,并对我可能从哪里开始有任何build议?

Solutions Collecting From Web of "使用带有PreferenceActivity或PreferenceFragment的SharedPreferences自定义子类"

我想通过保留你已经拥有的SharedPrefs子类中的encryption,可以限制模块性和关注点分离。

所以我会build议重新考虑分类偏好类本身(如CheckBoxPreference)并在那里执行计算。

理想情况下,您也可以使用某种types的组合或静态实用程序,以便您可能必须对每种types的偏好进行子类化时,可以使用单个位置执行encryption/解密计算。 如果您需要encryption或解密一些其他数据或API发生更改,这也可以使您在将来获得更大的灵活性。

对于子分类,也许你可以这样做:

举个例子:

 class ListPreferenceCrypt extends ListPreference { ListPreferenceCrypt (Context context, AttributeSet attrs) { super ( context, attrs ); } ListPreferenceCrypt (Context context) { super ( context ); } @Override public void setValue( String value ) { //encrypt value String encryptedVal = MyCryptUtil.encrypt(value); super.setValue ( encryptedVal ); } @Override public String getValue( String key ) { //decrypt value String decryptedValue = MyCryptUtil.decrypt(super.getValue ( key )); return decryptedValue; } } 

注意上面是伪代码,会有不同的方法来覆盖

而你的XML可能是这样的:

 <PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android"> <PreferenceCategory android:title="@string/inline_preferences"> <com.example.myprefs.ListPreferenceCrypt android:key="listcrypt_preference" android:title="@string/title_listcrypt_preference" android:summary="@string/summary_listcrypt_preference" /> </PreferenceCategory> </PreferenceScreen> 

编辑

注意事项/反编译

当我想到更多的时候,我意识到其中一个需要注意的是,反编译APK时这种方法并不是特别困难。 这确实在布局中给出了重载类的完整类名(尽pipe可以通过不使用XML来避免)

但是,我不认为这比SharedPreferences子类安全性要低得多。 这也很容易反编译。 最终,如果你想要更强大的安全性,你应该考虑替代的存储方法。 也许OAuth或AccountManagerbuild议在您的链接的post。

这个怎么样:

  • 将一个字节[16]存储在.SO中。 如果你不使用.SO,那就只是为了这个目的。
  • 使用该字节数组来encryption一个新的字节[16],然后Base64对结果进行编码。 在你的类文件中的硬编码。

现在你已经设置了键让我解释一下:

是的,潜在的人可以偷看.SO,并find字节数组ergo您的密钥。 但是,随着encryption密钥2被base64编码,他将需要对其进行解码并使用所述密钥来反转encryption以提取密钥2字节。 到目前为止,这只涉及解散应用程序。

  • 当你想存储encryption数据时,首先用key1进行AES传递,然后用Key2和IV *进行AES / CBC / Padding5传递,
  • 如果你想在每次存储一个新的密码的时候改变IV的话,你可以安全地将Base64编码为IV,并保存到/ data / data文件夹中。

有了这两个步骤,反汇编应用程序不再是唯一需要的东西,因为它现在还需要控制你的运行时间来获取encryption的数据。 对于存储的密码来说,你必须说的是足够的。

然后,你可以简单地将它存储到SharedPreferences :)这样,如果你的SharedPreferences被攻破,数据仍然被locking。 我不认为inheritance它是真的是正确的方法,但因为你已经写了你的课 – 哦,好吧。

这里有一些代码来进一步说明我的意思

 //use to encrypt key public static byte[] encryptA(byte[] value) throws GeneralSecurityException, IOException { SecretKeySpec sks = getSecretKeySpec(true); System.err.println("encrypt():\t" + sks.toString()); Cipher cipher = Cipher.getInstance("AES"); cipher.init(Cipher.ENCRYPT_MODE, sks, cipher.getParameters()); byte[] encrypted = cipher.doFinal(value); return encrypted; } //use to encrypt data public static byte[] encrypt2(byte[] value) throws GeneralSecurityException, IOException { SecretKeySpec key1 = getSecretKeySpec(true); System.err.println("encrypt():\t" + key1.toString()); Cipher cipher = Cipher.getInstance("AES"); cipher.init(Cipher.ENCRYPT_MODE, key1, cipher.getParameters()); byte[] encrypted = cipher.doFinal(value); SecretKeySpec key2 = getSecretKeySpec(false); System.err.println("encrypt():\t" + key2.toString()); cipher = Cipher.getInstance("AES/CBC/PKCS5Padding"); cipher.init(Cipher.ENCRYPT_MODE, key2, new IvParameterSpec(getIV())); byte[] encrypted2 = cipher.doFinal(encrypted); return encrypted2;//Base64Coder.encode(encrypted2); } //use to decrypt data public static byte[] decrypt2(byte[] message, boolean A) throws GeneralSecurityException, IOException { SecretKeySpec key1 = getSecretKeySpec(false); System.err.println("decrypt():\t" + key1.toString()); Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding"); cipher.init(Cipher.DECRYPT_MODE, key1, new IvParameterSpec(getIV())); byte[] decrypted = cipher.doFinal(message); SecretKeySpec key2 = getSecretKeySpec(true); System.err.println("decrypt():\t" + key2.toString()); cipher = Cipher.getInstance("AES"); cipher.init(Cipher.DECRYPT_MODE, key2); byte[] decrypted2 = cipher.doFinal(decrypted); return decrypted2; } //use to decrypt key public static byte[] decryptKey(String message, byte[] key) throws GeneralSecurityException { SecretKeySpec sks = new SecretKeySpec(key, ALGORITHM); System.err.println("decryptKey()"); Cipher cipher = Cipher.getInstance("AES"); cipher.init(Cipher.DECRYPT_MODE, sks); byte[] decrypted = cipher.doFinal(Base64Coder.decode(message)); return decrypted; } //method for fetching keys private static SecretKeySpec getSecretKeySpec(boolean fromSO) throws NoSuchAlgorithmException, IOException, GeneralSecurityException { return new SecretKeySpec(fromSO ? getKeyBytesFromSO() : getKeyBytesFromAssets(), "AES"); } 

你怎么看?

我意识到这可能是脱离主题,因为你问使用自己的SharedPreferences,但我给你一个工作的解决scheme,存储敏感数据的问题:)