xamarin / AndroidX

AndroidX bindings for .NET for Android
MIT License
173 stars 44 forks source link

Java.Lang.UnsupportedOperationException: 'For encrypted files, please open the relevant FileInput/FileOutputStream.' #405

Open AlexanderVolkovDA opened 2 years ago

AlexanderVolkovDA commented 2 years ago

Version Information

Describe your Issue:

When trying to open encrypted stream to read file with AndroidX.Security.Crypto.EncryptedFile.OpenFileInput() exception Java.Lang.UnsupportedOperationException with message: 'For encrypted files, please open the relevant FileInput/FileOutputStream.' is thrown

Steps to Reproduce (with link to sample solution if possible):

        string masterKeyAlias = MasterKeys.GetOrCreate(MasterKeys.Aes256GcmSpec);
        Java.IO.File file = new File(Android.App.Application.Context.FilesDir, Settings.Settings.DataFileName);
        bool fileExists = file.Exists();
        if (fileExists)
        {
            file.Delete();
        }

        AndroidX.Security.Crypto.EncryptedFile.Builder builder = new EncryptedFile.Builder(
            file,
            Android.App.Application.Context,
            masterKeyAlias,
            EncryptedFile.FileEncryptionScheme.Aes256GcmHkdf4kb);
        AndroidX.Security.Crypto.EncryptedFile encryptedFile = builder.Build();

        using (System.IO.Stream output = encryptedFile.OpenFileOutput())
        {
            await output.WriteAsync(Encoding.UTF8.GetBytes("Just sample text"));
        }

        using (System.IO.Stream input = encryptedFile.OpenFileInput())
        {
            int notReachable = input.ReadByte();
        }

Include any relevant Exception Stack traces, build logs, adb logs:

  at Java.Interop.JniEnvironment+InstanceMethods.CallObjectMethod (Java.Interop.JniObjectReference instance, Java.Interop.JniMethodInfo method, Java.Interop.JniArgumentValue* args) [0x0006e] in <bd6bd528a8784b7caf03e9f25c9f0d7b>:0 
  at Java.Interop.JniPeerMembers+JniInstanceMethods.InvokeVirtualObjectMethod (System.String encodedMember, Java.Interop.IJavaPeerable self, Java.Interop.JniArgumentValue* parameters) [0x0002a] in <bd6bd528a8784b7caf03e9f25c9f0d7b>:0 
  at Java.IO.FileInputStream.get_Channel () [0x00000] in /Users/builder/azdo/_work/1/s/xamarin-android/src/Mono.Android/obj/Release/monoandroid10/android-30/mcw/Java.IO.FileInputStream.cs:160 
  at Android.Runtime.InputStreamInvoker..ctor (Java.IO.InputStream stream) [0x00025] in /Users/builder/azdo/_work/1/s/xamarin-android/src/Mono.Android/Android.Runtime/InputStreamInvoker.cs:21 
  at (wrapper remoting-invoke-with-check) Android.Runtime.InputStreamInvoker..ctor(Java.IO.InputStream)
  at Android.Runtime.InputStreamInvoker.FromJniHandle (System.IntPtr handle, Android.Runtime.JniHandleOwnership transfer) [0x00035] in /Users/builder/azdo/_work/1/s/xamarin-android/src/Mono.Android/Android.Runtime/InputStreamInvoker.cs:168 
  at AndroidX.Security.Crypto.EncryptedFile.OpenFileInput () [0x00018] in D:\a\1\s\generated\androidx.security.security-crypto\obj\Release\monoandroid9.0\generated\src\AndroidX.Security.Crypto.EncryptedFile.cs:223 
  at ProactisMobileApp.Data.ExpenseData.WriteDataFile (System.String text) [0x00227] in D:\Work\Projects\Proactis\MobileApp\ProactisMobileApp\ProactisMobileApp\ProactisMobileApp\Data\ExpenseData.cs:324 
  --- End of managed Java.Lang.UnsupportedOperationException stack trace ---
