keepass2android icon indicating copy to clipboard operation
keepass2android copied to clipboard

[BUG] Format_Badbase64Char

Open Harald-Koschinski opened this issue 7 months ago • 15 comments

Checks

  • [x] I have read the FAQ section, searched the open issues, and still think this is a new bug.

Describe the bug you encountered:

Since today - after Changing the Database - I get now this error when opening the Database. No Problem to open the Database in keepassXC and keepassDX

Describe what you expected to happen:

No response

What version of Keepass2Android are you using?

1.13-r0

Which version of Android are you on?

15

Harald-Koschinski avatar Jul 20 '25 15:07 Harald-Koschinski

can you create a sample database without sensitive data and a password you can share which exhibits the same behavior?

PhilippC avatar Jul 22 '25 05:07 PhilippC

This happened to me some weeks ago and still exists for one of my databases. Version 1.12

I'm not sure, but it could be that it appeared after I changed the DB with the keepass plug-in of Nextcloud.

24.07.2025 00:11:07:414 -- AppSettingsActivity.OnPause 7
24.07.2025 00:11:07:422 -- SelectCurrentDbActivity.OnStart 2
24.07.2025 00:11:07:424 -- SelectCurrentDbActivity 2: OnActivityResult Canceled/1
24.07.2025 00:11:07:424 -- TryGetFromActivityResult: no data
24.07.2025 00:11:07:428 -- Task in activity SelectCurrentDbActivity 2 changed to NullTask
24.07.2025 00:11:07:428 -- Finishing SelectCurrentDbActivity 2
24.07.2025 00:11:07:454 -- Finishing SelectCurrentDbActivity 2
24.07.2025 00:11:07:461 -- FileSelect.OnStart
24.07.2025 00:11:07:507 -- PasswordActivity.OnDestroyTrue 6
24.07.2025 00:11:07:545 -- PasswordActivity.OnCreate 8
24.07.2025 00:11:07:545 -- PasswordActivity:apptask= 8
24.07.2025 00:11:07:566 -- GetIocFromLaunchIntent()
24.07.2025 00:11:07:567 -- no keyprovider specified
24.07.2025 00:11:07:567 -- Reset keyfile
24.07.2025 00:11:07:571 -- PasswordActivity.OnStart 8
24.07.2025 00:11:07:572 -- PasswordActivity.OnResume 8
24.07.2025 00:11:07:572 --  DB null 8
24.07.2025 00:11:07:572 -- starting: True, Finishing: False, _performingLoad: False
24.07.2025 00:11:07:574 -- content://passwd.kdbx isCached = True
24.07.2025 00:11:07:575 -- Pre-loading database file starting
24.07.2025 00:11:07:578 -- content://passwd.kdbx isCached = True
24.07.2025 00:11:07:581 -- content://passwd.kdbx localVersionHash = 2E873E4D3348C5E907D2E1EFBA3722BB6EECA18217173C7A8BFC1F699DFC5439
24.07.2025 00:11:07:583 -- content://passwd.kdbx baseVersionHash = 2E873E4D3348C5E907D2E1EFBA3722BB6EECA18217173C7A8BFC1F699DFC5439
24.07.2025 00:11:07:583 -- CFS: OpenWhenNoLocalChanges
24.07.2025 00:11:07:584 -- CFS: hashing cached version
24.07.2025 00:11:07:590 -- CFS: Files in Sync
24.07.2025 00:11:07:594 -- Pre-loading database file completed
24.07.2025 00:11:07:609 -- PasswordModeSpinner item selected: 0
24.07.2025 00:11:07:644 -- parsing autofillStructure...
24.07.2025 00:11:07:644 -- Parsing done
24.07.2025 00:11:07:644 -- cannot autofill
24.07.2025 00:11:08:40 -- AppSettingsActivity.OnStop 7
24.07.2025 00:11:08:42 -- AppSettingsActivity.OnDestroyTrue 7
24.07.2025 00:11:08:77 -- SelectCurrentDbActivity.OnStop 2
24.07.2025 00:11:08:77 -- SelectCurrentDbActivity.OnDestroyTrue 2
24.07.2025 00:11:08:81 -- FileSelect.OnStop
24.07.2025 00:11:08:101 -- FileSelect.OnDestroyTrue
24.07.2025 00:11:17:289 -- Java.Lang.SecurityException: Permission Denial: opening provider androidx.core.content.FileProvider from ProcessRecord{7144093 30684:keepass2android.keepass2android/u0a434} (pid=30684, uid=10434) that is not exported from UID 10349
 ---> Android.Util.AndroidException: Remote stack trace:
	at com.android.server.am.ContentProviderHelper.checkAssociationAndPermissionLocked(qb/96856480 4207cc64f8273a078ed00bb37ec66a12d547258ca33788ccc3647eb1c250b266:88)
	at com.android.server.am.ContentProviderHelper.getContentProviderImpl(qb/96856480 4207cc64f8273a078ed00bb37ec66a12d547258ca33788ccc3647eb1c250b266:62)
	at com.android.server.am.ContentProviderHelper.getContentProvider(qb/96856480 4207cc64f8273a078ed00bb37ec66a12d547258ca33788ccc3647eb1c250b266:52)
	at com.android.server.am.ActivityManagerService.getContentProvider(qb/96856480 4207cc64f8273a078ed00bb37ec66a12d547258ca33788ccc3647eb1c250b266:114)
	at android.app.IActivityManager$Stub.onTransact$getContentProvider$(IActivityManager.java:12177)


  --- End of managed Android.Util.AndroidException stack trace ---
