kyo171
kyo171

Reputation: 419

How to tell if keys are hardware backed?

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() }

            }

Upvotes: 2

Views: 3337

Answers (1)

Janis Danisevskis
Janis Danisevskis

Reputation: 21

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.

Upvotes: 2

Related Questions