Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Shared preferences? javax.crypto.BadPaddingException: pad block corrupted only in some devices

I am getting some errors from google play console where some users ( Pixel XL, nexus 5 and Xperia Z3+) are getting

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)

The app is working fine in the rest of devices ( even in some nexus 5 is working fine)

The problem come when the first time that the user open the app, It try to load the music volume from shared preferences. As they never entered in the options menu to change the default value, It should get the default value:

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

If we enter in getInt from ObsucredSharedPreferences:

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

So Instead of getting the null value from getString I am getting a value like "ERKJFER89er" (I never write that value in the preferences, otherwise it should crash on every phone) so when It try to decryp the value it expect an int value and it throws a javax.crypto.BadPaddingException: pad block corrupted I don't know how to workaround this or how to fix this, any idea will be apreciate

Code of decrypt:

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 user say that do a factory reset doesn't solve the problem but do a factory reset with wipe cache and wipe data solve it

Full stack strace for google pixel

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)
like image 867
D4rWiNS Avatar asked Oct 25 '16 10:10

D4rWiNS


1 Answers

I can't say this is much more than a guess but I'll give it a try.

I've seen others use a default of null for SharedPreferences but I like to use the actual default value as a string. Based on that, I would have something more like this (assuming there's an encryt() method to go with decrypt()).

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

If that doesn't fix the problem, have you tried using something other than null as the default value. How about an empty string? Some unicode character(s) that you're sure can't be a real encrypted stored value? EDIT: This is unlikely the problem. After looking at documentation, the defValue parameter for getString() is Nullable, meaning that the method is designed to handle a null parameter gracefully.

Hope this helps and good luck.

EDIT: I've tried to duplicate your problem but couldn't. I tried a Nexus 5 emulated device and a Nexus 5 real device, both using API 23. getString() always returned null. I used code based on this. I imagine it's more or less the same your code is based from.

Leaving out the unused methods...

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

In 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);

You could try simplifying your code to something like this. If this works and and yours doesn't, it's just a matter of adding & removing code until you determine what causes the problem. (I know it's not necessarily as easy as that makes it sound).

Other question, things to think about. Have your tried both debug and release build configs? Do you uninstall the app each time to make sure it's really the 1st time the app is run? Is volume the only preference that this happens with?

I've went back and re-read your question & I now notice that you write the problems are reported by Google Play. This makes the big question, can you duplicate this yourself such that you can try different things to determine the root cause?

like image 168
Gary99 Avatar answered Sep 28 '22 04:09

Gary99