Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[regression/8.0.0] [Maui][Android] Signature/MAC verification failed #18230

Open
devWR opened this issue Oct 22, 2023 · 35 comments
Open

[regression/8.0.0] [Maui][Android] Signature/MAC verification failed #18230

devWR opened this issue Oct 22, 2023 · 35 comments
Labels
area-tooling XAML & C# Hot Reload, XAML Editor, Live Visual Tree, Live Preview, Debugging partner/cat 😻 this is an issue that impacts one of our partners or a customer our advisory team is engaged with platform/android 🤖 potential-regression This issue described a possible regression on a currently supported version., verification pending s/needs-attention Issue has more information and needs another look t/bug Something isn't working

Comments

@devWR
Copy link

devWR commented Oct 22, 2023

Description

Since I updated to .NET8 RC2 SecureStorage.SetAsync does not work. It throws Javax.Crypto.AEADBadTagException with the following message: "Signature/MAC verification failed".
Issue reproduced using Samsung A40 with Android 11. Case happens when backup rules exclude mauiessentials.xml file.

Below you can find stacktrace:

  --- End of managed Android.Security.KeyStoreException stack trace ---
android.security.KeyStoreException: Signature/MAC verification failed
	at android.security.KeyStore.getKeyStoreException(KeyStore.java:1461)
	at android.security.keystore.KeyStoreCryptoOperationChunkedStreamer.doFinal(KeyStoreCryptoOperationChunkedStreamer.java:186)
	at android.security.keystore.AndroidKeyStoreAuthenticatedAESCipherSpi$BufferAllOutputUntilDoFinalStreamer.doFinal(AndroidKeyStoreAuthenticatedAESCipherSpi.java:373)
	at android.security.keystore.AndroidKeyStoreCipherSpiBase.engineDoFinal(AndroidKeyStoreCipherSpiBase.java:506)
	at javax.crypto.Cipher.doFinal(Cipher.java:2113)
	at com.google.crypto.tink.integration.android.AndroidKeystoreAesGcm.decryptInternal(AndroidKeystoreAesGcm.java:118)
	at com.google.crypto.tink.integration.android.AndroidKeystoreAesGcm.decrypt(AndroidKeystoreAesGcm.java:101)
	at com.google.crypto.tink.KeysetHandle.decrypt(KeysetHandle.java:993)
	at com.google.crypto.tink.KeysetHandle.readWithAssociatedData(KeysetHandle.java:878)
	at com.google.crypto.tink.KeysetHandle.read(KeysetHandle.java:859)
	at com.google.crypto.tink.integration.android.AndroidKeysetManager$Builder.readMasterkeyDecryptAndParseKeyset(AndroidKeysetManager.java:378)
	at com.google.crypto.tink.integration.android.AndroidKeysetManager$Builder.build(AndroidKeysetManager.java:298)
	at androidx.security.crypto.EncryptedSharedPreferences.create(EncryptedSharedPreferences.java:169)
	at androidx.security.crypto.EncryptedSharedPreferences.create(EncryptedSharedPreferences.java:130)

Steps to Reproduce

Invoke Microsoft.Maui.Storage.SecureStorage.SetAsync

Link to public reproduction project repository

No response

Version with bug

8.0.0-rc.2.9373

Is this a regression from previous behavior?

Yes, this used to work in .NET MAUI

Last version that worked well

8.0.0-rc.1.9171

Affected platforms

Android

Affected platform versions

No response

Did you find any workaround?

Yes

Issue doesn't happen when, in manifest, android:allowBackup is set to false as following:
<application android:allowBackup="false" ... >

Relevant log output

No response

@devWR devWR added the t/bug Something isn't working label Oct 22, 2023
@jsuarezruiz jsuarezruiz added platform/android 🤖 area-tooling XAML & C# Hot Reload, XAML Editor, Live Visual Tree, Live Preview, Debugging labels Oct 23, 2023
@samhouts samhouts added the partner/android Issues for the Android SDK label Oct 23, 2023
@PureWeen PureWeen added this to the .NET 8 GA milestone Oct 24, 2023
@PureWeen PureWeen added the potential-regression This issue described a possible regression on a currently supported version., verification pending label Oct 24, 2023
@PureWeen
Copy link
Member

@moljac

@moljac
Copy link
Contributor

moljac commented Oct 24, 2023

@devWR

Can I see your AndroidManifest.xml?

if you have

<application android:allowBackup="true" ... >

true is default I think.

try setting it to

<application android:allowBackup="false" ... >

There are also other workarounds, but I'll need to dive in deeper.

And please report whether that worked.

@Redth Redth added partner/cat 😻 this is an issue that impacts one of our partners or a customer our advisory team is engaged with and removed partner/cat 😻 this is an issue that impacts one of our partners or a customer our advisory team is engaged with labels Oct 25, 2023
@Redth Redth modified the milestones: .NET 8 GA, .NET 8 SR1 Oct 25, 2023
@Redth Redth added the s/needs-info Issue needs more info from the author label Oct 25, 2023
@ghost
Copy link

ghost commented Oct 25, 2023

Hi @devWR. We have added the "s/needs-info" label to this issue, which indicates that we have an open question for you before we can take further action. This issue will be closed automatically in 7 days if we do not hear back from you by then - please feel free to re-open it if you come back to this issue after that time.

@devWR
Copy link
Author

devWR commented Oct 26, 2023

Strange case. I just tested it on Samsung A51 with Android 12 and issue doesn't happen.
@moljac, I've tried your workaround and it worked, thank you.
Do you have any idea where the issue comes from? It started happening since I've upgraded to RC2. Before allowBackup was set to true. After issue started happening I added into manifest:

android:fullBackupContent="@xml/auto_backup_rules"

where auto_backup_rules is as following:

<?xml version="1.0" encoding="utf-8"?>
<full-backup-content>
  <include domain="sharedpref" path="."/>
  <exclude domain="sharedpref" path="${applicationId}.mauiessentials.xml"/>
</full-backup-content> 

@ghost ghost added s/needs-attention Issue has more information and needs another look and removed s/needs-info Issue needs more info from the author labels Oct 26, 2023
@moljac
Copy link
Contributor

moljac commented Oct 26, 2023

Do you have any idea where the issue comes from?

Not completely. I checked native Android issues on SO and github and saw that as quick-n-dirty workaround. There are some other workarounds, but thosw will need deeper dives and curently I am not sure if I will have time for that.

@devWR
Copy link
Author

devWR commented Oct 27, 2023

Thank you for the workaround then. I believe that some solution shall be found as soon as possible because there is a number of applicaitons that need to store data securely - tokens, etc.

@samhouts samhouts changed the title [Maui][Android][.NET8 RC2] Signature/MAC verification failed [regression/8.0.0] [Maui][Android] Signature/MAC verification failed Nov 9, 2023
@cropyai
Copy link

cropyai commented Nov 18, 2023

@devWR

Can I see your AndroidManifest.xml?

if you have

<application android:allowBackup="true" ... >

true is default I think.

try setting it to

<application android:allowBackup="false" ... >

