Utilizar una subclase personalizada de SharedPreferences con PreferenceActivity o PreferenceFragment

Estoy usando una subclase personalizada de SharedPreferences para cifrar mi configuración guardada en la aplicación, similar a lo que se está haciendo en la segunda respuesta aquí: ¿Cuál es la forma más apropiada para almacenar la configuración de usuario en la aplicación de Android

El número de preferencias que tengo que guardar es cada vez mayor. Antes de que estuviera utilizando una vista personalizada para actualizar estas preferencias, pero que va a ser engorroso y quiero usar PreferenceActivity o PreferenceFragment en su lugar. El problema es que no parece que haya una forma de que cualquiera de esas clases acceda a mis datos usando mi subclase, lo que significa que los datos que extrae del archivo de preferencias predeterminado van a ser gibberes ya que no se descifran.

He descubierto que algunas personas han creado implementaciones personalizadas de Preferencia que cifran los datos allí, pero prefiero no hacerlo ya que los datos ya están siendo cifrados / descifrados en mi subclase SharedPreferences y me gustaría mantenerlo camino. También he estado buscando en el código fuente de PreferenceActivity y PreferenceManager y no estoy seguro de la mejor manera de abordar esto.

¿Alguien más ha tenido suerte realizando algo como esto y tiene alguna sugerencia sobre dónde podría empezar?

Creo que al mantener su cifrado en la subclase SharedPrefs que ya tiene, se limita la modularidad y la separación de las preocupaciones.

Por lo tanto, sugeriría reconsiderar la subclasificación de las clases de preferencia en sí (como CheckBoxPreference) y realizar su cálculo allí.

Lo ideal sería que también pudiera usar algún tipo de composición o una utilidad estática para que, si bien podría tener que subclase cada tipo de preferencia que utilice, puede utilizar un único lugar para realizar los cálculos de cifrado / descifrado. Esto también le permitiría más flexibilidad en el futuro si necesita cifrar o descifrar algunos otros datos o si la API cambia.

Para la subclase quizás podría hacer esto:

Así por ejemplo:

 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; } } 

NOTA lo anterior es psuedo-code, habría diferentes métodos para anular

Y su XML podría tener este aspecto:

 <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> 

EDITAR

Advertencias / Decompiling

A medida que pensaba en esto más, me di cuenta de una de las advertencias es que este método no es particularmente difícil de desviar cuando se descompila un APK. Esto da los nombres de clases completos de clases sobreescritas en los diseños (aunque esto puede evitarse si no se utiliza XML)

Sin embargo, no creo que esto sea mucho menos seguro que subcompartir SharedPreferences . Eso también, es susceptible a la descompilación. En última instancia, si desea una mayor seguridad, debe considerar métodos alternativos de almacenamiento. Tal vez OAuth o AccountManager como se sugiere en su publicación enlazada.

Qué tal esto:

  • Almacene un byte [16] en un .SO. Si no usas un .SO, entonces haz uno solo para ese propósito.
  • Utilice esa matriz de bytes para criptar un nuevo byte [16] y luego Base64 codificar el resultado. Hardcode que en su archivo de clase.

Ahora que has configurado las teclas, déjame explicar:

Sí, potencialmente uno podría echar un vistazo a la. SO y encontrar la matriz de bytes ergo su clave. Pero con la clave codificada 2 codificada en base64, necesitaría decodificarla e invertir el cifrado con dicha clave para extraer key2 bytes. Hasta ahora esto sólo implica desmontar la aplicación.

  • Cuando desee almacenar datos cifrados, primero realice un pase AES con clave1, luego un paso AES / CBC / Padding5 con Key2 y un paso IV *
  • Puede con seguridad Base64 codificar el IV y guardarlo así en su / data / data carpeta si desea cambiar el IV cada vez que una nueva contraseña se almacena.

Con estos dos pasos el desmontaje de la aplicación ya no es lo único que se requiere, ya que ahora es necesario que también tome el control de su tiempo de ejecución para llegar a los datos cifrados. Lo que tienes que decir es bastante suficiente para una contraseña almacenada.

Entonces usted podría almacenar simplemente eso en SharedPreferences 🙂 De esa manera si sus SharedPreferences consiguen comprometido, los datos todavía se traban lejos. No creo que subclasificar es realmente el enfoque correcto, pero ya que ya escribió su clase – oh bien.

He aquí un código para ilustrar lo que quiero decir

 //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"); } 

¿Qué piensas?

Me doy cuenta de que puede ser fuera de tema ya que está preguntando sobre el uso de sus propias SharedPreferences, pero estoy dando una solución de trabajo para el problema de almacenamiento de datos sensibles 🙂

  • ¿Cómo mantener la configuración del idioma de la aplicación después de reiniciar el dispositivo?
  • Método RingtonePreference getEntry
  • Cómo agregar un hipervínculo a una pantalla de preferencias (PreferenceActivity)
  • Cómo utilizar PreferenceFragment?
  • Android registerOnSharedPreferenceChangeListener () causa bloqueo en una vista personalizada
  • Lea SharedPreferences cuando no haya definido el nombre del archivo
  • ¿Dónde debe llamar a PreferenceManager.setDefaultValues?
  • PreferenciaActividad: guardar valor como entero
  • La forma más segura de usar SharedPreferences
  • Android: ¿cómo obtener la lista de todas las preferencias xml para mi aplicación y leerlas?
  • SharedPreference Cambios no reflejados en mi servicio de fondo de pantalla
  • FlipAndroid es un fan de Google para Android, Todo sobre Android Phones, Android Wear, Android Dev y Aplicaciones para Android Aplicaciones.