¿Preferencias compartidas? Javax.crypto.BadPaddingException: bloque de pad corrompido sólo en algunos dispositivos

Estoy recibiendo algunos errores de google play consola donde algunos usuarios (Pixel XL, nexus 5 y Xperia Z3 +) están recibiendo

Caused by: java.lang.RuntimeException: javax.crypto.BadPaddingException: pad block corrupted at com.darwins.custom.ObscuredSharedPreferences.decrypt(ObscuredSharedPreferences.java:193) at com.darwins.custom.ObscuredSharedPreferences.getInt(ObscuredSharedPreferences.java:134) 

La aplicación está funcionando bien en el resto de dispositivos (incluso en algunos nexus 5 está funcionando bien)

El problema viene cuando la primera vez que el usuario abre la aplicación, intenta cargar el volumen de la música de las preferencias compartidas. Como nunca entraron en el menú de opciones para cambiar el valor por defecto, debería obtener el valor predeterminado:

 if(sp == null) sp = new ObscuredSharedPreferences(ctx, ctx.getSharedPreferences(SP_NAME, Context.MODE_PRIVATE)); if(musicVolume == -1) musicVolume = sp.getInt(KEY_SP_MUSIC_VOLUME,10); 

Si entramos en getInt de ObsucredSharedPreferences:

 @Override public int getInt(String key, int defValue) { final String v = delegate.getString(key, null); return v!=null ? Integer.parseInt(decrypt(v)) : defValue; } 

Así que en lugar de obtener el valor nulo de getString estoy recibiendo un valor como "ERKJFER89er" (nunca escribo ese valor en las preferencias, de lo contrario debería bloquearse en todos los teléfonos) por lo que cuando se trata de descifrar el valor esperado un valor int y Que lanza un javax.crypto.BadPaddingException: pad block corrupted No sé cómo solucionar esto o cómo arreglar esto, cualquier idea será apreciate

Código de descifrado:

 protected String decrypt(String value){ try { final byte[] bytes = value!=null ? Base64.decode(value,Base64.DEFAULT) : new byte[0]; SecretKeyFactory keyFactory = SecretKeyFactory.getInstance("PBEWithMD5AndDES"); SecretKey key = keyFactory.generateSecret(new PBEKeySpec(SEKRIT)); Cipher pbeCipher = Cipher.getInstance("PBEWithMD5AndDES"); pbeCipher.init(Cipher.DECRYPT_MODE, key, new PBEParameterSpec( Secure.getString(context.getContentResolver(), Secure.ANDROID_ID).getBytes(UTF8), 20)); return new String(pbeCipher.doFinal(bytes),UTF8); } catch( Exception e) { throw new RuntimeException(e); } } 

1 usuario dice que hacer un restablecimiento de fábrica no soluciona el problema, pero hacer un restablecimiento de fábrica con la caché de borrado y borrar los datos de resolverlo

Strace completo de la pila para el pixel de google

 java.lang.RuntimeException: Unable to start activity ComponentInfo{com.suduck.upgradethegame/com.darwins.cubegame.WelcomeActivity}: java.lang.RuntimeException: javax.crypto.BadPaddingException: pad block corrupted at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2665) at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2726) at android.app.ActivityThread.-wrap12(ActivityThread.java) at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1477) at android.os.Handler.dispatchMessage(Handler.java:102) at android.os.Looper.loop(Looper.java:154) at android.app.ActivityThread.main(ActivityThread.java:6119) at java.lang.reflect.Method.invoke(Native Method) at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:886) at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:776) Caused by: java.lang.RuntimeException: javax.crypto.BadPaddingException: pad block corrupted at com.darwins.custom.ObscuredSharedPreferences.decrypt(ObscuredSharedPreferences.java:193) at com.darwins.custom.ObscuredSharedPreferences.getInt(ObscuredSharedPreferences.java:134) at com.darwins.clases.Logro.<init>(Logro.java:41) at com.darwins.clases.LogrosManager.iniciar(LogrosManager.java:64) at com.darwins.clases.LogrosManager.<init>(LogrosManager.java:48) at com.darwins.motor.CEngine.Inicializar(CEngine.java:141) at com.darwins.superclases.CActividad.onCreate(CActividad.java:47) at com.darwins.cubegame.WelcomeActivity.onCreate(WelcomeActivity.java:32) at android.app.Activity.performCreate(Activity.java:6679) at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1118) at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2618) ... 9 more Caused by: javax.crypto.BadPaddingException: pad block corrupted at com.android.org.bouncycastle.jcajce.provider.symmetric.util.BaseBlockCipher$BufferedGenericBlockCipher.doFinal(BaseBlockCipher.java:1267) at com.android.org.bouncycastle.jcajce.provider.symmetric.util.BaseBlockCipher.engineDoFinal(BaseBlockCipher.java:1100) at javax.crypto.Cipher.doFinal(Cipher.java:2056) at com.darwins.custom.ObscuredSharedPreferences.decrypt(ObscuredSharedPreferences.java:190) 

No puedo decir que esto es mucho más que una suposición, pero voy a darle una oportunidad.