There are also other workarounds, but I'll need to dive in deeper.

And please report whether that worked.

I can confirm the issue happening in my case. The other workaround is to clear the storage and cache of the app from app settings.

@fekberg
Copy link

fekberg commented Dec 13, 2023

Same issue here but the workaround isn't working for me at all.

@RBeaubien
Copy link

I had the same issue. setting allowBackup to false fixed it for me.

@plppp2001
Copy link

plppp2001 commented Dec 14, 2023

Same issue here but the workaround isn't working for me at all.

I'm having the same issue, (on an actual samsung device - Release APK installation only). I'll try this workaround... not sure if it'll help.... it worked so far, but this issue needs to resolved @maui team

** UPDATE this work around worked for me so far, but I'll have to retest this again later.

@BurkusCat
Copy link
Contributor

where auto_backup_rules is as following:

@devWR what backup rules did you end up using that fixed the issue? Or did you not get it resolved and needed to fully disable backup?

@denhaandrei
Copy link

The same for me in 8.0.7 version

@EvgenyMuryshkin
Copy link

The workaround does not work. The exception message said something about wrong encryption keys being used for storage.

I had an old version of the app deployed, but after upgrading to the latest Visual Studio (17.9) app started to crash.
Also, there was an Android software update during this time, so not sure what exactly contributed.

Removing the app and deploying from scratch helps.

@jukkaasikainen
Copy link

jukkaasikainen commented Feb 29, 2024

Below is the working auto_backup_rules.xml from under the \Platforms\Android\Resources\xml folder:

Edit: See below for correct examples

@BurkusCat
Copy link
Contributor

BurkusCat commented Mar 19, 2024

Below is the working auto_backup_rules.xml from under the \Platforms\Android\Resources\xml folder:

Unfortunately, even with those rules on MAUI 8.0.10 I'm still getting secure storage exceptions:
Javax.Crypto.AEADBadTagException

I imagine a workaround is to disable backup entirely but that isn't a great solution since it means users will lose any historical data/statistics if they moved devices/uninstalled.

Stack trace