android.os.RemoteException: Remote stack trace:
	at com.android.server.am.ContentProviderHelper.checkAssociationAndPermissionLocked(qb/96856480 4207cc64f8273a078ed00bb37ec66a12d547258ca33788ccc3647eb1c250b266:88)
	at com.android.server.am.ContentProviderHelper.getContentProviderImpl(qb/96856480 4207cc64f8273a078ed00bb37ec66a12d547258ca33788ccc3647eb1c250b266:62)
	at com.android.server.am.ContentProviderHelper.getContentProvider(qb/96856480 4207cc64f8273a078ed00bb37ec66a12d547258ca33788ccc3647eb1c250b266:52)
	at com.android.server.am.ActivityManagerService.getContentProvider(qb/96856480 4207cc64f8273a078ed00bb37ec66a12d547258ca33788ccc3647eb1c250b266:114)
	at android.app.IActivityManager$Stub.onTransact$getContentProvider$(IActivityManager.java:12177)


  --- End of managed Android.Util.AndroidException stack trace ---
android.os.RemoteException: Remote stack trace:
	at com.android.server.am.ContentProviderHelper.checkAssociationAndPermissionLocked(qb/96856480 4207cc64f8273a078ed00bb37ec66a12d547258ca33788ccc3647eb1c250b266:88)
	at com.android.server.am.ContentProviderHelper.getContentProviderImpl(qb/96856480 4207cc64f8273a078ed00bb37ec66a12d547258ca33788ccc3647eb1c250b266:62)
	at com.android.server.am.ContentProviderHelper.getContentProvider(qb/96856480 4207cc64f8273a078ed00bb37ec66a12d547258ca33788ccc3647eb1c250b266:52)
	at com.android.server.am.ActivityManagerService.getContentProvider(qb/96856480 4207cc64f8273a078ed00bb37ec66a12d547258ca33788ccc3647eb1c250b266:114)
	at android.app.IActivityManager$Stub.onTransact$getContentProvider$(IActivityManager.java:12177)


   Exception_EndOfInnerExceptionStack
   at Java.Interop.JniEnvironment.InstanceMethods.CallNonvirtualObjectMethod(JniObjectReference , JniObjectReference , JniMethodInfo , JniArgumentValue* )
   at Java.Interop.JniPeerMembers.JniInstanceMethods.InvokeNonvirtualObjectMethod(String , IJavaPeerable , JniArgumentValue* )
   at Android.Content.ContentResolver.Query(Uri , String[] , String , String[] , String , CancellationSignal )
   at keepass2android.Io.AndroidContentStorage.TryGetDisplayName(IOConnectionInfo ioc, String& displayName)
  --- End of managed Java.Lang.SecurityException stack trace ---
