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.
- ¿Cuál es el nombre de archivo utilizado por defecto las preferencias compartidas?
- Preferencias anidadas.xml
- Uso de preferencias no predeterminadas en PreferenceActivity
- Obtener preferencias en AppWidget Provider
- Cómo utilizar addPreferencesFromResource para android 2.X?
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?
- Archivo de preferencias del usuario vs Archivo de preferencias de la aplicación
- ¿Por qué Custom DialogPreference no se activa en onSharedPreferenceChanged?
- Cómo establecer el valor predeterminado de un ListPreference
- Preferencias de Android: Valores predeterminados incorrectos DESPITE "setDefaultValues"
- SharedPreference Cambios no reflejados en mi servicio de fondo de pantalla
- Respondiendo a las actualizaciones de preferencias en Android
- Android: ¿Cómo restablecer / eliminar las preferencias de la aplicación durante las pruebas de unidad?
- Mostrar el botón de arriba en la barra de acción en las preferencias de la pantalla principal
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 🙂
- RecycleView que muestra sólo el primer elemento
- ¿Cómo puedo obtener el número de serie del teléfono (IMEI)