Introduction: The Dual Mandate of Android IoT Security
In the rapidly expanding landscape of Android IoT, Automotive, and Smart TV devices, security is paramount. Protecting sensitive data and ensuring device integrity often relies on cryptographic operations. The Android Keystore system provides a robust mechanism for managing cryptographic keys, with a critical distinction between software-backed and hardware-backed keys. Hardware-backed keys, residing in a Trusted Execution Environment (TEE) or a dedicated StrongBox Security Module, offer superior protection against extraction and tampering. However, this enhanced security often comes with a performance overhead. This article delves into benchmarking hardware-backed Keystore operations, providing a practical guide for developers to understand and measure this trade-off in Android IoT applications.
Understanding the performance characteristics of hardware-backed cryptographic operations is crucial for designing responsive and secure IoT solutions. Developers must carefully balance the need for strong security guarantees with the performance demands of their applications, especially in resource-constrained environments typical of many IoT devices.
Why Hardware-Backed Keys Matter for IoT
IoT devices are often deployed in environments with varying levels of physical security, making them susceptible to advanced persistent threats. Hardware-backed keys mitigate many common attack vectors:
- Key Extraction Prevention: Keys never leave the secure hardware, making them resistant to software exploits.
- Tamper Resistance: The secure environment (TEE/StrongBox) is designed to detect and react to tampering attempts.
- Attestation: Hardware-backed keys can often be attested to prove their origin and properties, enhancing trust.
- Irreversibility: Operations like signing cannot be repudiated as the private key cannot be exported.
These benefits are particularly valuable for sensitive operations such as secure boot, firmware updates, device identity, and protecting user data in smart devices.
Android Keystore System Overview
The Android Keystore system provides APIs for applications to generate, store, and use cryptographic keys. It abstracts away the underlying hardware details, allowing developers to specify key properties. Key properties determine whether a key is hardware-backed, its usage restrictions, and other security characteristics.
Requesting Hardware-Backed Keys
When generating a key, developers can explicitly request hardware backing. The system attempts to provision the key in the most secure hardware available (StrongBox, then TEE). If hardware backing is not available or fails, it falls back to software-backed keys unless explicitly disallowed.
Key properties are defined using KeyGenParameterSpec. To request hardware backing, you specify setIsStrongBoxBacked(true) for StrongBox or rely on the default behavior that attempts TEE if StrongBox is not requested. For this article, we’ll focus on ensuring a hardware-backed key by checking its properties.
Benchmarking Setup and Methodology
To accurately benchmark Keystore operations, we need a consistent environment and a clear methodology. For Android IoT, this typically means a physical device running a recent Android version (e.g., Android 9+ for StrongBox support). For our examples, we assume a device with TEE support for hardware-backed keys.
Measurement Techniques
We will measure the execution time of key generation, encryption, decryption, signing, and verification using System.nanoTime(). It’s crucial to perform multiple iterations and average the results to account for system fluctuations and JIT compilation. Warming up the JVM before actual measurements can also help achieve more consistent results.
We will compare a software-backed key’s performance against a hardware-backed key for identical cryptographic operations and key parameters.
Prerequisites
- Android Studio with an Android IoT target device (e.g., an embedded board, AOSP-based TV, or automotive head unit).
- Minimum API Level 23 (Android 6.0) for Keystore features, API Level 28 (Android 9.0) for StrongBox.
Code Implementation: Generating and Using Keys
Let’s create a simple Android application to perform these benchmarks. We’ll use AES/GCM for symmetric encryption and RSA/PSS for asymmetric signing, as these are common and recommended algorithms.
1. Generating Hardware-Backed Keys
The following code snippet demonstrates how to generate an AES key and an RSA key, explicitly trying to get them hardware-backed. After generation, we verify if the key is indeed hardware-backed.
import android.security.keystore.KeyGenParameterSpec;import android.security.keystore.KeyProperties;import java.security.KeyPairGenerator;import java.security.KeyStore;import java.security.PrivateKey;import java.security.PublicKey;import java.security.spec.AlgorithmParameterSpec;import javax.crypto.KeyGenerator;import javax.crypto.SecretKey;import android.util.Log;public class KeystoreManager { private static final String TAG = "KeystoreBench"; private static final String ANDROID_KEYSTORE = "AndroidKeyStore"; public SecretKey generateAesKey(String alias, boolean requireHardwareBacked) throws Exception { long startTime = System.nanoTime(); KeyGenerator keyGenerator = KeyGenerator.getInstance(KeyProperties.KEY_ALGORITHM_AES, ANDROID_KEYSTORE); KeyGenParameterSpec.Builder builder = new KeyGenParameterSpec.Builder(alias, KeyProperties.PURPOSE_ENCRYPT | KeyProperties.PURPOSE_DECRYPT) .setBlockModes(KeyProperties.BLOCK_MODE_GCM) .setEncryptionPaddings(KeyProperties.ENCRYPTION_PADDING_NONE) .setKeySize(256) .setRandomizedEncryptionRequired(true); if (requireHardwareBacked) { builder.setIsStrongBoxBacked(true); // Attempt StrongBox first // If StrongBox isn't available, system might fall back to TEE, // but if StrongBox is explicitly requested and fails, generation will fail. // For TEE only, remove setIsStrongBoxBacked and just rely on default. } keyGenerator.init(builder.build()); SecretKey secretKey = keyGenerator.generateKey(); long endTime = System.nanoTime(); Log.d(TAG, "AES Key Generation Time: " + (endTime - startTime) / 1_000_000.0 + " ms"); return secretKey; } public KeyPair generateRsaKeyPair(String alias, boolean requireHardwareBacked) throws Exception { long startTime = System.nanoTime(); KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance(KeyProperties.KEY_ALGORITHM_RSA, ANDROID_KEYSTORE); AlgorithmParameterSpec spec = new KeyGenParameterSpec.Builder(alias, KeyProperties.PURPOSE_SIGN | KeyProperties.PURPOSE_VERIFY) .setDigests(KeyProperties.DIGEST_SHA256) .setSignaturePaddings(KeyProperties.SIGNATURE_PADDING_RSA_PSS) .setKeySize(2048) .setIsUserAuthenticationRequired(false) // For simpler benchmarking, but for production, consider true .setIsStrongBoxBacked(requireHardwareBacked ? true : false) // StrongBox attempt .build(); keyPairGenerator.initialize(spec); KeyPair keyPair = keyPairGenerator.generateKeyPair(); long endTime = System.nanoTime(); Log.d(TAG, "RSA KeyPair Generation Time: " + (endTime - startTime) / 1_000_000.0 + " ms"); return keyPair; } public boolean isKeyHardwareBacked(String alias) throws Exception { KeyStore keyStore = KeyStore.getInstance(ANDROID_KEYSTORE); keyStore.load(null); KeyStore.Entry entry = keyStore.getEntry(alias, null); if (entry instanceof KeyStore.PrivateKeyEntry) { return ((KeyStore.PrivateKeyEntry) entry).getPrivateKey().isHardwareBacked(); } else if (entry instanceof KeyStore.SecretKeyEntry) { return ((KeyStore.SecretKeyEntry) entry).getSecretKey().isHardwareBacked(); } return false; }}
2. Performing Benchmarked Operations
Now, let’s implement the methods for encryption/decryption (AES) and signing/verification (RSA). We will wrap these operations in a loop to get average timings.
import javax.crypto.Cipher;import javax.crypto.spec.GCMParameterSpec;import java.security.Signature;import java.util.Arrays;import java.util.concurrent.TimeUnit;public class CryptoBenchmarker { private static final String TAG = "CryptoBenchmarker"; private static final String AES_TRANSFORMATION = "AES/GCM/NoPadding"; private static final String RSA_SIGNATURE_ALGORITHM = "SHA256withRSA/PSS"; private static final int IV_LENGTH = 12; // GCM recommended IV length private static final int GCM_TAG_LENGTH = 128; // GCM tag length in bits public static long benchmarkAesEncrypt(SecretKey secretKey, byte[] data, int iterations) throws Exception { long totalTime = 0; Cipher cipher = Cipher.getInstance(AES_TRANSFORMATION); for (int i = 0; i < iterations; i++) { byte[] iv = new byte[IV_LENGTH]; // Generate new IV for each encryption new java.security.SecureRandom().nextBytes(iv); GCMParameterSpec gcmSpec = new GCMParameterSpec(GCM_TAG_LENGTH, iv); long startTime = System.nanoTime(); cipher.init(Cipher.ENCRYPT_MODE, secretKey, gcmSpec); cipher.doFinal(data); long endTime = System.nanoTime(); totalTime += (endTime - startTime); } return TimeUnit.NANOSECONDS.toMillis(totalTime / iterations); } public static long benchmarkAesDecrypt(SecretKey secretKey, byte[] encryptedData, byte[] iv, int iterations) throws Exception { long totalTime = 0; Cipher cipher = Cipher.getInstance(AES_TRANSFORMATION); GCMParameterSpec gcmSpec = new GCMParameterSpec(GCM_TAG_LENGTH, iv); for (int i = 0; i < iterations; i++) { long startTime = System.nanoTime(); cipher.init(Cipher.DECRYPT_MODE, secretKey, gcmSpec); cipher.doFinal(encryptedData); long endTime = System.nanoTime(); totalTime += (endTime - startTime); } return TimeUnit.NANOSECONDS.toMillis(totalTime / iterations); } public static long benchmarkRsaSign(PrivateKey privateKey, byte[] data, int iterations) throws Exception { long totalTime = 0; Signature signature = Signature.getInstance(RSA_SIGNATURE_ALGORITHM); for (int i = 0; i < iterations; i++) { long startTime = System.nanoTime(); signature.initSign(privateKey); signature.update(data); signature.sign(); long endTime = System.nanoTime(); totalTime += (endTime - startTime); } return TimeUnit.NANOSECONDS.toMillis(totalTime / iterations); } public static long benchmarkRsaVerify(PublicKey publicKey, byte[] data, byte[] signatureBytes, int iterations) throws Exception { long totalTime = 0; Signature signature = Signature.getInstance(RSA_SIGNATURE_ALGORITHM); for (int i = 0; i < iterations; i++) { long startTime = System.nanoTime(); signature.initVerify(publicKey); signature.update(data); signature.verify(signatureBytes); long endTime = System.nanoTime(); totalTime += (endTime - startTime); } return TimeUnit.NANOSECONDS.toMillis(totalTime / iterations); }}
3. Orchestrating the Benchmark
In your main Activity or a dedicated test class, run the benchmarks. Remember to clean up keys after testing.
// Inside your Activity or a test methodKeystoreManager km = new KeystoreManager();byte[] sampleData = new byte[1024]; // 1KB data for crypto operationsnew java.security.SecureRandom().nextBytes(sampleData);int iterations = 100; // Number of iterations for averaging measurementsLog.d(TAG, "Starting Keystore Benchmarks...");try { // --- AES Benchmarks --- Log.d(TAG, "n--- AES 256-bit Benchmarks ---"); // Software-backed AES SecretKey aesSoftKey = km.generateAesKey("aes_soft_key", false); Log.d(TAG, "AES Soft Key Hardware Backed: " + km.isKeyHardwareBacked("aes_soft_key")); long softEncryptTime = CryptoBenchmarker.benchmarkAesEncrypt(aesSoftKey, sampleData, iterations); Log.d(TAG, "Avg Soft AES Encrypt (1KB): " + softEncryptTime + " ms"); byte[] ivSoft = new byte[12]; new java.security.SecureRandom().nextBytes(ivSoft); // Example IV for decryption Cipher tempCipherSoft = Cipher.getInstance("AES/GCM/NoPadding"); tempCipherSoft.init(Cipher.ENCRYPT_MODE, aesSoftKey, new GCMParameterSpec(128, ivSoft)); byte[] encryptedSoftData = tempCipherSoft.doFinal(sampleData); long softDecryptTime = CryptoBenchmarker.benchmarkAesDecrypt(aesSoftKey, encryptedSoftData, ivSoft, iterations); Log.d(TAG, "Avg Soft AES Decrypt (1KB): " + softDecryptTime + " ms"); // Hardware-backed AES SecretKey aesHardKey = km.generateAesKey("aes_hard_key", true); Log.d(TAG, "AES Hard Key Hardware Backed: " + km.isKeyHardwareBacked("aes_hard_key")); long hardEncryptTime = CryptoBenchmarker.benchmarkAesEncrypt(aesHardKey, sampleData, iterations); Log.d(TAG, "Avg Hard AES Encrypt (1KB): " + hardEncryptTime + " ms"); byte[] ivHard = new byte[12]; new java.security.SecureRandom().nextBytes(ivHard); Cipher tempCipherHard = Cipher.getInstance("AES/GCM/NoPadding"); tempCipherHard.init(Cipher.ENCRYPT_MODE, aesHardKey, new GCMParameterSpec(128, ivHard)); byte[] encryptedHardData = tempCipherHard.doFinal(sampleData); long hardDecryptTime = CryptoBenchmarker.benchmarkAesDecrypt(aesHardKey, encryptedHardData, ivHard, iterations); Log.d(TAG, "Avg Hard AES Decrypt (1KB): " + hardDecryptTime + " ms"); // --- RSA Benchmarks --- Log.d(TAG, "n--- RSA 2048-bit Benchmarks ---"); // Software-backed RSA KeyPair rsaSoftKeyPair = km.generateRsaKeyPair("rsa_soft_key", false); Log.d(TAG, "RSA Soft Key Hardware Backed: " + km.isKeyHardwareBacked("rsa_soft_key")); long softSignTime = CryptoBenchmarker.benchmarkRsaSign(rsaSoftKeyPair.getPrivate(), sampleData, iterations); Log.d(TAG, "Avg Soft RSA Sign (1KB): " + softSignTime + " ms"); Signature tempSigSoft = Signature.getInstance("SHA256withRSA/PSS"); tempSigSoft.initSign(rsaSoftKeyPair.getPrivate()); tempSigSoft.update(sampleData); byte[] signatureSoft = tempSigSoft.sign(); long softVerifyTime = CryptoBenchmarker.benchmarkRsaVerify(rsaSoftKeyPair.getPublic(), sampleData, signatureSoft, iterations); Log.d(TAG, "Avg Soft RSA Verify (1KB): " + softVerifyTime + " ms"); // Hardware-backed RSA KeyPair rsaHardKeyPair = km.generateRsaKeyPair("rsa_hard_key", true); Log.d(TAG, "RSA Hard Key Hardware Backed: " + km.isKeyHardwareBacked("rsa_hard_key")); long hardSignTime = CryptoBenchmarker.benchmarkRsaSign(rsaHardKeyPair.getPrivate(), sampleData, iterations); Log.d(TAG, "Avg Hard RSA Sign (1KB): " + hardSignTime + " ms"); Signature tempSigHard = Signature.getInstance("SHA256withRSA/PSS"); tempSigHard.initSign(rsaHardKeyPair.getPrivate()); tempSigHard.update(sampleData); byte[] signatureHard = tempSigHard.sign(); long hardVerifyTime = CryptoBenchmarker.benchmarkRsaVerify(rsaHardKeyPair.getPublic(), sampleData, signatureHard, iterations); Log.d(TAG, "Avg Hard RSA Verify (1KB): " + hardVerifyTime + " ms");} catch (Exception e) { Log.e(TAG, "Benchmark error: ", e);} finally { // Clean up keys try { KeyStore keyStore = KeyStore.getInstance(ANDROID_KEYSTORE); keyStore.load(null); keyStore.deleteEntry("aes_soft_key"); keyStore.deleteEntry("aes_hard_key"); keyStore.deleteEntry("rsa_soft_key"); keyStore.deleteEntry("rsa_hard_key"); } catch (Exception e) { Log.e(TAG, "Key cleanup failed: ", e); }}
Performance Analysis and Discussion
Running the above benchmarks on an actual Android IoT device will yield different results depending on the underlying hardware (e.g., specific SoC, TEE implementation, StrongBox module). However, general trends are expected:
- Key Generation: Hardware-backed key generation is typically significantly slower than software-backed. This is due to the secure environment’s overhead in generating, provisioning, and protecting the key.
- Symmetric Operations (AES): For AES encryption and decryption, the performance difference between hardware and software might be noticeable but often less dramatic than asymmetric operations, especially for smaller data sizes. Modern CPUs often have dedicated AES instructions that are highly optimized in software, narrowing the gap. However, the secure environment’s context switching and data transfer overhead for hardware-backed operations will still incur a cost.
- Asymmetric Operations (RSA Sign/Verify): Asymmetric operations like RSA signing and verification will generally show a more pronounced performance difference. These operations are computationally intensive, and the secure hardware processing them often involves additional latency. Verification using the public key might sometimes be performed outside the TEE, hence potentially faster, but private key operations (signing) must always happen within the secure hardware.
Factors Influencing Performance:
- Device Hardware: The specific CPU, TEE (e.g., ARM TrustZone implementation), and StrongBox hardware directly impact performance.
- Key Size: Larger key sizes (e.g., 4096-bit RSA) will exponentially increase processing time for both hardware and software, with hardware potentially scaling worse due to fixed processing units.
- Data Size: The amount of data being encrypted, decrypted, signed, or verified will affect the runtime.
- Algorithm Complexity: Different cryptographic algorithms have varying computational demands.
- System Load: Other processes running on the device can influence benchmark results.
Best Practices for Balancing Performance and Security
Based on the benchmarking results, developers can make informed decisions:
- Prioritize Hardware for Critical Keys: Always use hardware-backed keys for sensitive operations like device attestation, root of trust, and protecting long-term secrets.
- Optimize Key Usage: Avoid frequent key generation. Generate keys once and store them securely.
- Batch Operations: For large volumes of data, consider encrypting/decrypting in chunks or using a hybrid approach where a hardware-backed key encrypts a symmetric key, which then encrypts the bulk data (though AES-GCM is typically efficient).
- Profile and Test: Always profile your specific IoT application on target hardware to identify bottlenecks and optimize.
- Understand Trade-offs: Be aware that every additional layer of security, especially involving secure hardware, introduces some performance overhead. Document and justify these trade-offs.
- Use Appropriate Algorithms: Stick to modern, efficient, and secure algorithms (e.g., AES-256 GCM, RSA-2048/3072 PSS).
Conclusion
Benchmarking hardware-backed Keystore operations is an essential exercise for any developer working on secure Android IoT, Automotive, or Smart TV applications. While hardware backing significantly enhances the security posture of cryptographic keys, it introduces a measurable performance impact. By understanding these trade-offs through practical benchmarking, developers can architect robust, secure, and performant solutions that meet the stringent demands of modern IoT ecosystems, ensuring both data integrity and a smooth user experience.
Android Mobile Specs & Compare Directory
Are you researching mobile hardware properties, processor SoCs, GPU chipsets, or RAM configurations? Access our complete specs catalog to compare up to 5 devices side-by-side!
Compare Devices Specs →