java.lang.SecurityException: Permission Denial: opening provider androidx.core.content.FileProvider from ProcessRecord{7144093 30684:keepass2android.keepass2android/u0a434} (pid=30684, uid=10434) that is not exported from UID 10349
	at android.os.Parcel.createExceptionOrNull(Parcel.java:3257)
	at android.os.Parcel.createException(Parcel.java:3241)
	at android.os.Parcel.readException(Parcel.java:3224)
	at android.os.Parcel.readException(Parcel.java:3166)
	at android.app.IActivityManager$Stub$Proxy.getContentProvider(IActivityManager.java:6532)
	at android.app.ActivityThread.acquireProvider(ActivityThread.java:8732)
	at android.app.ContextImpl$ApplicationContentResolver.acquireUnstableProvider(ContextImpl.java:3952)
	at android.content.ContentResolver.acquireUnstableProvider(ContentResolver.java:2555)
	at android.content.ContentResolver.query(ContentResolver.java:1217)
	at android.content.ContentResolver.query(ContentResolver.java:1165)
	at mono.android.view.View_OnClickListenerImplementor.n_onClick(Native Method)
	at mono.android.view.View_OnClickListenerImplementor.onClick(View_OnClickListenerImplementor.java:31)
	at android.view.View.performClick(View.java:8464)
	at android.widget.TextView.performClick(TextView.java:18389)
	at com.google.android.material.button.MaterialButton.performClick(MaterialButton.java:1218)
	at android.view.View.performClickInternal(View.java:8441)
	at android.view.View.-$$Nest$mperformClickInternal(Unknown Source:0)
	at android.view.View$PerformClick.run(View.java:32966)
	at android.os.Handler.handleCallback(Handler.java:959)
	at android.os.Handler.dispatchMessage(Handler.java:100)
	at android.os.Looper.loopOnce(Looper.java:257)
	at android.os.Looper.loop(Looper.java:342)
	at android.app.ActivityThread.main(ActivityThread.java:9634)
	at java.lang.reflect.Method.invoke(Native Method)
	at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:619)
	at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:929)
Caused by: android.os.RemoteException: Remote stack trace:
	at com.android.server.am.ContentProviderHelper.checkAssociationAndPermissionLocked(qb/96856480 4207cc64f8273a078ed00bb37ec66a12d547258ca33788ccc3647eb1c250b266:88)
	at com.android.server.am.ContentProviderHelper.getContentProviderImpl(qb/96856480 4207cc64f8273a078ed00bb37ec66a12d547258ca33788ccc3647eb1c250b266:62)
	at com.android.server.am.ContentProviderHelper.getContentProvider(qb/96856480 4207cc64f8273a078ed00bb37ec66a12d547258ca33788ccc3647eb1c250b266:52)
	at com.android.server.am.ActivityManagerService.getContentProvider(qb/96856480 4207cc64f8273a078ed00bb37ec66a12d547258ca33788ccc3647eb1c250b266:114)
	at android.app.IActivityManager$Stub.onTransact$getContentProvider$(IActivityManager.java:12177)


  --- End of managed Java.Lang.SecurityException stack trace ---
java.lang.SecurityException: Permission Denial: opening provider androidx.core.content.FileProvider from ProcessRecord{7144093 30684:keepass2android.keepass2android/u0a434} (pid=30684, uid=10434) that is not exported from UID 10349
	at android.os.Parcel.createExceptionOrNull(Parcel.java:3257)
	at android.os.Parcel.createException(Parcel.java:3241)
	at android.os.Parcel.readException(Parcel.java:3224)
	at android.os.Parcel.readException(Parcel.java:3166)
	at android.app.IActivityManager$Stub$Proxy.getContentProvider(IActivityManager.java:6532)
	at android.app.ActivityThread.acquireProvider(ActivityThread.java:8732)
	at android.app.ContextImpl$ApplicationContentResolver.acquireUnstableProvider(ContextImpl.java:3952)
	at android.content.ContentResolver.acquireUnstableProvider(ContentResolver.java:2555)
	at android.content.ContentResolver.query(ContentResolver.java:1217)
	at android.content.ContentResolver.query(ContentResolver.java:1165)
	at mono.android.view.View_OnClickListenerImplementor.n_onClick(Native Method)
	at mono.android.view.View_OnClickListenerImplementor.onClick(View_OnClickListenerImplementor.java:31)
	at android.view.View.performClick(View.java:8464)
	at android.widget.TextView.performClick(TextView.java:18389)
	at com.google.android.material.button.MaterialButton.performClick(MaterialButton.java:1218)
	at android.view.View.performClickInternal(View.java:8441)
	at android.view.View.-$$Nest$mperformClickInternal(Unknown Source:0)
	at android.view.View$PerformClick.run(View.java:32966)
	at android.os.Handler.handleCallback(Handler.java:959)
	at android.os.Handler.dispatchMessage(Handler.java:100)
	at android.os.Looper.loopOnce(Looper.java:257)
	at android.os.Looper.loop(Looper.java:342)
	at android.app.ActivityThread.main(ActivityThread.java:9634)
	at java.lang.reflect.Method.invoke(Native Method)
	at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:619)
	at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:929)
