flutter_secure_storage icon indicating copy to clipboard operation
flutter_secure_storage copied to clipboard

BadPaddingException when storing JWT in Flutter Secure Storage on Android

Open ELMEHDAOUIAhmed opened this issue 11 months ago • 9 comments

Describe the bug

After a successful login, writing the new JWT (and other keys) to flutter_secure_storage sometimes throws a BadPaddingException deep inside Android’s KeyStore/EncryptedSharedPreferences, causing:

PlatformException(Exception encountered, read, javax.crypto.BadPaddingException: error:le000065:Cipher functions:OPENSSL_internal:BAD_DECRYPT at com.android.org.conscrypt.NativeCrypto.EVP_CipherFinal_ex(Native Method) … ,null)

This makes all subsequent reads/writes to that key fail until the user clears app data.

To Reproduce

  1. Use flutter_secure_storage (v9.2.4) with AndroidOptions(encryptedSharedPreferences: true).
  2. Log in and store a JWT (via secureStorage.write('jwt_token', token)).
  3. Occasionally you’ll see the above exception when writing or reading the token.
  4. Clearing app data (Settings → Apps → YourApp → Storage → Clear data) “fixes” it once, but the issue recurs.

Minimal repro code snippet (in your login controller):

try {
  await secureStorage.write("jwt_token", token);
} catch (e) {
  // ← BadPaddingException here
  debugPrint("Failed to store token: $e");
}

Even when trying to make a workaround it didnt help
Catch PlatformException on read/write, delete the bad key, then retry. E.g. in your SecureStorage wrapper:
@override
Future<void> write(String key, String value) async {
  try {
    await _secureStorage.write(key: key, value: value);
  } on PlatformException catch (e) {
    if (e.message?.contains('BadPaddingException') == true) {
      await _secureStorage.delete(key: key);
      await _secureStorage.write(key: key, value: value);
      return;
    }
    rethrow;
  }
}

Environment
Flutter 3.x
flutter_secure_storage: 9.2.4
Android emulator / device API 23+
encryptedSharedPreferences = true

ELMEHDAOUIAhmed avatar May 18 '25 15:05 ELMEHDAOUIAhmed

Did you solve this?

Amrazyan avatar May 22 '25 07:05 Amrazyan

Did you solve this?

Yes, but not through exception handling. For some reason, even if you try to catch and handle this particular exception, it doesn't work. After checking my code, I found that I was unintentionally overwriting a key with the same name. Interestingly, this issue doesn't occur with all keys. Without an official explanation, I can’t say for sure why, but for example: if I overwrite a key named "test", it doesn’t throw an exception it just updates the value. However, if I overwrite a key named "TEST", the exception is triggered. I'm glad I figured it out, but I don't have time to investigate further.

ELMEHDAOUIAhmed avatar May 22 '25 14:05 ELMEHDAOUIAhmed

Getting the same exception on trying to read on some Android devices and I'm not sure what could be causing this. Using default configurations.

final FlutterSecureStorage _secureStorage = const FlutterSecureStorage();

 @override
  Future<Either<AppError, String?>> get(String key) async {
    try {
      String? value = await _secureStorage.read(key: key);
      return Right(value);
    } catch (e, st) {
      Sentry.captureException(e, stackTrace: st);
      return Left(
        AppError(
          title: 'Get item from Secure Storage Failed',
          description: e.toString(),
        ),
      );
    }
  }

@override
  Future<Either<AppError, void>> set(String key, String? value) async {
    try {
      await _secureStorage.write(key: key, value: value);
      return const Right(null);
    } catch (e, st) {
      Sentry.captureException(e, stackTrace: st);
      return Left(
        AppError(
          title: 'Set item in Secure Storage Failed',
          description: e.toString(),
        ),
      );
    }
  }

agent515 avatar May 22 '25 14:05 agent515

Getting same exception on trying to read on some Android devices and I'm not sure what could be causing this. Using default configurations.

final FlutterSecureStorage _secureStorage = const FlutterSecureStorage();

@override Future<Either<AppError, String?>> get(String key) async { try { String? value = await _secureStorage.read(key: key); return Right(value); } catch (e, st) { Sentry.captureException(e, stackTrace: st); return Left( AppError( title: 'Get item from Secure Storage Failed', description: e.toString(), ), ); } }

@override Future<Either<AppError, void>> set(String key, String? value) async { try { await _secureStorage.write(key: key, value: value); return const Right(null); } catch (e, st) { Sentry.captureException(e, stackTrace: st); return Left( AppError( title: 'Set item in Secure Storage Failed', description: e.toString(), ), ); } }

You reminded me of something I tested it on a friend's Samsung A15 and it didn't trigger the exception, but on my Pixel 8 (physical device) it did.

ELMEHDAOUIAhmed avatar May 22 '25 15:05 ELMEHDAOUIAhmed

In my case, it's working on Pixel 7 (both real device and emulator) and not working on itel rs4 (real device)

agent515 avatar May 22 '25 15:05 agent515

Any solution for the issue?

agent515 avatar Jun 30 '25 05:06 agent515

This is what resolved the issue for me -

If you get this exception when reading a value, assume it’s unreadable → delete the key.

Future<Either<AppError, String?>> get(String key) async {
    try {
      String? value = await _secureStorage.read(key: key);
      return Right(value);
    } catch (e, st) {
      Sentry.captureException(e, stackTrace: st);
      // If decrypt fails, wipe seucre storage.
      final result = await deleteAll();
      return result.fold(
        (l) => Left(l),
        (r) => Right(null),
      );
    }
  }


Future<Either<AppError, void>> deleteAll() async {
    try {
      await _secureStorage.deleteAll();
      return const Right(null);
    } catch (e, st) {
      Sentry.captureException(e, stackTrace: st);
      return Left(
        AppError(
          title: 'Delete all items in Secure Storage Failed',
          description: e.toString(),
        ),
      );
    }
  }

agent515 avatar Jul 02 '25 13:07 agent515