Import encrypted AES key into Android Keystore and store it under new alias

What you want to achieve is not possible by simply using the AndroidKeystore. What you need is custom code that runs within the TEE.

The reason for this is simple: When you have set-up your app with an asymmetric key-pair stored in the AndroidKeystore and you receive the wrapped AES key it does not matter if the unwrapping takes place inside or outside the AndroidKeystore:

All keys of an app stored in the AndroidKeystore are usable by the app in the normal environment. Is is by design as you wouldn't be able to use them otherwise.

Hence if the asymmetric key-pair is usable by the app the app is always able to unwrap the received wrapped AES key (using code in the normal environment). Therefore it does not make any difference where the unwrapping takes place. You can not guarantee that someone had copied the wrapped AES key when the app received it and then unwrapped it using the asymmetric key from the AndroidKeystore.


What you're looking for now exists, as of API level 28 (Android Pie). To use you you need to:

  • Create a wrapping key pair, an RSA key pair with purpose PURPOSE_WRAP_KEY. You should also generate an attestation for the public key to verify that the private key is a keystore key, in secure hardware.
  • Send the public key (and attestation) from your app to the server that will provide the wrapped symmetric key.
  • On the server, you need to wrap the symmetric key. This involves more than just encrypting it, because the wrapper needs to contain not just the key material but also the authorization list that defines how the key may be used. This is done by packaging the key and authorization info in an ASN.1 DER-encoded structure, per the schema documented here. There's some sample wrapping code in the CTS test. Note that if this format seems excessively complicated (e.g. the optional "masking key"), it's because in a future Android release there will be a corresponding secure export function and the use cases for that require the additional complexity. The secure export function didn't make it into Q, but will probably make it into R.
  • Send the wrapped key to the app, which must create a WrappedKeyEntry and use Keystore.setEntry() to store it.

This should work on any device with API level 28. However, if the device has a Keymaster version < 4 (see the attestation certificate to find out what version of Keymaster is present), then the unwrapping operation will return the wrapped key material to Android userspace. Keymaster version 4 (or above) will keep the unwrapped material in secure hardware, but because lower versions don't have support for the wrapped key feature, it has to be sort of emulated.

What happens if you have a lower Keymaster version is that when you create a PURPOSE_WRAP_KEY key pair, what is actually requested of the secure hardware is a PURPOSE_DECRYPT key pair. Then when you do the import, the keystore daemon uses this PURPOSE_DECRYPT private key to decrypt the secret from the wrapper, then it imports the secret into the secure hardware and wipes the userspace memory that held it. So, the key material exists in the keystore daemon's memory for a fraction of a millisecond. Again, if the device has Keymaster version 4+ it is only unwrapped inside the secure hardware and never leaves.