Caused by: android.os.RemoteException: Remote stack trace:
	at com.android.server.am.ContentProviderHelper.checkAssociationAndPermissionLocked(qb/96856480 4207cc64f8273a078ed00bb37ec66a12d547258ca33788ccc3647eb1c250b266:88)
	at com.android.server.am.ContentProviderHelper.getContentProviderImpl(qb/96856480 4207cc64f8273a078ed00bb37ec66a12d547258ca33788ccc3647eb1c250b266:62)
	at com.android.server.am.ContentProviderHelper.getContentProvider(qb/96856480 4207cc64f8273a078ed00bb37ec66a12d547258ca33788ccc3647eb1c250b266:52)
	at com.android.server.am.ActivityManagerService.getContentProvider(qb/96856480 4207cc64f8273a078ed00bb37ec66a12d547258ca33788ccc3647eb1c250b266:114)
	at android.app.IActivityManager$Stub.onTransact$getContentProvider$(IActivityManager.java:12177)


24.07.2025 00:11:17:290 -- content://org.nextcloud.files/external_files/emulated/0/Android/media/com.nextcloud.client/nextcloud/.../passwd.kdbx isCached = True
24.07.2025 00:11:17:291 -- LockingActivity: OnActivityResult 
24.07.2025 00:11:17:291 -- PasswordActivity.OnActivityResult 874348/1000
24.07.2025 00:11:17:299 -- status message: Initializing...
24.07.2025 00:11:17:300 -- status submessage: 
24.07.2025 00:11:17:341 -- status message: Lade Datenbank…
24.07.2025 00:11:17:346 -- status submessage: Schlüsseltransformationen werden durchgeführt …
24.07.2025 00:11:17:430 -- status submessage: Lese Datenbank ein…
24.07.2025 00:11:17:436 -- System.FormatException: Format_BadBase64Char
   at System.Convert.FromBase64CharPtr(Char* , Int32 )
   at System.Convert.FromBase64String(String s)
   at KeePassLib.Serialization.KdbxFile.ReadBase64(XmlReader xr, Boolean bRaw)
   at KeePassLib.Serialization.KdbxFile.ReadTime(XmlReader xr)
   at KeePassLib.Serialization.KdbxFile.ReadXmlElement(KdbContext ctx, XmlReader xr)
   at KeePassLib.Serialization.KdbxFile.ReadDocumentStreamed(XmlReader xr, Stream sParentStream)
   at KeePassLib.Serialization.KdbxFile.ReadXmlStreamed(Stream sXml, Stream sParent)
   at KeePassLib.Serialization.KdbxFile.Load(Stream sSource, KdbxFormat fmt, IStatusLogger slLogger)
   at keepass2android.KdbxDatabaseFormat.PopulateDatabaseFromStream(PwDatabase db, Stream s, IStatusLogger slLogger)
   at KeePassLib.PwDatabase.Open(Stream s, String fileNameWithoutPathAndExt, IOConnectionInfo ioSource, CompositeKey pwKey, IStatusLogger slLogger, IDatabaseFormat format)
   at keepass2android.Database.PopulateDatabaseFromStream(PwDatabase pwDatabase, Stream s, IOConnectionInfo iocInfo, CompositeKey compositeKey, ProgressDialogStatusLogger status, IDatabaseFormat databaseFormat)
   at keepass2android.Database.LoadData(IKp2aApp app, IOConnectionInfo iocInfo, MemoryStream databaseData, CompositeKey compositeKey, ProgressDialogStatusLogger status, IDatabaseFormat databaseFormat)
   at keepass2android.Kp2aApp.LoadDatabase(IOConnectionInfo ioConnectionInfo, MemoryStream memoryStream, CompositeKey compositeKey, ProgressDialogStatusLogger statusLogger, IDatabaseFormat databaseFormat, Boolean makeCurrent)
   at keepass2android.LoadDb.TryLoad(MemoryStream databaseStream)
   at keepass2android.LoadDb.Run()
