In this lab we create an app that stores our pins and/or passwords.
It must have the following features and UI components:
You can create your own project, or use the one provided by us archive github
Target API >= 23. - the reason is provided in the Android Keystore section
If you are Android users you might have noticed that certain apps require you to setup a lock screen for your device if you don't have one already.
Android's Keyguard handles the lock/unlocking of the device. The SDK offers the KeyguardManager system service using which you can check if the device is secured with a screen lock and if it's locked or not.
To interact with this service, you need to obtain it using the Context's method getSystemService
and providing it a string representing the service, in this case the Context.KEYGUARD_SERVICE constant. You have done something similar, in a previous lab for obtaining Connectivity Service.
// assuming this is called from an Activity, which is a Context keyguardService = (KeyguardService) getSystemService(Context.KEYGUARD_SERVICE);
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.
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 and your phone is not secured, the decryption from the other tasks might throw an Exception unless you set a flag like in this solution.
The Android Keystore system (tutorial, documentation) provides mechanisms for securing storage 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.When the app starts, initialize the Keystore and generate a pair of keys if they don't exist yet.
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_RSA (asymmetric-key algorithm) // * KeyProperties. ENCRYPTION_PADDING_RSA_PKCS1 Cipher cipher = Cipher.getInstance("RSA/ECB/PKCS1Padding"); //<-- provide the same configuration! // Encrypt cipher.init(Cipher.ENCRYPT_MODE, publicKey); byte[] bytes = cipher.doFinal(data); String encodedData = Base64.encodeToString(bytes, Base64.DEFAULT); // Decrypt cipher.init(Cipher.DECRYPT_MODE, privateKey); byte[] encryptedData = Base64.decode(encodedData, Base64.DEFAULT); String data = new String(cipher.doFinal(encryptedData));
If you want to install an application on Android, the apk must be digitally signed with a certificate. For example, when you test your application on the emulator, Android Studio signs the apk with a debug certificate. The first time when you run or debug a project in Android Studio, a debug keystore and certificate is automatically created using the Android SDK tools in $HOME/.android/debug.keystore. Also the keystore is initialized and the key password is set.
As a security measure the debug certificate needs to be used only for testing and for debug builds. This certificate is not secure for using on app stores.
For later runs/debugs Android Studio automatically stores the debug signing configuration so that we do not need to enter it every time we launch the app. The signing configuration contains the keystore location $HOME/.android/debug.keystore, keystore password, key name and key password. This debug signing configuration used at run/debug is not available for editing. You can create a signing config for your release builds.
Steps for generating and uploading key and keystore:
From here we continue to sign the app with the key stored in the newly created keystore. You can skip the first two steps if you are already in the window at the 3rd step:
For more details on application signing and distribution check App Signing
In this task will see how Android applications are signed:
In this task will create a signing configuration for different build types.
Add an activity to the project. Include an EditText and a Button in the first activity. When the user types a text and presses the button, it will send the text to the second activity through an intent (putExtra). In the second activity, get the message from the Intent and display it in the TextView.
In the first activity generate a symmetric key using KeyGenerator for HmacSha256 algorithm. Save this key in a Singleton (that can be accessed from both activities). Then generate the HMAC of the text introduced by the user (using MAC with HmacSha256 algorithm) and send the HMAC along with the initial message (through the Intent). In the second activity, obtain the HMAC from the Intent, obtain the Singleton, get the symmetric key and recompute the HMAC. If the HMAC is valid (equal with the recomputed one), Display the message “Data is unmodified”.
Send the data and HMAC as byte arrays in the Intent. Use Arrays.equals() for byte arrays comparison.
String secret = "secret"; String message = "important message"; Mac sha256HMAC = Mac.getInstance("HmacSHA256"); SecretKeySpec secretKey = new SecretKeySpec(secret.getBytes(),"HmacSHA256"); sha256HMAC.init(secretkey); byte[] secretMessageBytes = sha256HMAC.doFinal(message.getBytes())