Reputation: 960
I decided to use new EncryptedSharedPreferences from AndroidX Security library. Since the app is supporting API 21 and higher, I decided to try out this new v1.1.0-alpha02 version, since it supports API 21+
So, I succeded to make the implementation for API 23+, but for older versions where Android KeyStore is not supported, I couldn't make it right, and there are no exact instructions how the master key should be created to make it work somehow.
The code for initializing SharedPrefs:
EncryptedSharedPreferences.create(
"prefs_name",
createMasterKey(),
App.appContext,
EncryptedSharedPreferences.PrefKeyEncryptionScheme.AES256_SIV,
EncryptedSharedPreferences.PrefValueEncryptionScheme.AES256_GCM
)
with this function for creating master key
private fun createMasterKey(): String {
return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
MasterKeys.getOrCreate(MasterKeys.AES256_GCM_SPEC)
} else {
val alias = "my_alias"
val start: Calendar = GregorianCalendar()
val end: Calendar = GregorianCalendar()
end.add(Calendar.YEAR, 30)
val spec = KeyPairGeneratorSpec.Builder(App.appContext)
.setAlias(alias)
.setSubject(X500Principal("CN=$alias"))
.setSerialNumber(BigInteger.valueOf(abs(alias.hashCode()).toLong()))
.setStartDate(start.time).setEndDate(end.time)
.build()
val kpGenerator: KeyPairGenerator = KeyPairGenerator.getInstance(
"RSA",
"AndroidKeyStore"
)
kpGenerator.initialize(spec)
val kp: KeyPair = kpGenerator.generateKeyPair()
kp.public.toString()
}
}
I found this solution somewhere out there, but it's not verified (no confirmation that it actually works), but it seems it should work.
When using this code block for API 21 and 22, the error appears on creating EncryptedSharedPreferences, and it says: Method threw 'com.google.crypto.tink.shaded.protobuf.InvalidProtocolBufferException' exception. Protocol message contained an invalid tag (zero).
Did someone find the solution for this implementation, or do you know why is this happening? I think this would help a lot of people, since there is no exact explanation what should this master key contain.
Thanks in advance!
Upvotes: 51
Views: 20527
Reputation: 6540
Here is a different solution that I found on google issue tracker.
As google document stated, "Note: The methods in both the EncryptedFile class and the EncryptedSharedPreferences
class aren't thread-safe." You need to wrap the calling in a synchronised method.
@synchronized
fun createSharedPreferences() {
val keyGenParameterSpec = MasterKeys.AES256_GCM_SPEC
val masterKeyAlias = MasterKeys.getOrCreate(keyGenParameterSpec)
prefs = EncryptedSharedPreferences.create(
"file-name",
masterKeyAlias,
context,
PrefKeyEncryptionScheme.AES256_SIV,
PrefValueEncryptionScheme.AES256_GCM)
}
src. https://issuetracker.google.com/issues/147480931#comment19
Upvotes: -1
Reputation: 67
UPDATE: Errors on different devices. The result isn't the solution.
I setup the android propreties "fullbackupcontent" this way in my Manifest
android:fullBackupContent="@xml/backup_descriptor"
Here is my backup_descriptor file
<?xml version="1.0" encoding="utf-8"?>
<full-backup-content>
<!-- App data isn't included in user's backup unless client-side encryption is enabled. -->
<include domain="file" path="." requireFlags="clientSideEncryption" />
<!-- Exclude specific shared preferences that contain GCM registration Id -->
<!-- <exclude domain=["file" | "database" | "sharedpref" | "external" | "root"]-->
<!-- path="string" />-->
</full-backup-content>
Now my app is working again & I can keep allowing backups.
Source : https://developer.android.com/guide/topics/data/autobackup#define-device-conditions
https://developer.android.com/guide/topics/data/autobackup#IncludingFiles
Upvotes: 5
Reputation: 5750
Here is what I have done, it seems to have fixed the error. Instead of removing the auto-backup feature, just do this:
In AndroidManifest.xml
android:allowBackup="true"
android:fullBackupContent="@xml/backup_descriptor"
In backup_descriptor.xml
<full-backup-content>
<exclude domain="sharedpref" path="keys.xml"/>
</full-backup-content>
keys.xml being the encrypted shared preference file name. Exclude all encrypted shared preference files this way.
Also, I am using implementation 'androidx.security:security-crypto:1.1.0-alpha02'
for now, seems like everything is working well with this setup.
Upvotes: 12
Reputation: 747
I can fix the InvalidProtocolBufferException
in two ways, though I don't like either of them:
Use an older version of security-crypto
implementation 'androidx.security:security-crypto:1.1.0-alpha01'
Use the latest (at the time of writing) version of security-crypto
, but with a forced older version of tink:
implementation 'androidx.security:security-crypto:1.1.0-alpha03'
implementation('com.google.crypto.tink:tink-android') {
version {
strictly '1.4.0'
}
}
Upvotes: 13
Reputation: 377
Add to manifest
android:allowBackup="false" android:fullBackupContent="false"
Because after uninstalling the application you still have backed up your crypto file which you definitely can't decrypt after installing a new version.
Upvotes: 36