Can we extract public/private keys from the Android Keystore?
Asked Answered
G

1

11

Regarding Android Keystore system article,

Key material never enters the application process. When an application performs cryptographic operations using an Android Keystore key, behind the scenes plaintext, ciphertext, and messages to be signed or verified are fed to a system process which carries out the cryptographic operations. If the app's process is compromised, the attacker may be able to use the app's keys but cannot extract their key material (for example, to be used outside of the Android device)

So, my question is why is in BasicAndroidKeyStore, developer able to get the KeyPair object and then print its public/private keys?

If developer can access the keys, how this system can be considered to be secured? It's not. If an attacker compromises the app process, he can easily retrieve the kays and use outside of the device.

Galleon answered 18/9, 2018 at 7:21 Comment(2)
That the key can't be extracted is only valid in case the device supports "hardwar security" for the keystore (most devices do so). Otherwise you can access the AndroidKeyStore with root permissions (rooted device or root exploit).Cruces
Does this answer your question? Android KeyStore get raw bytes/string of stored keyGerminate
A
5

The example code that you pointed to from BasicAndroidKeyStore does not log the public key as getPublic() from the KeyPair class only returns a reference the the public key object, not the public key itself.

Log.d(TAG, "Public Key reference is: " + kp.getPublic().toString());

Logs:

D/KeyStoreFragment: Public Key reference is: android.security.keystore.AndroidKeyStoreRSAPublicKey@b8004e8f

The same goes for getPrivate().

Log.d(TAG, "Private Key reference is: " + kp.getPrivate().toString());

Logs:

D/KeyStoreFragment: Private Key reference is android.security.keystore.AndroidKeyStoreRSAPrivateKey@5da42c27


Now, as you point out in your comment, kp.getPublic().getEncoded() will return the actual public key, but a public key's original purpose is not meant to be secret.

The private key is meant to be secret and while using a hardware-backed keystore with keys supported in the device's secure hardware, the secret keys are stored safely in the TEE/SE and cannot be extracted by the app itself or another bad actor with root privileges. You can see it in this example:

Log.d(TAG, "Private Key is " + Arrays.toString(kp.getPrivate().getEncoded()));

Logs:

D/KeyStoreFragment: Private Key is null


To verify your keys are supported by your device's secure hardware, you can use some variation of this code to suit your needs. You can paste this snippet after the same Log.d mentioned above in the example app's createKeys() method.

    KeyFactory factory = KeyFactory.getInstance(kp.getPrivate().getAlgorithm(), "AndroidKeyStore");
    KeyInfo keyInfo = null;
    try {
        keyInfo = factory.getKeySpec(kp.getPrivate(), KeyInfo.class);
    } catch (InvalidKeySpecException e) {
        e.printStackTrace();
    }
    if (keyInfo.isInsideSecureHardware())
        Log.d(TAG, "Key is supported in secure hardware");
    else
        Log.d(TAG, "Key is not supported in secure hardware");
Aluminous answered 19/9, 2018 at 0:34 Comment(5)
What about kp.getPublic().getEncoded()? it returns the key's byte array.Galleon
@Hi I'm Frogatto: It is the public key. By definition it has to be exportable as it is public.Cruces
@Steve Miskovetz: You code sample kp.getPublic().toString() causes confusion - you should change it to getPrivate().Cruces
@Cruces Yes. kp.getPrivate().getEncoded() returns null. It seems that using AndroidKeyStore the public key is exportable (as its name suggests), but the private key is kept securely in the hardware.Galleon
Thanks to both of you for the comments. I refactored my answer to give a clearer distinction between getPublic() and getPrivate() along with touching on getEncoded().Aluminous

© 2022 - 2024 — McMap. All rights reserved.