He visto a otros usar un valor predeterminado de null para SharedPreferences, pero me gusta usar el valor predeterminado real como una cadena. Basado en eso, tendría algo más como esto (suponiendo que hay un método de encryt() para ir con decrypt() ).

 @Override public int getInt(String key, int defValue) { final String v = delegate.getString(key, encrypt(String.valueOf(defValue)); return Integer.parseInt(decrypt(v)); } 

Si eso no soluciona el problema, ha intentado usar algo distinto de null como valor predeterminado. ¿Qué tal una cuerda vacía? Algunos caracteres unicode (s) que usted está seguro no puede ser un valor almacenado cifrado real? EDIT: Esto es improbable que el problema. Después de examinar la documentación, el parámetro defValue para getString() es Nullable, lo que significa que el método está diseñado para manejar un parámetro nulo con gracia.

Espero que esto ayude y buena suerte.

EDIT: He intentado duplicar su problema, pero no pudo. Intenté un dispositivo emulado Nexus 5 y un dispositivo Nexus 5 real, ambos utilizando API 23. getString() siempre devuelve null. Utilicé código basado en esto . Me imagino que es más o menos lo mismo de su código se basa.

Dejando fuera los métodos no utilizados …

 public class ObscuredSharedPreferences implements SharedPreferences { private static final String TAG = "ObscuredSp"; protected static final String UTF8 = "utf-8"; private static final char[] SEKRIT = "abc".toCharArray() ; // INSERT A RANDOM PASSWORD HERE. protected SharedPreferences delegate; protected Context context; public ObscuredSharedPreferences(Context context, SharedPreferences delegate) { this.delegate = delegate; this.context = context; } @Override public int getInt(String key, int defValue) { final String v = delegate.getString(key, null); Log.d(TAG, "got int " + v); return v!=null ? Integer.parseInt(decrypt(v)) : defValue; } protected String decrypt(String value){ try { final byte[] bytes = value!=null ? Base64.decode(value, Base64.DEFAULT) : new byte[0]; SecretKeyFactory keyFactory = SecretKeyFactory.getInstance("PBEWithMD5AndDES"); SecretKey key = keyFactory.generateSecret(new PBEKeySpec(SEKRIT)); Cipher pbeCipher = Cipher.getInstance("PBEWithMD5AndDES"); pbeCipher.init(Cipher.DECRYPT_MODE, key, new PBEParameterSpec(Settings.Secure.getString(context.getContentResolver(), Settings.Secure.ANDROID_ID).getBytes(UTF8), 20)); return new String(pbeCipher.doFinal(bytes),UTF8); } catch( Exception e) { throw new RuntimeException(e); } } } 

En onCreate()

 private static final String MY_PREFS_FILE_NAME = "MyFile"; private static final String KEY_SP_MUSIC_VOLUME = "KeySpMusicVol"; final SharedPreferences prefs = new ObscuredSharedPreferences( this, this.getSharedPreferences(MY_PREFS_FILE_NAME, Context.MODE_PRIVATE)); int musicVolume; musicVolume = prefs.getInt(KEY_SP_MUSIC_VOLUME, 10); Log.d(TAG, "volume = " + musicVolume); 

Usted podría intentar simplificar su código a algo como esto. Si esto funciona y el tuyo no, es sólo cuestión de agregar y quitar código hasta que determine qué causa el problema. (Sé que no es necesariamente tan fácil como eso hace que suene).

Otra pregunta, cosas para pensar. ¿Han probado tanto la configuración de compilación de depuración como de liberación? ¿Desinstala la aplicación cada vez para asegurarse de que es la primera vez que se ejecuta la aplicación? ¿Es el volumen la única preferencia con la que esto sucede?

He vuelto a leer tu pregunta y ahora me doy cuenta de que escribes los problemas que informa Google Play. Esto hace que la gran pregunta, ¿puede duplicar esto usted mismo tal que usted puede probar cosas diferentes para determinar la causa raíz?

¿Cuál es el resultado de devolución de decrypt(null) ? Parece que su aplicación lee algunos datos incorrectos que se escribieron incorrectamente antes.

También me di cuenta de que algunos dispositivos tienen un comportamiento diferente en la ruta de la carpeta de datos y probablemente causó este problema.

Una solución posible para su problema es registrar el contexto de falla de detalle incluyendo los datos que causaron error de descifrado. Puede probar algún servicio de registro en línea como Fabric o Logentries. O puede implementar su ExceptionHandler global, guardar los datos y enviar los datos a usted cuando ocurrió un accidente.

IMO, prefiero guardar todos los datos en String y analizarlos en tiempo de ejecución en caso de cambio de formato de datos.

FYI. Aquí está mi aplicación de preferencia. Soporta la preferencia cifrada / codificada / cifrada guardada en SharedPreference. También es muy fácil de extender para apoyar las preferencias en línea.

https://github.com/passos/SimplePreferences/blob/master/library/src/main/java/com/ioenv/preferences/

FlipAndroid es un fan de Google para Android, Todo sobre Android Phones, Android Wear, Android Dev y Aplicaciones para Android Aplicaciones.