从原生的Kotlin Android中检索Xamarin安全存储的数据。

huangapple go评论73阅读模式
英文:

Retrieve Xamarin secured storage data from native Kotlin Android

问题

在Android中检索数据的步骤如下:首先,您需要找到与Xamarin中存储的数据对应的密钥。然后,您需要手动解密数据。

英文:

I have an application written in Xamarin that saves data using SecureStorage.SetAsync. Now I need to convert the application into native Kotlin and Swift projects. Xamarin stores the data in the iOS keychain which is relatively simple to retrieve. However I am struggling to retrieve the data in Kotlin Android as I believe I need to find the key and decrypt the data manually.

How do I go about retrieving the data in Android?

答案1

得分: 1

根据官方文件关于SecureStorage API的平台实现细节,它使用Android本机API Shared Preferences来存储数据。

Android KeyStore用于存储用于在将值保存到Shared Preferences之前加密值的密码密钥,文件名为[YOUR-APP-PACKAGE-ID].xamarinessentials。在共享首选项文件中使用的密钥(不是加密密钥,而是值的密钥)是传递给SecureStorage API的密钥的MD5哈希。

所以我尝试了这个SecureStorage.SetAsync("test", "hahaha");。然后我搜索了有关共享首选项文件的Android默认位置,它位于/data/data/YOUR_PACKAGE_NAME/shared_prefs/YOUR_PACKAGE_NAME.xamarinessentials.xml

我使用Android Studio的设备文件浏览器检查了文件:

从原生的Kotlin Android中检索Xamarin安全存储的数据。

该值已加密,您可以查看有关加密字符串的源代码。此外,您可以查看有关如何使用共享首选项的源代码var sharedPreferences = GetSharedPreferences(sharedName)。而sharedName是SecureStorage类中的string Alias = $"{AppInfo.PackageName}.xamarinessentials"

因此,我使用Android本机API读取了共享首选项文件:

val name = this.PackageName + ".xamarinessentials"
val sp = GetSharedPreferences(name, FileCreationMode.Private)
val value = sp.GetString("test",null)

调试结果:

从原生的Kotlin Android中检索Xamarin安全存储的数据。

该值已加密,您需要根据有关加密字符串的源代码进行解密。因此,您只需将这三行C#代码转换为Kotlin代码以获取该值。

此外,如果您想将Xamarin应用程序转换为Android Kotlin应用程序,您可以直接使用本机SharedPreferences API。我无法理解为什么您需要从本机Kotlin Android中检索Xamarin安全存储的数据。

英文:

According to the official document about the Platform Implementation Specifics of the SecureStorage API, it used the android native api Shared Preferences to store the data.

> The Android KeyStore is used to store the cipher key used to encrypt the value before it is saved into a Shared Preferences with a filename of [YOUR-APP-PACKAGE-ID].xamarinessentials. The key (not a cryptographic key, the key to the value) used in the shared preferences file is a MD5 Hash of the key passed into the SecureStorage APIs.

So I have tried this SecureStorage.SetAsync("test", "hahaha");. Then I searched about the android default location of the shared preferences file, it is in the /data/data/YOUR_PACKAGE_NAME/shared_prefs/YOUR_PACKAGE_NAME.xamarinessentials.xml.

I checked the file with the Android Studio's Device File Explorer:

从原生的Kotlin Android中检索Xamarin安全存储的数据。

The value has been encrypted, you can check the source code about encrypting the string. In addition, you can check the source code about how did it use the shared preferences var sharedPreferences = GetSharedPreferences(sharedName). And the sharedName is the string Alias = $"{AppInfo.PackageName}.xamarinessentials" in the SecureStorage class.

So I used the android native api to read the shared preferences file:

var name = this.PackageName + ".xamarinessentials";
var sp = GetSharedPreferences(name, FileCreationMode.Private);
var value = sp.GetString("test",null);

And the debug result:

从原生的Kotlin Android中检索Xamarin安全存储的数据。

The value is encrypted and you need to decrypt it according to the source code about encrypting the string. So you can just convert the three line c# code to kotlin code to get the value.

In addition, if you want to convert the xamarin app to Android Kotlin app, you can use the native SharedPreferences api directly. I can't understand why you need to Retrieve Xamarin secured storage data from native Kotlin Android.

答案2

得分: 0

以下是Kotlin代码的翻译部分:

fun decryptPrefData(preferenceKey: String): String? {
    val alias = "${context.packageName}.xamarinessentials"
    val sharedPref = context.getSharedPreferences(alias, Context.MODE_PRIVATE)
    return sharedPref.getString(preferenceKey, null)?.let { encryptedFullString ->
        val encryptedFullBytes = Base64.decode(encryptedFullString, Base64.DEFAULT)
        val iv = encryptedFullBytes.copyOfRange(0, 12)
        val encryptedData = encryptedFullBytes.copyOfRange(12, encryptedFullBytes.size)

        val keyStore = KeyStore.getInstance("AndroidKeyStore")
        keyStore.load(null)
        val secretKey = keyStore.getKey(alias, null) as SecretKey

        val cipher = Cipher.getInstance("AES/GCM/NoPadding")
        cipher.init(Cipher.DECRYPT_MODE, secretKey, GCMParameterSpec(128, iv))
        cipher.doFinal(encryptedData).toString(Charsets.UTF_8)
    }
}

希望这对您有所帮助。

英文:

To complete @Liyun Zhang - MSFT's answer, here is the Kotlin code:

fun decryptPrefData(preferenceKey: String): String? {
    val alias = "${context.packageName}.xamarinessentials"
    val sharedPref = context.getSharedPreferences(alias, Context.MODE_PRIVATE)
    return sharedPref.getString(preferenceKey, null)?.let { encryptedFullString ->
        val encryptedFullBytes = Base64.decode(encryptedFullString, Base64.DEFAULT)
        val iv = encryptedFullBytes.copyOfRange(0, 12)
        val encryptedData = encryptedFullBytes.copyOfRange(12, encryptedFullBytes.size)

        val keyStore = KeyStore.getInstance("AndroidKeyStore")
        keyStore.load(null)
        val secretKey = keyStore.getKey(alias, null) as SecretKey

        val cipher = Cipher.getInstance("AES/GCM/NoPadding")
        cipher.init(Cipher.DECRYPT_MODE, secretKey, GCMParameterSpec(128, iv))
        cipher.doFinal(encryptedData).toString(Charsets.UTF_8)
    }
}

huangapple
  • 本文由 发表于 2023年4月11日 14:30:04
  • 转载请务必保留本文链接:https://go.coder-hub.com/75982982.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定