This is an old revision of the document!
The Android Keystore system (tutorial, documentation) provides mechanisms for securing storing and for generating the cryptographic keys needed by an app. Its role as a security container is guaranteed by the fact that the keys cannot be extracted from the application's process and from the device.
Why do we need this?
The Android M (Marshmallow, API Level 23) release introduced several security related improvements and features: runtime permissions, different SSL library and updates for its Keystore API, which is now supporting also symmetric keys in addition to the asymmetric available from API 18. The way we generate these keys also changed from this level, now using KeyGenParameterSpec instead of the KeyPairGeneratorSpec.
Terminology and classes related to the Keystore system, some of them from Java Cryptography API:
load(null)
. This password is unrelated to the device password if a LockScreen is setup.keyStore = KeyStore.getInstance("AndroidKeyStore"); keyStore.load(inputStream, password);
To obtain the keys:
If you have not provided a password to the keystore, when you get the keys just provide null as the password parameter.
To generate the keys (API > 23):
// (1) KeyPairGenerator generator = KeyPairGenerator.getInstance("RSA", "AndroidKeyStore"); // instead of using hardcoded strings, we can also use the KeyProperties.KEY_ALGORITHM_* variables. // (2) create keys only used for signing KeyGenParameterSpec keyGenParameterSpec = new KeyGenParameterSpec.Builder(alias, KeyProperties.PURPOSE_SIGN) .setDigests(KeyProperties.DIGEST_SHA256, KeyProperties.DIGEST_SHA512) .setSignaturePaddings(KeyProperties.ENCRYPTION_PADDING_NONE) .build(); // (3) generator.initialize(keyGenParameterSpec); // (4) generator.generateKeyPair();
To store the keys:
generateKeyPair()
of the KeyPairGenerator.Using the keys obtained from the Keystore, we can perform cryptographic transformations on our data using the Cipher class.
In the following example we encrypt and decrypt data using a symmetric key algorithm. For an asymmetric-key algorithm the code is similar, it differs just the initialization of the Cipher and that we provide the public key for encryption and the private one for decryption.
// The KeyPairGenerator was configured with the spec: // * KeyProperties.KEY_ALGORITHM_AES (symmetric-key algorithm) // * KeyProperties.BLOCK_MODE_GCM // * KeyProperties.ENCRYPTION_PADDING_NONE Cipher cipher = Cipher.getInstance("AES/GCM/NoPadding"); //<-- provide the same configuration! // Encrypt cipher.init(Cipher.ENCRYPT_MODE, secretKey); byte[] bytes = cipher.doFinal(data); String encodedData = Base64.encodeToString(bytes, Base64.DEFAULT); // Decrypt cipher.init(Cipher.DECRYPT_MODE, secretKey); byte[] encryptedData = Base64.decode(encodedData, Base64.DEFAULT); String data = new String(cipher.doFinal(encryptedData));
In this lab we create an app that stores our pins and/or passwords. It must have the following features and UI components:
Target API >= 23.
To improve the app's security allow access to the app only if the device is protected by a Lock screen. Don't allow the use of the app unless it is set.
getSystemService
and providing it a string representing the service, in this case the Context.KEYGUARD_SERVICE constant.KeyguardManager.isDeviceSecure
methodsetPositiveButton
send an implicit intent with the action DevicePolicyManager.ACTION_SET_NEW_PASSWORD
setNegativeButton
close the app (call finish()
)If you skip this task, the decryption might throw an Exception (see solution).
If you generate the keys with a certain parameter configuration and then you change it, you need to clear the application's data in order for the encryption/decryption to work because you reuse the keys already existing in the keystore. You can also uninstall the app in order to clear the data. Running the app from the android studio's run button won't clear the app's data.