24.07.2025 00:11:21:273 -- PasswordActivity.OnPause 8
24.07.2025 00:11:21:302 -- AppSettingsActivity.OnCreate 9
24.07.2025 00:11:21:302 -- AppSettingsActivity:apptask= 9
24.07.2025 00:11:21:306 -- AppSettingsActivity.OnStart 9
24.07.2025 00:11:21:307 -- AppSettingsActivity.OnResume 9
24.07.2025 00:11:21:307 --  DB null 9
24.07.2025 00:11:21:815 -- PasswordActivity.OnStop 8

lordyavin avatar Jul 23 '25 22:07 lordyavin

I exported the file and discovered that the icons are saved with Base64 encoding. So I removed all custom icons from my database and, et voilà, it can be opened again. So at least a workaround exists.

lordyavin avatar Jul 23 '25 22:07 lordyavin

With the following Java app one may identify the problematic icons in an exported version of the db.

import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.text.MessageFormat;
import java.util.Base64;
import java.util.Base64.Decoder;

import javax.xml.parsers.ParserConfigurationException;
import javax.xml.parsers.SAXParser;
import javax.xml.parsers.SAXParserFactory;

import org.xml.sax.Attributes;
import org.xml.sax.SAXException;
import org.xml.sax.ext.DefaultHandler2;

public class KeePassXmlCheck {

  static class Icon {
    String uuid;
    byte[] data;
  }

  public static void main(String[] args)
      throws ParserConfigurationException, SAXException, IOException {
    for (String filepath : args) {
      check(Path.of(filepath));
    }
  }

  private static void check(Path filepath)
      throws ParserConfigurationException, SAXException, IOException {
    SAXParser saxParser = SAXParserFactory.newInstance().newSAXParser();
    try (var in = Files.newInputStream(filepath)) {
      saxParser.parse(
          in,
          new DefaultHandler2() {
            private Decoder decoder = Base64.getDecoder();
            private KeePassXmlCheck.Icon icon;
            private String characters;
            private int numError;

            @Override
            public void startElement(
                String uri, String localName, String qName, Attributes attributes)
                throws SAXException {
              if (qName.equals("Icon")) {
                icon = new Icon();
              }
            }

            @Override
            public void characters(char[] ch, int start, int length) throws SAXException {
              characters = String.copyValueOf(ch, start, length);
            }

            @Override
            public void endElement(String uri, String localName, String qName) throws SAXException {
              if (qName.equals("UUID")) {
                icon.uuid = characters;
              } else if (qName.equals("Data")) {
                try {
                  // System.out.println(MessageFormat.format("Decoding `{0}`", icon.uuid));
                  icon.data = decoder.decode(characters);
                } catch (IllegalArgumentException e) {
                  System.err.println(
                      MessageFormat.format(
                          "Error: {0} - {1} : {2}",
                          filepath.getFileName(), icon.uuid, e.getMessage()));
                  // System.err.println("Data = " + characters);
                  numError++;
                }
              }
            }

            @Override
            public void endDocument() throws SAXException {
              System.err.println(filepath.getFileName() + ": Number of error: " + numError);
            }
          });
    }
  }
}

lordyavin avatar Jul 23 '25 23:07 lordyavin

Finally looked at the code to provide a quickfix. The exception happend while reading a time. Turns out, it probably has nothing to do with icon data, but removing icons did help. So the icon replacement done by KeePassXC updated the times of entries.

