Rakatan
Rakatan

Reputation: 461

Encrypting Realm with key stored in KeyStore

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

Answers (3)

Stefan Arn
Stefan Arn

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

zaki50
zaki50

Reputation: 370

There is a WIP example project in feature/example/store_password branch in Realm repository which uses Android keystore.

https://github.com/realm/realm-java/tree/feature/example/store_password/examples/StoreEncryptionPassword

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

Alex Klyubin
Alex Klyubin

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

Related Questions