java.lang.UnsupportedOperationException: For encrypted files, please open the relevant FileInput/FileOutputStream.
    at androidx.security.crypto.EncryptedFile$EncryptedFileInputStream.getChannel(EncryptedFile.java:319)
    at mono.java.lang.RunnableImplementor.n_run(Native Method)
    at mono.java.lang.RunnableImplementor.run(RunnableImplementor.java:30)
    at android.os.Handler.handleCallback(Handler.java:883)
    at android.os.Handler.dispatchMessage(Handler.java:100)
    at android.os.Looper.loop(Looper.java:235)
    at android.app.ActivityThread.main(ActivityThread.java:7441)
    at java.lang.reflect.Method.invoke(Native Method)
    at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:492)
    at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:936)
AlexanderVolkovDA commented 2 years ago

Tried to investigate the issue. Have a guess that it is connected to mapping of Java androidx.security.crypto.EncryptedFile.openFileOutput() return type from FileInputStream to System.IO.Stream. Somewhere in initialization it calls FileInputStream.getChannel() that throws the exception. But FileInputStream.getChannel() looks like this:

    @Override
    public FileChannel getChannel() {
        throw new UnsupportedOperationException("For encrypted files, please open the "
                + "relevant FileInput/FileOutputStream.");
    }
moljac commented 2 years ago

Are you using Android.Support?

moljac commented 2 years ago

Tried to investigate the issue. Have a guess that it is connected to mapping of Java androidx.security.crypto.EncryptedFile.openFileOutput() return type from FileInputStream to System.IO.Stream. Somewhere in initialization it calls FileInputStream.getChannel() that throws the exception. But FileInputStream.getChannel() looks like this:

    @Override
    public FileChannel getChannel() {
        throw new UnsupportedOperationException("For encrypted files, please open the "
                + "relevant FileInput/FileOutputStream.");
    }

True. So, this is not issue with the bindings.

You need to see what must be done in java or kotlin (native) and then port that code. This exception would happen with java or kotlin.

AlexanderVolkovDA commented 2 years ago

this is not issue with the bindings.

I thought that the issue is in mapping to general System.IO.Stream class instead more specific one (that is used in native).

You need to see what must be done in java or kotlin (native) and then port that code.

That's exactly what I've tried to do. In java/kotlin call of androidx.security.crypto.EncryptedFile.openFileOutput() does not lead to call of FileInputStream.getChannel(). But in C# it does.

From android documentation sample usage is: https://developer.android.com/reference/androidx/security/crypto/EncryptedFile

  String masterKeyAlias = MasterKeys.getOrCreate(MasterKeys.AES256_GCM_SPEC);

  File file = new File(context.getFilesDir(), "secret_data");
  EncryptedFile encryptedFile = EncryptedFile.Builder(
      file,
      context,
      masterKeyAlias,
      EncryptedFile.FileEncryptionScheme.AES256_GCM_HKDF_4KB
  ).build();

  // write to the encrypted file
  FileOutputStream encryptedOutputStream = encryptedFile.openFileOutput();

  // read the encrypted file
  FileInputStream encryptedInputStream = encryptedFile.openFileInput();

So, as you can see in steps to reproduce, I tried to do the same in my code (just added small file existence check, usings and explicit types), but opening the file always throws this exception.

Maybe you could introduce FileInputStream class, similar to native one with no getChannel() call on file open?

Are you using Android.Support?

Yes, we use some:

Xamarin.Android.Support.Design 27.0.2.1 Xamarin.Android.Support.v4 27.0.2.1 Xamarin.Android.Support.v7.AppCompat 27.0.2.1 Xamarin.Android.Support.v7.CardView 27.0.2.1 Xamarin.Android.Support.v7.MediaRouter 27.0.2.1

rgroenewoudt commented 1 year ago

I'm running into the same issue with the latest 1.1.0-alpha05.