In Android 4.3, a new facility was added to allow apps to save private encryption keys in a system KeyStore . Called Android KeyStore, it restricts access only to the app that created them, and it was secured using the device pin code.
Specifically, the Android KeyStore is a certificate store, and so only public/private keys can be stored. Currently, arbitrary symmetric keys such as an AES key cannot be stored. In Android 4.4, the Elliptic Curve Digital Signature Algorithm (ECDSA) support was added to the Android KeyStore. This recipe discusses how to generate a new key, and save and fetch it from the Android KeyStore.
As this feature was only added in Android 4.3, ensure that the minimum SDK version in the Android manifest file is set to 18
.
Let's get started.
public static final String ANDROID_KEYSTORE = "AndroidKeyStore"; public void loadKeyStore() { try { keyStore = KeyStore.getInstance(ANDROID_KEYSTORE); keyStore.load(null); } catch (Exception e) { // TODO: Handle this appropriately in your app e.printStackTrace(); } }
public void generateNewKeyPair(String alias, Context context) throws Exception { Calendar start = Calendar.getInstance(); Calendar end = Calendar.getInstance(); // expires 1 year from today end.add(1, Calendar.YEAR); KeyPairGeneratorSpec spec = new KeyPairGeneratorSpec.Builder(context) .setAlias(alias) .setSubject(new X500Principal("CN=" + alias)) .setSerialNumber(BigInteger.TEN) .setStartDate(start.getTime()) .setEndDate(end.getTime()) .build(); // use the Android keystore KeyPairGenerator gen = KeyPairGenerator.getInstance("RSA", ANDROID_KEYSTORE); gen.initialize(spec); // generates the keypair gen.generateKeyPair(); }
public PrivateKey loadPrivteKey(String alias) throws Exception { if (keyStore.isKeyEntry(alias)) { Log.e(TAG, "Could not find key alias: " + alias); return null; } KeyStore.Entry entry = keyStore.getEntry(KEY_ALIAS, null); if (!(entry instanceof KeyStore.PrivateKeyEntry)) { Log.e(TAG, " alias: " + alias + " is not a PrivateKey"); return null; } return ((KeyStore.PrivateKeyEntry) entry).getPrivateKey(); }
The KeyStore
class has been around since API level 1. To access the new Android KeyStore, you use a special constant "AndroidKeystore"
.
According to the Google documentation, there is a strange issue with the KeyStore
class that requires you to call the load(null)
method even though you are not loading the KeyStore
from an input stream; otherwise, you may experience a crash.
When generating the key pair, we populate a KeyPairGeneratorSpec.Builder
object with the required details—including the alias that we use to retrieve it later. In this example, we set an arbitrary validation period of 1
year from the current date and default the serial to TEN
.
Loading a key from the alias is as simple as loading keyStore.getEntry("alias", null)
; from here, we cast to the PrivateKey
interface so that we can use it in our encryption/decryption.
The API for the KeyChain
class was also updated in Android 4.3 to allow developers to determine whether the device supports hardware-backed certificate store or not. This basically means that the device supports a secure element for the certificate store. This is an exciting enhancement as it promises to keep the certificate store safe even on rooted devices. However, not all devices support this hardware feature. The LG Nexus 4, a popular device, uses ARM's TrustZone for hardware protection.
KeyStore
class in the Android Developer reference guide at https://developer.android.com/reference/java/security/KeyStore.html18.224.58.122