[mono-rt] [ERROR] FATAL UNHANDLED EXCEPTION: Javax.Crypto.AEADBadTagException: Exception of type 'Javax.Crypto.AEADBadTagException' was thrown.
[mono-rt]  ---> Android.Security.KeyStoreException: Signature/MAC verification failed (internal Keystore code: -30 message: system/security/keystore2/src/operation.rs:850: KeystoreOperation::finish
[mono-rt] 
[mono-rt] Caused by:
[mono-rt]     0: system/security/keystore2/src/operation.rs:426: Finish failed.
[mono-rt]     1: Error::Km(r#VERIFICATION_FAILED))
[mono-rt] 
[mono-rt]   --- End of managed Android.Security.KeyStoreException stack trace ---
[mono-rt] android.security.KeyStoreException: Signature/MAC verification failed (internal Keystore code: -30 message: system/security/keystore2/src/operation.rs:850: KeystoreOperation::finish
[mono-rt] 
[mono-rt] Caused by:
[mono-rt]     0: system/security/keystore2/src/operation.rs:426: Finish failed.
[mono-rt]     1: Error::Km(r#VERIFICATION_FAILED)) (public error code: 10 internal Keystore code: -30)
[mono-rt] 	at android.security.KeyStore2.getKeyStoreException(KeyStore2.java:386)
[mono-rt] 	at android.security.KeyStoreOperation.handleExceptions(KeyStoreOperation.java:78)
[mono-rt] 	at android.security.KeyStoreOperation.finish(KeyStoreOperation.java:128)
[mono-rt] 	at android.security.keystore2.KeyStoreCryptoOperationChunkedStreamer$MainDataStream.finish(KeyStoreCryptoOperationChunkedStreamer.java:228)
[mono-rt] 	at android.security.keystore2.KeyStoreCryptoOperationChunkedStreamer.doFinal(KeyStoreCryptoOperationChunkedStreamer.java:181)
[mono-rt] 	at android.security.keystore2.AndroidKeyStoreAuthenticatedAESCipherSpi$BufferAllOutputUntilDoFinalStreamer.doFinal(AndroidKeyStoreAuthenticatedAESCipherSpi.java:396)
[mono-rt] 	at android.security.keystore2.AndroidKeyStoreCipherSpiBase.engineDoFinal(AndroidKeyStoreCipherSpiBase.java:618)
[mono-rt] 	at javax.crypto.Cipher.doFinal(Cipher.java:2114)
[mono-rt] 	at com.google.crypto.tink.integration.android.AndroidKeystoreAesGcm.decryptInternal(AndroidKeystoreAesGcm.java:118)
[mono-rt] 	at com.google.crypto.tink.integration.android.AndroidKeystoreAesGcm.decrypt(AndroidKeystoreAesGcm.java:101)
[mono-rt] 	at com.google.crypto.tink.KeysetHandle.decrypt(KeysetHandle.java:993)
[mono-rt] 	at com.google.crypto.tink.KeysetHandle.readWithAssociatedData(KeysetHandle.java:878)
[mono-rt] 	at com.google.crypto.tink.KeysetHandle.read(KeysetHandle.java:859)
[mono-rt] 	at com.google.crypto.tink.integration.android.AndroidKeysetManager$Builder.readMasterkeyDecryptAndParseKeyset(AndroidKeysetManager.java:378)
[mono-rt] 	at com.google.crypto.tink.integration.android.AndroidKeysetManager$Builder.build(AndroidKeysetManager.java:298)
[mono-rt] 	at androidx.security.crypto.EncryptedSharedPreferences.create(EncryptedSharedPreferences.java:169)
[mono-rt] 	at androidx.security.crypto.EncryptedSharedPreferences.create(EncryptedSharedPreferences.java:130)
[mono-rt] 	at mono.java.lang.RunnableImplementor.n_run(Native Method)
[mono-rt] 	at mono.java.lang.RunnableImplementor.run(RunnableImplementor.java:31)
[mono-rt] 	at android.os.Handler.handleCallback(Handler.java:958)
[mono-rt] 	at android.os.Handler.dispatchMessage(Handler.java:99)
[mono-rt] 	at android.os.Looper.loopOnce(Looper.java:205)
[mono-rt] 	at android.os.Looper.loop(Looper.java:294)
[mono-rt] 	at android.app.ActivityThread.main(ActivityThread.java:8248)
[mono-rt] 	at java.lang.reflect.Method.invoke(Native Method)
[mono-rt] 	at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:552)
[mono-rt] 	at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:971)
[mono-rt] 
[mono-rt]   --- End of managed Android.Security.KeyStoreException stack trace ---
[mono-rt] android.security.KeyStoreException: Signature/MAC verification failed (internal Keystore code: -30 message: system/security/keystore2/src/operation.rs:850: KeystoreOperation::finish
[mono-rt] 
[mono-rt] Caused by:
[mono-rt]     0: system/security/keystore2/src/operation.rs:426: Finish failed.
[mono-rt]     1: Error::Km(r#VERIFICATION_FAILED)) (public error code: 10 internal Keystore code: -30)
[mono-rt] 	at android.security.KeyStore2.getKeyStoreException(KeyStore2.java:386)
[mono-rt] 	at android.security.KeyStoreOperation.handleExceptions(KeyStoreOperation.java:78)
[mono-rt] 	at android.security.KeyStoreOperation.finish(KeyStoreOperation.java:128)
[mono-rt] 	at android.security.keystore2.KeyStoreCryptoOperationChunkedStreamer$MainDataStream.finish(KeyStoreCryptoOperationChunkedStreamer.java:228)
[mono-rt] 	at android.security.keystore2.KeyStoreCryptoOperationChunkedStreamer.doFinal(KeyStoreCryptoOperationChunkedStreamer.java:181)
[mono-rt] 	at android.security.keystore2.AndroidKeyStoreAuthenticatedAESCipherSpi$BufferAllOutputUntilDoFinalStreamer.doFinal(AndroidKeyStoreAuthenticatedAESCipherSpi.java:396)
[mono-rt] 	at android.security.keystore2.AndroidKeyStoreCipherSpiBase.engineDoFinal(AndroidKeyStoreCipherSpiBase.java:618)
[mono-rt] 	at javax.crypto.Cipher.doFinal(Cipher.java:2114)
[mono-rt] 	at com.google.crypto.tink.integration.android.AndroidKeystoreAesGcm.decryptInternal(AndroidKeystoreAesGcm.java:118)
[mono-rt] 	at com.google.crypto.tink.integration.android.AndroidKeystoreAesGcm.decrypt(AndroidKeystoreAesGcm.java:101)
[mono-rt] 	at com.google.crypto.tink.KeysetHandle.decrypt(KeysetHandle.java:993)
[mono-rt] 	at com.google.crypto.tink.KeysetHandle.readWithAssociatedData(KeysetHandle.java:878)
[mono-rt] 	at com.google.crypto.tink.KeysetHandle.read(KeysetHandle.java:859)
[mono-rt] 	at com.google.crypto.tink.integration.android.AndroidKeysetManager$Builder.readMasterkeyDecryptAndParseKeyset(AndroidKeysetManager.java:378)
[mono-rt] 	at com.google.crypto.tink.integration.android.AndroidKeysetManager$Builder.build(AndroidKeysetManager.java:298)
[mono-rt] 	at androidx.security.crypto.EncryptedSharedPreferences.create(EncryptedSharedPreferences.java:169)
[mono-rt] 	at androidx.security.crypto.EncryptedSharedPreferences.create(EncryptedSharedPreferences.java:130)
[mono-rt] 	at mono.java.lang.RunnableImplementor.n_run(Native Method)
[mono-rt] 	at mono.java.lang.RunnableImplementor.run(RunnableImplementor.java:31)
[mono-rt] 	at android.os.Handler.handleCallback(Handler.java:958)
[mono-rt] 	at android.os.Handler.dispatchMessage(Handler.java:99)
[mono-rt] 	at android.os.Looper.loopOnce(Looper.java:205)
[mono-rt] 	at android.os.Looper.loop(Looper.java:294)
[mono-rt] 	at android.app.ActivityThread.main(ActivityThread.java:8248)
[mono-rt] 	at java.lang.reflect.Method.invoke(Native Method)
[mono-rt] 	at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:552)
[mono-rt] 	at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:971)
[mono-rt] 
[mono-rt]    --- End of inner exception stack trace ---
[mono-rt]    at Java.Interop.JniEnvironment.StaticMethods.CallStaticObjectMethod(JniObjectReference type, JniMethodInfo method, JniArgumentValue* args) in /Users/runner/work/1/s/xamarin-android/external/Java.Interop/src/Java.Interop/obj/Release/net7.0/JniEnvironment.g.cs:line 21452
[mono-rt]    at Java.Interop.JniPeerMembers.JniStaticMethods.InvokeObjectMethod(String encodedMember, JniArgumentValue* parameters) in /Users/runner/work/1/s/xamarin-android/external/Java.Interop/src/Java.Interop/Java.Interop/JniPeerMembers.JniStaticMethods.cs:line 165
[mono-rt]    at AndroidX.Security.Crypto.EncryptedSharedPreferences.Create(Context context, String fileName, MasterKey masterKey, PrefKeyEncryptionScheme prefKeyEncryptionScheme, PrefValueEncryptionScheme prefValueEncryptionScheme) in C:\a\_work\1\s\generated\androidx.security.security-crypto\obj\Release
et6.0-android\generated\src\AndroidX.Security.Crypto.EncryptedSharedPreferences.cs:line 227
[mono-rt]    at Microsoft.Maui.Storage.SecureStorageImplementation.GetEncryptedSharedPreferences() in D:\a\_work\1\s\src\Essentials\src\SecureStorage\SecureStorage.android.cs:line 93
[mono-rt]    at Microsoft.Maui.Storage.SecureStorageImplementation.PlatformRemoveAll() in D:\a\_work\1\s\src\Essentials\src\SecureStorage\SecureStorage.android.cs:line 78
[mono-rt]    at Microsoft.Maui.Storage.SecureStorageImplementation.RemoveAll() in D:\a\_work\1\s\src\Essentials\src\SecureStorage\SecureStorage.shared.cs:line 233
[mono-rt]    at Catlists.Services.SettingsService.IsProductPurchased(IProduct product) in C:\Projects\cat-lists\src\Catlists\Services\SettingsService.cs:line 630
[mono-rt]    at Catlists.Services.AdManager.ShouldShowAdsForThisUser() in C:\Projects\cat-lists\src\Catlists\Services\AdManager.cs:line 75
[mono-rt]    at Catlists.ViewModels.SettingsViewModel.OnNavigatedTo(NavigationParameters parameters) in C:\Projects\cat-lists\src\Catlists\ViewModels\SettingsViewModel.cs:line 75
[mono-rt]    at Burkus.Mvvm.Maui.LifecycleEventUtility.TriggerOnNavigatedTo(Object bindingContext, NavigationParameters navigationParameters)
[mono-rt]    at Burkus.Mvvm.Maui.NavigationService.<HandleNavigation>d__14`1[[Catlists.Pages.SettingsPage, Catlists, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null]].MoveNext()
[mono-rt]    at Burkus.Mvvm.Maui.NavigationService.<Push>d__1`1[[Catlists.Pages.SettingsPage, Catlists, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null]].MoveNext()
[mono-rt]    at Burkus.Mvvm.Maui.NavigationService.<Push>d__0`1[[Catlists.Pages.SettingsPage, Catlists, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null]].MoveNext()
[mono-rt]    at Catlists.ViewModels.HomeViewModel.Settings() in C:\Projects\cat-lists\src\Catlists\ViewModels\HomeViewModel.cs:line 123
[mono-rt]    at CommunityToolkit.Mvvm.Input.AsyncRelayCommand.AwaitAndThrowIfFailed(Task executionTask) in /_/src/CommunityToolkit.Mvvm/Input/AsyncRelayCommand.cs:line 351
[mono-rt]    at System.Threading.Tasks.Task.<>c.<ThrowAsync>b__128_0(Object state)
[mono-rt]    at Android.App.SyncContext.<>c__DisplayClass2_0.<Post>b__0() in /Users/runner/work/1/s/xamarin-android/src/Mono.Android/Android.App/SyncContext.cs:line 36
[mono-rt]    at Java.Lang.Thread.RunnableImplementor.Run() in /Users/runner/work/1/s/xamarin-android/src/Mono.Android/Java.Lang/Thread.cs:line 36
[mono-rt]    at Java.Lang.IRunnableInvoker.n_Run(IntPtr jnienv, IntPtr native__this) in /Users/runner/work/1/s/xamarin-android/src/Mono.Android/obj/Release/net8.0/android-34/mcw/Java.Lang.IRunnable.cs:line 84
[mono-rt]    at Android.Runtime.JNINativeWrapper.Wrap_JniMarshal_PP_V(_JniMarshal_PP_V callback, IntPtr jnienv, IntPtr klazz) in /Users/runner/work/1/s/xamarin-android/src/Mono.Android/Android.Runtime/JNINativeWrapper.g.cs:line 26
[mono-rt]   --- End of managed Javax.Crypto.AEADBadTagException stack trace ---
[mono-rt] javax.crypto.AEADBadTagException
[mono-rt] 	at android.security.keystore2.AndroidKeyStoreCipherSpiBase.engineDoFinal(AndroidKeyStoreCipherSpiBase.java:626)
[mono-rt] 	at javax.crypto.Cipher.doFinal(Cipher.java:2114)
[mono-rt] 	at com.google.crypto.tink.integration.android.AndroidKeystoreAesGcm.decryptInternal(AndroidKeystoreAesGcm.java:118)
[mono-rt] 	at com.google.crypto.tink.integration.android.AndroidKeystoreAesGcm.decrypt(AndroidKeystoreAesGcm.java:101)
[mono-rt] 	at com.google.crypto.tink.KeysetHandle.decrypt(KeysetHandle.java:993)
[mono-rt] 	at com.google.crypto.tink.KeysetHandle.readWithAssociatedData(KeysetHandle.java:878)
[mono-rt] 	at com.google.crypto.tink.KeysetHandle.read(KeysetHandle.java:859)
[mono-rt] 	at com.google.crypto.tink.integration.android.AndroidKeysetManager$Builder.readMasterkeyDecryptAndParseKeyset(AndroidKeysetManager.java:378)
[mono-rt] 	at com.google.crypto.tink.integration.android.AndroidKeysetManager$Builder.build(AndroidKeysetManager.java:298)
[mono-rt] 	at androidx.security.crypto.EncryptedSharedPreferences.create(EncryptedSharedPreferences.java:169)
[mono-rt] 	at androidx.security.crypto.EncryptedSharedPreferences.create(EncryptedSharedPreferences.java:130)
[mono-rt] 	at mono.java.lang.RunnableImplementor.n_run(Native Method)
[mono-rt] 	at mono.java.lang.RunnableImplementor.run(RunnableImplementor.java:31)
[mono-rt] 	at android.os.Handler.handleCallback(Handler.java:958)
[mono-rt] 	at android.os.Handler.dispatchMessage(Handler.java:99)
[mono-rt] 	at android.os.Looper.loopOnce(Looper.java:205)
[mono-rt] 	at android.os.Looper.loop(Looper.java:294)
[mono-rt] 	at android.app.ActivityThread.main(ActivityThread.java:8248)
[mono-rt] 	at java.lang.reflect.Method.invoke(Native Method)
[mono-rt] 	at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:552)
[mono-rt] 	at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:971)
[mono-rt] Caused by: android.security.KeyStoreException: Signature/MAC verification failed (internal Keystore code: -30 message: system/security/keystore2/src/operation.rs:850: KeystoreOperation::finish
[mono-rt] 
[mono-rt] Caused by:
[mono-rt]     0: system/security/keystore2/src/operation.rs:426: Finish failed.
[mono-rt]     1: Error::Km(r#VERIFICATION_FAILED)) (public error code: 10 internal Keystore code: -30)
[mono-rt] 	at android.security.KeyStore2.getKeyStoreException(KeyStore2.java:386)
[mono-rt] 	at android.security.KeyStoreOperation.handleExceptions(KeyStoreOperation.java:78)
[mono-rt] 	at android.security.KeyStoreOperation.finish(KeyStoreOperation.java:128)
[mono-rt] 	at android.security.keystore2.KeyStoreCryptoOperationChunkedStreamer$MainDataStream.finish(KeyStoreCryptoOperationChunkedStreamer.java:228)
[mono-rt] 	at android.security.keystore2.KeyStoreCryptoOperationChunkedStreamer.doFinal(KeyStoreCryptoOperationChunkedStreamer.java:181)
[mono-rt] 	at android.security.keystore2.AndroidKeyStoreAuthenticatedAESCipherSpi$BufferAllOutputUntilDoFinalStreamer.doFinal(AndroidKeyStoreAuthenticatedAESCipherSpi.java:396)
[mono-rt] 	at android.security.keystore2.AndroidKeyStoreCipherSpiBase.engineDoFinal(AndroidKeyStoreCipherSpiBase.java:618)
[mono-rt] 	... 20 more
[mono-rt] 
[mono-rt]   --- End of managed Javax.Crypto.AEADBadTagException stack trace ---
[mono-rt] javax.crypto.AEADBadTagException
[mono-rt] 	at android.security.keystore2.AndroidKeyStoreCipherSpiBase.engineDoFinal(AndroidKeyStoreCipherSpiBase.java:626)
[mono-rt] 	at javax.crypto.Cipher.doFinal(Cipher.java:2114)
[mono-rt] 	at com.google.crypto.tink.integration.android.AndroidKeystoreAesGcm.decryptInternal(AndroidKeystoreAesGcm.java:118)
[mono-rt] 	at com.google.crypto.tink.integration.android.AndroidKeystoreAesGcm.decrypt(AndroidKeystoreAesGcm.java:101)
[mono-rt] 	at com.google.crypto.tink.KeysetHandle.decrypt(KeysetHandle.java:993)
[mono-rt] 	at com.google.crypto.tink.KeysetHandle.readWithAssociatedData(KeysetHandle.java:878)
[mono-rt] 	at com.google.crypto.tink.KeysetHandle.read(KeysetHandle.java:859)
[mono-rt] 	at com.google.crypto.tink.integration.android.AndroidKeysetManager$Builder.readMasterkeyDecryptAndParseKeyset(AndroidKeysetManager.java:378)
[mono-rt] 	at com.google.crypto.tink.integration.android.AndroidKeysetManager$Builder.build(AndroidKeysetManager.java:298)
[mono-rt] 	at androidx.security.crypto.EncryptedSharedPreferences.create(EncryptedSharedPreferences.java:169)
[mono-rt] 	at androidx.security.crypto.EncryptedSharedPreferences.create(EncryptedSharedPreferences.java:130)
[mono-rt] 	at mono.java.lang.RunnableImplementor.n_run(Native Method)
[mono-rt] 	at mono.java.lang.RunnableImplementor.run(RunnableImplementor.java:31)
[mono-rt] 	at android.os.Handler.handleCallback(Handler.java:958)
[mono-rt] 	at android.os.Handler.dispatchMessage(Handler.java:99)
[mono-rt] 	at android.os.Looper.loopOnce(Looper.java:205)
[mono-rt] 	at android.os.Looper.loop(Looper.java:294)
[mono-rt] 	at android.app.ActivityThread.main(ActivityThread.java:8248)
[mono-rt] 	at java.lang.reflect.Method.invoke(Native Method)
[mono-rt] 	at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:552)
[mono-rt] 	at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:971)
[mono-rt] Caused by: android.security.KeyStoreException: Signature/MAC verification failed (internal Keystore code: -30 message: system/security/keystore2/src/operation.rs:850: KeystoreOperation::finish
[mono-rt] 
[mono-rt] Caused by:
[mono-rt]     0: system/security/keystore2/src/operation.rs:426: Finish failed.
[mono-rt]     1: Error::Km(r#VERIFICATION_FAILED)) (public error code: 10 internal Keystore code: -30)
[mono-rt] 	at android.security.KeyStore2.getKeyStoreException(KeyStore2.java:386)
[mono-rt] 	at android.security.KeyStoreOperation.handleExceptions(KeyStoreOperation.java:78)
[mono-rt] 	at android.security.KeyStoreOperation.finish(KeyStoreOperation.java:128)
[mono-rt] 	at android.security.keystore2.KeyStoreCryptoOperationChunkedStreamer$MainDataStream.finish(KeyStoreCryptoOperationChunkedStreamer.java:228)
[mono-rt] 	at android.security.keystore2.KeyStoreCryptoOperationChunkedStreamer.doFinal(KeyStoreCryptoOperationChunkedStreamer.java:181)
[mono-rt] 	at android.security.keystore2.AndroidKeyStoreAuthenticatedAESCipherSpi$BufferAllOutputUntilDoFinalStreamer.doFinal(AndroidKeyStoreAuthenticatedAESCipherSpi.java:396)
[mono-rt] 	at android.security.keystore2.AndroidKeyStoreCipherSpiBase.engineDoFinal(AndroidKeyStoreCipherSpiBase.java:618)
[mono-rt] 	... 20 more
[mono-rt] 

The MAUI docs recommending removing secure storage keys if you get an exception on GetAsync or SetAsync so I am trying to do that with RemoveAll but still getting an exception.

https://learn.microsoft.com/en-us/dotnet/maui/platform-integration/storage/secure-storage?view=net-maui-8.0&tabs=android#use-secure-storage

It's possible that an exception is thrown when calling GetAsync or SetAsync. This can be caused by a device not supporting > secure storage, encryption keys changing, or corruption of data. it's best to handle this by removing and adding the setting back if possible.

@jukkaasikainen
Copy link

jukkaasikainen commented Mar 20, 2024

Unfortunately, even with those rules on MAUI 8.0.10 I'm still getting secure storage exceptions:
Javax.Crypto.AEADBadTagException

Thanks for letting me know. I've since then disabled the AndroidManifest.xml setting

<application android:allowBackup="false" android:fullBackupContent="@xml/auto_backup_rules" ... >

but keeping the auto_backup_rules.xml file, which may or may not do something in this scenario.

I think the app retains access to secure variables and values through updates, though now I'm not 100% sure anymore. I'll try to remember to update this when testing after next version update.

EDIT: Secure values do indeed stay within the device when updating the app without first uninstalling it.

@mikeparker104 mikeparker104 added the partner/cat 😻 this is an issue that impacts one of our partners or a customer our advisory team is engaged with label Mar 21, 2024
@raindeer
Copy link

raindeer commented Mar 21, 2024

Have the same issue on a Pixel 7 with Android 14, Maui 8.0.10.

Using `<application android:allowBackup="false" works for me

@plppp2001
Copy link

This issue needs to be fixed so that we can have android:allowBackup="true"

@leonluc-dev
Copy link

leonluc-dev commented Mar 25, 2024

It seems the documentation for the backup configuration is slightly outdated. Here are the set of rules that worked for me, allowing me to keep backup enabled but solving this issue.

In the manifest set the following two properties on the <application> tag:

<application 
	....
        android:allowBackup="true"
        android:dataExtractionRules="@xml/backup_rules"
        android:fullBackupContent="@xml/backup_rules_legacy">

dataExtractionRules is used for Android 12 and up. fullBackupContent for Android 11 and lower.

backup_rules.xml

<?xml version="1.0" encoding="utf-8" ?>
<data-extraction-rules>
	<cloud-backup disableIfNoEncryptionCapabilities="false">
		<exclude domain="sharedpref" path="${applicationId}.microsoft.maui.essentials.preferences.xml"/>
	</cloud-backup>
	<device-transfer>
		<exclude domain="sharedpref" path="${applicationId}.microsoft.maui.essentials.preferences.xml"/>
	</device-transfer>
</data-extraction-rules>

backup_rules_legacy.xml

<?xml version="1.0" encoding="utf-8"?>
<full-backup-content>
	<exclude domain="sharedpref" path="${applicationId}.microsoft.maui.essentials.preferences.xml"/>
</full-backup-content>

Note: For some of my projects I noticed ${applicationId} isn't properly processed. If that is the case be sure to replace it with with your full application id. For example:

<exclude domain="sharedpref" path="org.example.MyAppName.microsoft.maui.essentials.preferences.xml"/>

@axylophon
Copy link

Is there a fix planned for this issue? We also have the problem. Maui 8.0.10. When reinstalling our app crashes with the same exception as in the first post.

@BurkusCat
Copy link
Contributor

@leonluc-dev Great work tracking this down. I've not tested but I think you've hit the nail on the head. It looks like this has always been the file name for MAUI Secure storage (https://github.com/dotnet/maui/pull/4211/files#diff-93ca7559b0a2eaf2bab3285f6d90c92ed92eee598bfbc814b368201753a0ef29). The documentation writer probably just made an assumption that xamarinessentials would change to mauiessentials and therefore these errors would come from not setting the backup rules correctly.

  • I've verified that "${applicationId}.microsoft.maui.essentials.preferences.xml" is what the secure storage preferences file is saved as.
  • "${applicationId}.microsoft.maui.essentials.versiontracking.xml" is for the VersionTracking APIs (which you may also want to exclude if you use it).
  • "${applicationId}_preferences.xml" appears to be for normal unencrypted preferences which you likely do want to backup (example, if user chooses a setting for how often they want to sync, then when they move to a new device then that setting will automatically be set for them)

image

@plppp2001
Copy link

plppp2001 commented Mar 27, 2024

@leonluc-dev Great work tracking this down. I've not tested but I think you've hit the nail on the head. It looks like this has always been the file name for MAUI Secure storage (https://github.com/dotnet/maui/pull/4211/files#diff-93ca7559b0a2eaf2bab3285f6d90c92ed92eee598bfbc814b368201753a0ef29). The documentation writer probably just made an assumption that xamarinessentials would change to mauiessentials and therefore these errors would come from not setting the backup rules correctly.

  • I've verified that "${applicationId}.microsoft.maui.essentials.preferences.xml" is what the secure storage preferences file is saved as.

  • "${applicationId}.microsoft.maui.essentials.versiontracking.xml" is for the VersionTracking APIs (which you may also want to exclude if you use it).

  • "${applicationId}_preferences.xml" appears to be for normal unencrypted preferences which you likely do want to backup (example, if user chooses a setting for how often they want to sync, then when they move to a new device then that setting will automatically be set for them)

image

How can we notify the actual documentation writer to update it?

@BurkusCat
Copy link
Contributor

How can we notify the actual documentation writer to update it?

Leonluc already submitted a GitHub issue for the docs :) dotnet/docs-maui#2167 (comment)

@moljac
Copy link
Contributor

moljac commented Mar 27, 2024

How can we notify the actual documentation writer to update it?

Issue was raised.

@figuerres
Copy link

Hello just found this, i think i am also getting hit by this.

i have a new app that when i run it for debug builds on my device i do not see a problem.
when i build a release apk for ad hoc and run it i keep getting crashes and errors.

from my sentry logs i get messages like this:

android.security.keystore2.AndroidKeyStoreCipherSpiBase in engineDoFinal at line 632
javax.crypto.Cipher in doFinal at line 2114
com.google.crypto.tink.integration.android.AndroidKeystoreAesGcm in decryptInternal at line 118
com.google.crypto.tink.integration.android.AndroidKeystoreAesGcm in decrypt at line 101
com.google.crypto.tink.KeysetHandle in decrypt at line 993
com.google.crypto.tink.KeysetHandle in readWithAssociatedData at line 878
com.google.crypto.tink.KeysetHandle in read at line 859
com.google.crypto.tink.integration.android.AndroidKeysetManager$Builder in readMasterkeyDecryptAndParseKeyset at line 378
com.google.crypto.tink.integration.android.AndroidKeysetManager$Builder in build at line 298
androidx.security.crypto.EncryptedSharedPreferences in create at line 169
androidx.security.crypto.EncryptedSharedPreferences in create at line 130

and then

KeyStoreException
Signature/MAC verification failed (internal Keystore code: -30 message: system/security/keystore2/src/operation.rs:850: KeystoreOperation::finish

Caused by:
0: system/security/keystore2/src/operation.rs:426: Finish failed.
1: Error::Km(r#VERIFICATION_FAILED))
android.security.KeyStore2 in getKeyStoreException at line 435
android.security.KeyStoreOperation in handleExceptions at line 78
android.security.KeyStoreOperation in finish at line 128
android.security.keystore2.KeyStoreCryptoOperationChunkedStreamer$MainDataStream in finish at line 228
android.security.keystore2.KeyStoreCryptoOperationChunkedStreamer in doFinal at line 181
… .security.keystore2.AndroidKeyStoreAuthenticatedAESCipherSpi$BufferAllOutputUntilDoFinalStreamer in doFinal at line 396
android.security.keystore2.AndroidKeyStoreCipherSpiBase in engineDoFinal at line 624
javax.crypto.Cipher in doFinal at line 2114
com.google.crypto.tink.integration.android.AndroidKeystoreAesGcm in decryptInternal at line 118
com.google.crypto.tink.integration.android.AndroidKeystoreAesGcm in decrypt at line 101
com.google.crypto.tink.KeysetHandle in decrypt at line 993
com.google.crypto.tink.KeysetHandle in readWithAssociatedData at line 878
com.google.crypto.tink.KeysetHandle in read at line 859
com.google.crypto.tink.integration.android.AndroidKeysetManager$Builder in readMasterkeyDecryptAndParseKeyset at line 378
com.google.crypto.tink.integration.android.AndroidKeysetManager$Builder in build at line 298
androidx.security.crypto.EncryptedSharedPreferences in create at line 169
androidx.security.crypto.EncryptedSharedPreferences in create at line 130

android v14
samsung galaxy 22

also i see this:

JniObjectReference StaticMethods.CallStaticObjectMethod(JniObjectReference, JniMethodInfo, JniArgumentValue*)
In App

Assembly:
Java.Interop
Version:
7.0.0.0
PublicKeyToken:
84e04ff9cfb79065
JniObjectReference JniStaticMethods.InvokeObjectMethod(string, JniArgumentValue*)
In App

Called from: ISharedPreferences EncryptedSharedPreferences.Create(Context, string, MasterKey, PrefKeyEncryptionScheme, PrefValueEncryptionScheme)

Show 2 more frames

the app wants to use secure storage for user tokens.

try to login or resume from recent session and BOOM!

i will see if the info here lets me work past this.....

@figuerres
Copy link

first test setting allow backup to false seems to stop the crash!
also note that a debug build of the same app to the same device did NOT have the problem !
so why does debug not error and release does ??

@South2AK
Copy link

South2AK commented Apr 12, 2024

@figuerres I have the error on both debug and release using MAUI 8.0.20

Also Allow Backup none doesn´t seem to be a workaround since all saved data get´s deleted after a restart of the app.

I´m having the same error when using any SecureStorage command:

[ViewRootImpl@5b71c8b[MainActivity]] ViewPostIme pointer 0
[ViewRootImpl@5b71c8b[MainActivity]] ViewPostIme pointer 1
[ViewRootImpl@5b71c8b[MainActivity]] onDisplayChanged oldDisplayState=2 newDisplayState=2
[DOTNET]    at Java.Interop.JniEnvironment.StaticMethods.CallStaticObjectMethod(JniObjectReference type, JniMethodInfo method, JniArgumentValue* args) in /Users/runner/work/1/s/xamarin-android/external/Java.Interop/src/Java.Interop/obj/Release/net7.0/JniEnvironment.g.cs:line 21452
[DOTNET]    at Java.Interop.JniPeerMembers.JniStaticMethods.InvokeObjectMethod(String encodedMember, JniArgumentValue* parameters) in /Users/runner/work/1/s/xamarin-android/external/Java.Interop/src/Java.Interop/Java.Interop/JniPeerMembers.JniStaticMethods.cs:line 165
[DOTNET]    at AndroidX.Security.Crypto.EncryptedSharedPreferences.Create(Context context, String fileName, MasterKey masterKey, PrefKeyEncryptionScheme prefKeyEncryptionScheme, PrefValueEncryptionScheme prefValueEncryptionScheme) in C:\a\_work\1\s\generated\androidx.security.security-crypto\obj\Release
et6.0-android\generated\src\AndroidX.Security.Crypto.EncryptedSharedPreferences.cs:line 227
[DOTNET]    at Microsoft.Maui.Storage.SecureStorageImplementation.GetEncryptedSharedPreferences() in D:\a\_work\1\s\src\Essentials\src\SecureStorage\SecureStorage.android.cs:line 93
[DOTNET]    at Microsoft.Maui.Storage.SecureStorageImplementation.<>c__DisplayClass6_0.<PlatformSetAsync>b__0() in D:\a\_work\1\s\src\Essentials\src\SecureStorage\SecureStorage.android.cs:line 53
[DOTNET]    at System.Threading.Tasks.Task.InnerInvoke()
[DOTNET]    at System.Threading.Tasks.Task.<>c.<.cctor>b__281_0(Object obj)
[DOTNET]    at System.Threading.ExecutionContext.RunFromThreadPoolDispatchLoop(Thread threadPoolThread, ExecutionContext executionContext, ContextCallback callback, Object state)
[DOTNET] --- End of stack trace from previous location ---
[DOTNET]    at System.Threading.ExecutionContext.RunFromThreadPoolDispatchLoop(Thread threadPoolThread, ExecutionContext executionContext, ContextCallback callback, Object state)
[DOTNET]    at System.Threading.Tasks.Task.ExecuteWithThreadLocal(Task& currentTaskSlot, Thread threadPoolThread)
[DOTNET] --- End of stack trace from previous location ---
[DOTNET]    at Schaden_Digital.Views.PageLogin.Button_Clicked_1(Object sender, EventArgs e) in F:\Sourcecode\Online Gutachten\Online Gutachten\Views\Login\PageLogin.xaml.cs:line 77
[DOTNET]   --- End of managed Javax.Crypto.AEADBadTagException stack trace ---
[DOTNET] javax.crypto.AEADBadTagException
[DOTNET] 	at android.security.keystore2.AndroidKeyStoreCipherSpiBase.engineDoFinal(AndroidKeyStoreCipherSpiBase.java:632)
[DOTNET] 	at javax.crypto.Cipher.doFinal(Cipher.java:2114)
[DOTNET] 	at com.google.crypto.tink.integration.android.AndroidKeystoreAesGcm.decryptInternal(AndroidKeystoreAesGcm.java:118)
[DOTNET] 	at com.google.crypto.tink.integration.android.AndroidKeystoreAesGcm.decrypt(AndroidKeystoreAesGcm.java:101)
[DOTNET] 	at com.google.crypto.tink.KeysetHandle.decrypt(KeysetHandle.java:993)
[DOTNET] 	at com.google.crypto.tink.KeysetHandle.readWithAssociatedData(KeysetHandle.java:878)
[DOTNET] 	at com.google.crypto.tink.KeysetHandle.read(KeysetHandle.java:859)
[DOTNET] 	at com.google.crypto.tink.integration.android.AndroidKeysetManager$Builder.readMasterkeyDecryptAndParseKeyset(AndroidKeysetManager.java:378)
[DOTNET] 	at com.google.crypto.tink.integration.android.AndroidKeysetManager$Builder.build(AndroidKeysetManager.java:298)
[DOTNET] 	at androidx.security.crypto.EncryptedSharedPreferences.create(EncryptedSharedPreferences.java:169)
[DOTNET] 	at androidx.security.crypto.EncryptedSharedPreferences.create(EncryptedSharedPreferences.java:130)
[DOTNET] Caused by: android.security.KeyStoreException: Signature/MAC verification failed (internal Keystore code: -30 message: system/security/keystore2/src/operation.rs:850: KeystoreOperation::finish
[DOTNET] 
[DOTNET] Caused by:
[DOTNET]     0: system/security/keystore2/src/operation.rs:426: Finish failed.
[DOTNET]     1: Error::Km(r#VERIFICATION_FAILED)) (public error code: 10 internal Keystore code: -30)
[DOTNET] 	at android.security.KeyStore2.getKeyStoreException(KeyStore2.java:435)
[DOTNET] 	at android.security.KeyStoreOperation.handleExceptions(KeyStoreOperation.java:78)
[DOTNET] 	at android.security.KeyStoreOperation.finish(KeyStoreOperation.java:128)
[DOTNET] 	at android.security.keystore2.KeyStoreCryptoOperationChunkedStreamer$MainDataStream.finish(KeyStoreCryptoOperationChunkedStreamer.java:228)
[DOTNET] 	at android.security.keystore2.KeyStoreCryptoOperationChunkedStreamer.doFinal(KeyStoreCryptoOperationChunkedStreamer.java:181)
[DOTNET] 	at android.security.keystore2.AndroidKeyStoreAuthenticatedAESCipherSpi$BufferAllOutputUntilDoFinalStreamer.doFinal(AndroidKeyStoreAuthenticatedAESCipherSpi.java:396)
[DOTNET] 	at android.security.keystore2.AndroidKeyStoreCipherSpiBase.engineDoFinal(AndroidKeyStoreCipherSpiBase.java:624)
[DOTNET] 	... 10 more
[DOTNET] Exception of type 'Javax.Crypto.AEADBadTagException' was thrown.
[ViewRootImpl@5b71c8b[MainActivity]] onDisplayChanged oldDisplayState=2 newDisplayState=2

@mmiller-d8
Copy link

To add another wrinkle to this, the documentation says:

NET MAUI automatically handles this case by removing the key so it can be reset. Alternatively, you can disable Auto Backup.

First, it doesn't handle the case because it still fails. Second, and more importantly, removing the key myself doesn't work either. It seems that calling SetValue must be attempting to read the value before setting it because the call still fails with the same exception.

@leonluc-dev Thank you for putting the complete workaround. Unfortunately for me, I still can't set secure storage values. I'm getting Signature/MAC verification failed (internal Keystore code: -30 message: In KeystoreOperation::finish

@SpikeThatMike
Copy link

Same problem as @mmiller-d8, I have done the work around @leonluc-dev posted but its crashing on
Signature/MAC verification failed (internal Keystore code: -30 message: system/security/keystore2/src/operation.rs:850: KeystoreOperation::finish

@gerhartz
Copy link

gerhartz commented May 21, 2024

Same issue as @mmiller-d8. Seeing this exception on my Android 14 / Pixel 6a device. Building with Maui 8.0.21, NET SDK 8.0.300, Xcode 15.4

I was struggling to consistently reproduce the issue but figured out a process:

  1. Launch my .net8 app
  2. Run the Android Backup (Settings --> Backup --> Back up now)
  3. Uninstall/Reinstall the app
  4. Launch app. Any call to secure storage fails. Trying to remove keys also fails.

Clearing the app storage avoids the problem (not suggesting that as a solution). But for anyone else that feels the issue is happening randomly for them I realized that my phone does auto-backup after 2hrs of inactivity which made the problem pop up again the next time I rebuilt after clearing app storage.

@gerhartz
Copy link

gerhartz commented May 23, 2024

A workaround I've found is to clear the shared preferences with Android code. It feels brittle and not quite a polished solution but it does enable you to get past the error and back to saving/loading data

#if ANDROID 
   var packageName = AppInfo.Current.PackageName;
   var alias = $"{packageName}.microsoft.maui.essentials.preferences";
   var preferences = AndroidApp.Context.GetSharedPreferences(alias, FileCreationMode.Private);
   preferences?.Edit()?.Clear()?.Apply();
#endif

At this point I'm not sure whether the flag FileCreationMode.Private is correct or what the side effects of this may be.

gerhartz referenced this issue May 23, 2024
It appears that reusing the shared preferences instance sometimes causes changes to not be properly committed to disk as the instance is not disposed in some cases before the app is torn down (eg: force close).

Caching isn't really necessary here anyways as the underlying android preference manager does its own layer of caching (so we are just paying the interop tax to get a 'new' instance here).

The simplest fix is to stop caching which is the real content of this change.  We don't cache in the regular preferences API where we use shared preferences and the issue does not seem to exist there.
@kmiterror
Copy link

To add another wrinkle to this, the documentation says:

NET MAUI automatically handles this case by removing the key so it can be reset. Alternatively, you can disable Auto Backup.

First, it doesn't handle the case because it still fails. Second, and more importantly, removing the key myself doesn't work either. It seems that calling SetValue must be attempting to read the value before setting it because the call still fails with the same exception.

@leonluc-dev Thank you for putting the complete workaround. Unfortunately for me, I still can't set secure storage values. I'm getting Signature/MAC verification failed (internal Keystore code: -30 message: In KeystoreOperation::finish

The reason for this is that the SecureStorage GeneralSecurityException handling is broken:

catch (GeneralSecurityException)
{
// TODO: Use Logger here?
System.Diagnostics.Debug.WriteLine(
$"Unable to decrypt key, {key}, which is likely due to an app uninstall. Removing old key and returning null.");
PlatformRemove(key);
return null;
}

The idea is good but calling PlatformRemove will crash in case of data being restored from cloud back-up.
The reason is that PlatformRemove (as any other SecureStorage method) relies on GetEncryptedSharedPreferences.
This method calls android nativeEncryptedSharedPreferences.Create method to de-crypt the key-store.
Since it was encrypted on another device this method will throw a java.security.GeneralSecurityException.
https://developer.android.com/reference/androidx/security/crypto/EncryptedSharedPreferences#create(android.content.Context,java.lang.String,androidx.security.crypto.MasterKey,androidx.security.crypto.EncryptedSharedPreferences.PrefKeyEncryptionScheme,androidx.security.crypto.EncryptedSharedPreferences.PrefValueEncryptionScheme)

Why GetEncryptedSharedPreferences catches InvalidProtocolBufferException instead of java.security.GeneralSecurityException is also unclear to me.
Both MasterKey.Builder and EncryptedSharedPreferences.Create throws java.security.GeneralSecurityException.
But also just changing to proper exception won't make it work, because RemoveAll will crash again because it uses GetEncryptedSharedPreferences internally.

For regular developers consuming SecureStorage it's best to either disable auto-backup all together or selectively as @leonluc-dev described here #18230 (comment)

For microsoft developers maintaining this part of code:
In my opinion, instead of calling PlatformRemove(key) it should be handled as @gerhartz described

   var preferences = AndroidApp.Context.GetSharedPreferences(Alias, FileCreationMode.Private);
   preferences?.Edit()?.Clear()?.Apply();

This should resolve it properly for all developers.
This is hard to track down as it requires a lot of hoops: logs reading, googling, docs reading and finally connecting all the dots.
There are at least few issues on github in different flavours.
Most of them hang forever and are never resolved.

@Redth @moljac Could you have a look and resolve it?
I believe that this issue can pretty much bork the app in certain circumstances for some users.
It takes a lot of hours to track it down.
I think most end-users won't even bother to infrom app developers that the issue is there and will just out-right uninstall it.

So to summarize I think the right solution would be to change the SecureStorage.android.cs code like below:

		void PlatformRemoveAll()
		{
			var preferences = AndroidApp.Context.GetSharedPreferences(Alias, FileCreationMode.Private);
			preferences?.Edit()?.Clear()?.Apply();
		}

		ISharedPreferences GetEncryptedSharedPreferences()
		{
			try
			{
				var context = Application.Context;

				var prefsMainKey = new MasterKey.Builder(context, Alias)
					.SetKeyScheme(MasterKey.KeyScheme.Aes256Gcm)
					.Build();

				return EncryptedSharedPreferences.Create(
					context,
					Alias,
					prefsMainKey,
					EncryptedSharedPreferences.PrefKeyEncryptionScheme.Aes256Siv,
					EncryptedSharedPreferences.PrefValueEncryptionScheme.Aes256Gcm);
			}
			catch (GeneralSecurityException)
			{
				// TODO: Use Logger here?
				System.Diagnostics.Debug.WriteLine(
					"Unable get encrypted shared preferences, which is likely due to an app uninstall. Removing all keys and returning null.");
				PlatformRemoveAll();
				return GetEncryptedSharedPreferences();
			}
		}

@mmiller-d8
Copy link

Thank you for that @kmiterror I tried selectively removing the backup as in the comment you mentioned, but I'm still getting crashes when I update the app on a device. I haven't turned off all backups yet because I'm hoping they fix it. I'm not yet live, so I just delete the application data when I update it with a release build.

@kmiterror
Copy link

I tried selectively removing the backup

I think that the thing that you specify in AndroidManifest.xml just excludes the files from the backup.
Adding the "selective exclusion" will just stop the autoBackup mechanism to include this particular file in the future.

Since the file has already been restored on your device and you don't have keys to decrypt it SecureStorage.GetAsync(key); will throw exception.

So it seems that at this point you have to catch the android.security.KeyStoreException and then delete the preferences file that is storing the SecureStorage keys.

try{
var yourVar = await SecureStorage.GetAsync(key);
} catch (KeyStoreException ex) {
   var packageName = AppInfo.Current.PackageName;
   var alias = $"{packageName}.microsoft.maui.essentials.preferences";
   var preferences = AndroidApp.Context.GetSharedPreferences(alias, FileCreationMode.Private);
   preferences?.Edit()?.Clear()?.Apply();
}

So the solution is two-fold,
1, disable backup function for {packageName}.microsoft.maui.essentials.preferences file
2, delete the {packageName}.microsoft.maui.essentials.preferences file if SecureStorage throws exception during de-cryption

@mmiller-d8
Copy link

@kmiterror I did do the selective backup. Here's what I have.

<?xml version="1.0" encoding="utf-8"?> <data-extraction-rules> <cloud-backup disableIfNoEncryptionCapabilities="false"> <exclude domain="sharedpref" path="com.myapp.app.microsoft.maui.essentials.preferences.xml"/> </cloud-backup> <device-transfer> <exclude domain="sharedpref" path="com.myapp.app.microsoft.maui.essentials.preferences.xml"/> </device-transfer> </data-extraction-rules>

I also have a legacy set for older devices.

I'll try out the error handling. Thanks!

@jonpryor jonpryor removed the partner/android Issues for the Android SDK label Jun 24, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
area-tooling XAML & C# Hot Reload, XAML Editor, Live Visual Tree, Live Preview, Debugging partner/cat 😻 this is an issue that impacts one of our partners or a customer our advisory team is engaged with platform/android 🤖 potential-regression This issue described a possible regression on a currently supported version., verification pending s/needs-attention Issue has more information and needs another look t/bug Something isn't working
Projects
None yet
Development

No branches or pull requests