Reputation: 461
I am trying to setup an encrypted default realm instance in my app. The idea is to generate a key using a KeyPairGenerator with a given alias, store it in the AndroidKeyStore and use said key every time it is needed.
WHAT I DO
This is how i generate the key:
KeyStore ks = KeyStore.getInstance("AndroidKeyStore");
ks.load(null);
if (!ks.containsAlias(KEY_ALIAS)) {
Calendar start = Calendar.getInstance();
Calendar end = Calendar.getInstance();
end.add(Calendar.YEAR, 99);
KeyPairGeneratorSpec spec = new KeyPairGeneratorSpec.Builder(this)
.setAlias(KEY_ALIAS)
.setSubject(new X500Principal("CN=Example, O=ExampleOrg"))
.setSerialNumber(BigInteger.ONE)
.setStartDate(start.getTime())
.setEndDate(end.getTime())
.build();
KeyPairGenerator generator = KeyPairGenerator.getInstance("RSA", "AndroidKeyStore");
generator.initialize(spec);
KeyPair keyPair = generator.generateKeyPair();
}
I am using the KeyPairGenerator as i need to support api versions 18 and up.
Here is how i setup my default realm instance in my Application:
RealmConfiguration config = null;
try {
config = new RealmConfiguration
.Builder(this)
.encryptionKey(ks.getKey(KEY_ALIAS, null).getEncoded())
.name("dealmatrix.realm")
.schemaVersion(1)
.build();
where ks is a Keystore instance acquired like so:
Keystore ks = KeyStore.getInstance("AndroidKeyStore");
ks.load(null);
WHAT GOES WRONG
My problem is that this expression:
ks.getKey(KEY_ALIAS, null).getEncoded()
returns null, which understandably leads to an exception.
I have read online that this is the intended behaviour of the KeyStore system.
If indeed i am unable to get the stored encryption key's byte array, how am I supposed to encrypt my realm using said key?
Are there any other methods to securely store an encryption key so that i may use it in my realm configuration?
Upvotes: 3
Views: 4472
Reputation: 1154
The Android Keystore prohibits extraction of private keys from it. So the design would be to generate a Realm key outside of the Android Keystore, so that you can use it for the encryption/decryption of the Realm database.
But to safely store that Realm key, you would use the power of the Android Keystore, by encrypting the Realm key with the Android Keystore and then store it locally (e.g. Shared Preferences). Later you could read that encrypted Realm key, decrypt it with the Android Keystore and use it again to unlock your Realm database.
Upvotes: 0
Reputation: 370
There is a WIP example project in feature/example/store_password
branch in Realm repository which uses Android keystore.
Core logic is written in Store.java
We need some more works(cleanup, adding comments, supporting old devices) before releasing this example project. But I think this project helps you.
Upvotes: 1
Reputation: 5722
Android Keystore keys returning null from getEncoded is working as intended. getEncoded is supposed to return the private key's key material (usually in PKCS#8 DER-encoded format) or null if key material export is not supported. Android Keystore by design does not reveal/export key material of private or secret keys and thus getEncoded returns null. See https://developer.android.com/training/articles/keystore.html#SecurityFeatures.
You can still use these keys just fine with Signature and Cipher abstractions.
Upvotes: 0