Unlocking Android Security: Harnessing Android Hardware KeyStore and Biometric Lock
This is the first part of 3 part series on Android Hardware KeyStore and Biometric authentication.
Before diving into the implementation, let’s begin by understanding why to use Android hardware keyStore and biometric authentication in your Android applications.
The Importance of KeyStore:
In the development of any Android application, safeguarding sensitive information from malicious apps or potential hackers is of paramount importance. This confidential data includes passwords, cryptographic keys, and other critical credentials. Consider the case of our project, MOSIP Inji, where we were developing a wallet app to securely store Government IDs, certificates, and third-party credentials. It’s evident that these datasets are highly sensitive. Any security lapse at this stage could not only jeopardize the government and MOSIP's image but also impact users significantly.
Initially, we used the React Native Keychain library to generate keys and encrypt user data before storing it in the app’s local storage. This approach provided a layer of security by preventing direct data access from the database. However, the underlying use of Android’s SharedPreferences
by the library fell short in terms of our security requirements.
Let’s explore why.
Android Secret Storage Methods:
- SharedPreferences
SharedPreferences is a basic method provided by Android for storing small key-value pairs persistently. It’s primarily intended for storing simple user preferences, settings, and configuration data. While it’s convenient to use and provides an easy way to retrieve values, it’s not suitable for storing sensitive information. SharedPreferences data is stored as XML files in the app’s private directory, and although it offers a degree of privacy, it’s not secure against determined attackers. SharedPreferences data can be accessed by other apps with the same user ID, and even though it’s not directly accessible by users, it can be accessed if the device is rooted or if the app’s data is compromised.
2. EncryptedSharedPreferences
EncryptedSharedPreferences is an improvement over the basic SharedPreferences when it comes to storing sensitive data. Introduced in Android’s Security library, it provides a more secure storage option by encrypting the data before storing it in SharedPreferences. This ensures that the data is protected even if an attacker gains access to the device’s filesystem. EncryptedSharedPreferences uses the Android Keystore system to generate and store encryption keys. It’s a suitable option for applications that need to store sensitive data, such as access tokens or API keys, but it’s worth noting that its security level depends on how well the keys are secured. if keys are not stored securely, Hacker who has access to keys will be able to decrypt the data. So hardware keyStore can be used to store the keys.
3. Android Hardware KeyStore
The Android Hardware KeyStore is a dedicated and secure hardware-backed storage area for cryptographic keys and sensitive data. It leverages the Trusted Execution Environment (TEE), a secure enclave within the device’s hardware, to ensure that keys are stored in a tamper-resistant manner. The keys are generated, stored, and used within this secure environment, making it extremely difficult for attackers to extract them. The Hardware Keystore provides APIs for generating and managing cryptographic keys, and it’s designed to protect against various attack vectors, including physical attacks and software-based attacks. It’s the most secure option for storing sensitive data on Android devices and is often used for operations such as encryption, decryption, and digital signatures within applications. Additionally, it supports biometric authentication, adding an extra layer of security by requiring user verification before granting access to the stored keys.
In summary, while SharedPreferences is suitable for basic configuration data, EncryptedSharedPreferences provides a more secure option for storing sensitive data, and the Android Hardware Keystore offers the highest level of security for cryptographic keys and confidential information.
The Birth of a New React Native Library:
Faced with the inadequacy of SharedPreferences
and the absence of a suitable React Native library, we embarked on developing our own native module— SecureKeyStore. As this blog focuses solely on Android concepts, we will refrain from delving into React Native specifics.
Our objective with SecureKeyStore encompassed:
- Key Generation and Storage in Hardware KeyStore:
- Symmetric Key (AES/GCM): For user data encryption.
- Asymmetric Key (RSA): For secure communication with the server.
- HMAC Key: To protect encrypted data from tampering.
2. Cryptographic Operations:
- Encryption
- Decryption
- Signature Creation
- HMAC SHA Generation
3. Biometric Secured Key Access: We’ll discuss the rationale for this later.
4. Key and Device Details:
- Determining hardware keystore support on the device.
Now that we’ve established the ‘why’ and ‘what’ of our endeavor, let’s delve into the ‘how’.
Generating Keys:
For key generation, we’ll utilize two Android classes:
- KeySpec Builder:
- Configures key specifications, including algorithm details, authentication parameters, purpose, and validity.
2. KeyGenerator:
- Generates and stores keys in the hardware keystore.
Let’s start by constructing a spec for an AES/GCM key:
val purposes = PURPOSE_ENCRYPT or PURPOSE_DECRYPT
val keySpecBuilder = KeyGenParameterSpec.Builder(alias, purposes)
.setKeySize(2048)
.setDigests(DIGEST_SHA256, DIGEST_SHA512)
.setEncryptionPaddings(ENCRYPTION_PADDING_RSA_PKCS1)
.setSignaturePaddings(SIGNATURE_PADDING_RSA_PKCS1)
The first parameter ‘alias’ is a string used to access the key once stored.
The ‘purposes’ property defines the permissible key uses. In our case, encryption and decryption are chosen. Other valid purposes include SIGN, VERIFY, AGREE, ATTEST, and WRAP KEY. We’ll focus on ENCRYPT, DECRYPT, SIGN, and VERIFY in this blog, as they are the most relevant.
note: Algorithms support a subset of these purposes. Specifying an unsupported purpose results in an exception. android.security.KeyStoreException: Unsupported purpose
Other parameters are algorithm-specific.
With the specification in place, let’s initialize the KeyGenerator:
const val KEYSTORE_TYPE = "AndroidKeyStore"
KeyGenerator.getInstance(KEY_ALGORITHM_AES, KEYSTORE_TYPE)
The first argument specifies the desired algorithm — for instance, AES in this example. It’s advisable to avoid hardcoding this value and instead import it from android.security.keystore.KeyProperties.KEY_ALGORITHM_AES
.
The second argument designates the keyStore provider. “AndroidKeyStore” is the default, while alternatives like Bouncy Castle can also be used.
Both KeyGenerator and key spec are now initialized, allowing us to proceed with key generation:
keyGenerator.init(keySpecBuilder.build())
val key = keyGenerator.generateKey()
The ‘generateKey’ function creates a SecretKey and stores it in the app-level keyStore for future retrieval — storing is not documented in Android docs but based on other blogs and usages this is undocumented functionality.
For generating an asymmetric key, we use KeyPairGenerator
and KeyGenParameterSpec
:
val purposes = PURPOSE_SIGN or PURPOSE_VERIFY
val keySpecBuilder = KeyGenParameterSpec.Builder(alias, purposes)
.setKeySize(KEY_PAIR_KEY_SIZE)
.setDigests(DIGEST_SHA256, DIGEST_SHA512)
.setEncryptionPaddings(ENCRYPTION_PADDING_RSA_PKCS1)
.setSignaturePaddings(SIGNATURE_PADDING_RSA_PKCS1)
val keyPairGenerator = KeyPairGenerator.getInstance(KEY_ALGORITHM_RSA, KEYSTORE_TYPE)
keyPairGenerator.initialize(keySpecBuilder.build())
keyPairGenerator.generateKeyPair()
The result is an KeyPair
instance comprising both public and private keys.
Public key is accessible for the app but both Private key and also Secret key won’t be accessible in the app and can only be used for crypto operations. If you try to log any of these, you will get null logged.
Utilizing these keys for crypto operations will be covered in the next part...
This is my first blog ever so feel free to leave any corrections, feedback, or likes.
Singing off,
Tilak Puli