@PhilippC did you clone and own sources of various libraries to compose your app? If so, how do you keep track with evolution of those libs?

lordyavin avatar Jul 24 '25 06:07 lordyavin

The problem appeared as I openend my file in different systems because I need a new desktop app because keeweb is no more usable for me. In keepassXC I used the funny Icon-Importer. Now I am back to keepass2android on my mobile and don't have the base64 problem. I can not say when it was gone.

Harald-Koschinski avatar Jul 24 '25 07:07 Harald-Koschinski

Can anybody tell how these invalid files have been created? Is there a reproducible way to do so? If that is the case, I'd like to see an example of such a file and of course also report the issue to the author's of the corresponding KeePass port. Because IMHO no app should write an invalid file, and it's not the responsibility of all other ports to somehow swallow invalid data.

@lordyavin thanks a lot for the investigations on this topic! I'll reply on your PR.

Regarding

did you clone and own sources of various libraries to compose your app? If so, how do you keep track with evolution of those libs?

Are you referring to the KeePass 2.x source code? Yes, I cloned this and also had to do a couple of modifications. Unfortunately the KeePass 2 author wasn't willing to merge them in the original source code, so it remains a challenge to keep the implementations in sync. I am watching their releases on sourceforge but only "merge" when there is a relevant change.

PhilippC avatar Jul 29 '25 14:07 PhilippC

This is most likely a KeeWeb issue, a specific KeePass client (which also happens to have a plugin implementation on Nextcloud). I've experienced no such issues with other clients; I haven't tested definitively but have a strong suspicion that the 4.x database implementation is broken in KeeWeb. I encountered this issue recently and managed to fix it seemingly by reverting the database file version to 3.x. Regardless, definitely agree it is not the fault of this repository - it's a problem with another client breaking database compatibility.

SpectralCascade avatar Aug 03 '25 15:08 SpectralCascade

I can confirm that I used KeeWeb 1.18.7 on Nextcloud and I think that this client caused the issue. @SpectralCascade are you aware of existing a KeeWeb issue about this? If I've got some spare time I will try to reproduce the error and raise an issue at their issue tracker.

Since there are a lot KeePass clients out there, I think it is good to be robust against such uncritical file corruptions and give the user the chance to open it any way (read-only?) or the repair it. I guess being robust or providing a repair function is more feature than a bug.

lordyavin avatar Aug 03 '25 15:08 lordyavin

Hm, it quite unlikely to get a fix in KeeWeb: https://github.com/keeweb/keeweb/issues/2022

lordyavin avatar Aug 03 '25 16:08 lordyavin

Possibly related issues:

  • https://github.com/keeweb/keeweb/issues/910
  • https://github.com/keeweb/keeweb/issues/1520

lordyavin avatar Aug 03 '25 16:08 lordyavin

I am unsure whether there is a related KeeWeb issue already; and indeed KeeWeb are unlikely to fix it until that project acquires a new maintainer unfortunately. As for this project - it does seem excessive to implement a whole feature for attempting to load a broken/corrupt database. Perhaps in such event however a popup instead of an error toast should be displayed, so the user is made aware that the database is corrupt or incompatible (with the error message) and briefly warn about using other clients that might be unstable/unmaintained.

SpectralCascade avatar Aug 03 '25 17:08 SpectralCascade

Hi, encountered the same problem since I started using the KeeWeb app in Nextcloud. The KeepassXC desktop app has no issue with the Keepass database since used in KeeWeb/Nextcloud, but the Android Keepass2Android can no longer open the db and fails to load the file while throwing "Format_BadBase64Char" error message.

big-bvanbouwel avatar Oct 07 '25 08:10 big-bvanbouwel

Happend again. Since there is no maintainer for Nextclouds Keepass and the underlying KeeWeb, I removed it not from my installation. BTW: Editing the entry, previously added with Keeweb, with KeepassXC fixed the file.

lordyavin avatar Nov 25 '25 19:11 lordyavin

Any update for this issue? I encountered the same problem, too.

PiggyRan avatar Dec 10 '25 10:12 PiggyRan