1

I need to tell if my keys created are in the hardware backed AndroidKeyStore(TEE or SE) or the software backed implementation.

I understand that for api < 23 I should be using KeyChain.isBoundKeyAlgorithm(algorithm) and for api >= 23 I should use keyInfo.isInsideSecureHardware.

However, I have a Nexus 6, Android 7.1.1 api level 25. I created a keypair in AndroidKeyStore and when I call keyInfo.isInsideSecureHardware, it returns false but when i try calling KeyChain.isBoundKeyAlgorithm(algorithm), it returns true.

So my question is, is the keypair inside the hardware backed keystore(TEE/SE) or not?

Here is my function to check if the key is inside hardware back keystore or not

fun checkKeyInHardware(): Boolean {

            val key = androidKeyStore.getKey(AUTH_ALIAS, null) as PrivateKey
            val algorithm = key.algorithm

            Log.d("checkKeyInHardware", "Key algo = $algorithm")
            Log.d("checkKeyInHardware", "KeyChain.isBoundKeyAlgorithm(algorithm) = ${KeyChain.isBoundKeyAlgorithm(algorithm)}")

            return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
                val factory = KeyFactory.getInstance(algorithm, androidKeyStoreProvider)
                val keyInfo =
                        try {
                            factory.getKeySpec(key, KeyInfo::class.java)
                        } catch (e: InvalidKeySpecException) {
                            Log.d("checkKeyInHardware", "not in AndroidKeyStore")
                            null
                        }

                return if(keyInfo == null) {
                    Log.d("checkKeyInHardware", "keyinfo is null")
                    false
                }
                else {
                    Log.d("checkKeyInHardware", "keyInfo.isInsideSecureHardware = ${keyInfo.isInsideSecureHardware}")
                    keyInfo.isInsideSecureHardware || KeyChain.isBoundKeyAlgorithm(algorithm)
                }

            } else {
                KeyChain.isBoundKeyAlgorithm(algorithm)
            }

        }

and here is how i generate my key pairs

if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {

                val originationEnd = Date(start.time + ORIGINATION_TIME_OFFSET)
                val consumptionEnd = Date(end.time + CONSUMPTION_TIME_OFFSET)

                val kpg = KeyPairGenerator.getInstance(KeyProperties.KEY_ALGORITHM_EC, androidKeyStoreProvider)

                val paramSpecBuilder: KeyGenParameterSpec.Builder = KeyGenParameterSpec
                        .Builder(AUTH_ALIAS, KeyProperties.PURPOSE_SIGN)

                paramSpecBuilder.apply {
                    setDigests(KeyProperties.DIGEST_SHA256, KeyProperties.DIGEST_SHA384, KeyProperties.DIGEST_SHA512)
                    setAlgorithmParameterSpec(ecGenParameterSpec)
                    setCertificateNotBefore(start)
                    setCertificateNotAfter(end)
                    setKeyValidityStart(start)
                    setKeyValidityEnd(end)
                    setKeyValidityForOriginationEnd(originationEnd)
                    setKeyValidityForConsumptionEnd(consumptionEnd)
                    setCertificateSubject(subjectPrincipal)
                    setUserAuthenticationRequired(false)
                    setCertificateSerialNumber(serialNumber)

                    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N)
                        setAttestationChallenge(challenge)
                }

                if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
                    val paramSpec = paramSpecBuilder.setIsStrongBoxBacked(true).build()
                    kpg.initialize(paramSpec)

                    try {
                        generateKeyCatcher { kpg.generateKeyPair() }
                    } catch (e: StrongBoxUnavailableException) {
                        val weakBoxParamSpec = paramSpecBuilder.setIsStrongBoxBacked(false).build()
                        kpg.initialize(weakBoxParamSpec)
                        generateKeyCatcher { kpg.generateKeyPair() }
                    }
                } else {
                    val paramSpec = paramSpecBuilder.build()
                    Log.d("GenerateKey", "PROVIDER = ${kpg.provider.name}")
                    kpg.initialize(paramSpec)
                    generateKeyCatcher { kpg.generateKeyPair() }
                }

            }
            else {

                rearrangeSecurityProviders()

                val kpg = KeyPairGenerator.getInstance("RSA", androidKeyStoreProvider)

                val spec = KeyPairGeneratorSpec.Builder(context).run {
                    setAlias(AUTH_ALIAS)
                    setSubject(subjectPrincipal)
                    setSerialNumber(BigInteger.ONE)
                    setKeyType("EC")
                    setKeySize(256)
                    setAlgorithmParameterSpec(ecGenParameterSpec)
                    setStartDate(start)
                    setEndDate(end)
                    build()
                }

                Log.d("GenerateKey", "PROVIDER = ${kpg.provider.name}")
                Log.d("GenerateKey", "PROVIDER = ${kpg.provider}")
                kpg.initialize(spec)
                generateKeyCatcher { kpg.generateKeyPair() }

            }
kyo171
  • 399
  • 4
  • 9
  • Does this answer your question? [In Android, how to ensure that Private Key is stored inside secure hardware](https://stackoverflow.com/questions/38178683/in-android-how-to-ensure-that-private-key-is-stored-inside-secure-hardware) – Josh Correia Jul 28 '20 at 18:10

1 Answers1

2

Your key is not hardware backed.

KeyInfo.isInsideSecureHardware() provides reliable information about the key in question, whereas KeyChain.isBoundKeyAlgorithm() will tell if the back-end, i.e., Keymaster the TEE or SE trusted app, supports the given algorithm.

There can be a few reasons why you see this discrepancy. Keymaster versions < 1.0 implemented some parameter combinations but not others. There were also buggy KM 1.0 implementations that occasionally failed to generate EC keys. In both cases a software fallback would take over and generate the key in software.

KeyChain.isBoundKeyAlgorithm() is too coarse a check to tell if every combination is supported, and it won't tell if the back-end is buggy. So despite it returning true, your key ends up being software backed.

Starting with KM2.0 this fallback as been deactivated. So devices with KM2.0 or newer will generate the key in hardware (TEE/SE) or not at all.

For reliable information about asymmetric keys and conveying this information to a remote relying party, consider using Keystore Key Attestation. However, this feature is hardware enforced only since KM2.0 and newer.

  • H, thanks for your reply. _There were also buggy KM 1.0 implementations that occasionally failed to generate EC keys_ It seems to not happen occasionally for me though. Is there any config that i am using to cause it NOT to be generated in hardware? – kyo171 Dec 04 '19 at 03:37
  • I tried 2 phones, a galaxy s5 and a moto-g both on api 23. android 6.0.1. On the galaxy s5 the EC key i created was stored in hardware while the one in the moto-g was not. this is quite inconsistent. is there any config that may cause this to happen? – kyo171 Dec 04 '19 at 13:15
  • Keystore support is largely vendor specific, so it is impossible to tell which config is supported on which device. Things should get much more homogeneous on devices that shipped with Android 8. Having said that, you could try using SHA256 digests only. Also RSA is more consistently supported on older devices. – Janis Danisevskis Dec 04 '19 at 17:33