Differences Between AES Key Generation on Android and iOS

Second in a series comparing the cryptographic programming interfaces of Android and iOS.

Jim Hawkins
VMware 360
10 min readNov 16, 2020

--

The first in the series was about RSA key generation.

The Android and iOS programming interfaces both support generation and storage of symmetric keys for AES (Advanced Encryption Standard) cryptography. The programming interfaces can be utilized in Kotlin on Android, or in Swift on iOS. This post includes sample code and discussion of the platforms’ programming interfaces, separately and comparatively.

Series Disclaimer: IANAC

Before you copy and paste the code into your banking app, be warned that I Am Not A Cryptographer (IANAC).

My understanding of cryptography is based on some reading, some coding, and on years of standing next to security engineers who themselves stand next to cryptographers.

In case you understand even less than me about cryptography, a few terms and definitions are included after the code and discussions.

Generate an AES Key on Android

Kotlin code for Android to generate a random symmetric key for AES cryptography in the Android key store could look like this:

The above snippet is based on the code here:
github.com/vmware/captivewebview/…/captivecrypto/StoredKey.kt#L284
The code is compatible with Android version 6, aka Marshmallow, which corresponds to a minimum API level of 23.

Line notes:
(These are almost the same as the notes on the RSA key generation sample in Kotlin in the previous post.)

  • Line 5:
    The constant “AndroidKeyStore” specifies that the key is to be generated in the hardware-backed key store, if one is present on the device.
  • Lines 6 and 10
    For details of .apply(), .run(), and other Kotlin scope functions, see kotlinlang.org/…/scope-functions
  • Line 7:
    The KeyGenParameterSpec class must be used to specify keys generated in the Android Keystore. The class is part of the Android Keystore package. It is a specific subclass of a base class in the Java Security package. This is a common pattern in the Android cryptography programming interface.
  • Line 9
    The KeyProperties constants here form a bit mask.
  • Lines 11 to 13
    The key size, block mode, and padding mode are part of the key specification. Cipher specification will come later, when the key is used to encrypt and decrypt data. However, only modes that were in the key specification can be used in the cipher specification. The particular combination here may not be suitable for your application IANAC but this code works and can be adapted to your needs. If you want to try different combinations of modes, it’s a good idea to check what combination Android supports first. A method for doing that will be covered later in this series.
  • Line 18
    This is the actual generation and storage of the key.

The stored key can now be used for other tasks like encryption and decryption.

Generate an AES Key on iOS

Swift code for iOS to generate a random symmetric key for AES cryptography and store it in the iOS keychain could look like this:

The above snippet is based on the code here:
github.com/vmware/captive-web-view/…/CaptiveCrypto/StoredKey.swift#L449
The code is compatible with version 10.0+ of iOS.

Line notes:

  • Line 4:
    First, any generic key chain item with the same identification will be deleted. If this isn’t done then the addition will fail as a duplicate.
  • Lines 7 and 8:
    Generic password items in the keychain are identified by these two attributes, kSecAttrAccount and kSecAttrService. The code here sets values that are based on the key name, and that will be easy to identify if seen in other contexts, like an attribute dump.
  • Line 13:
    The StoredKeyError class is a custom exception subclass with convenience initialisers. The code is here: github.com/vmware/captive-web-view/…/CaptiveCrypto/StoredKey.swift#L735. You have to make your own custom exceptions in Swift. See under Some Assembly Required, below, for more on that.
  • Line 17:
    The key is generated in memory. This is a CryptoKit class, not part of the keychain interface.
  • Line 20:
    Adding kSecReturnAttributes means that the keychain operation will return a CFDictionary of the effective attributes. That can be handy if you want to see what default attributes were added by the system.
  • Line 21:
    The rawRepresentation property is an extension. The code is here:
    github.com/vmware/captive-web-view/…/CaptiveCrypto/StoredKey.swift#L22
  • Line 25:
    The key is stored in the keychain. The result variable is populated with a CFDictionary of attributes, on success.

The key has now been stored and could be retrieved to be used for other tasks like encryption and decryption.

Comparison of AES Key Generation Programming Interfaces

The above Android and iOS code snippets for AES key pair generation are quite different to each other because the native programming interfaces that they utilize are quite different.

Android Cryptography Programming Interface

The same programming interface is used for AES and RSA key generation on Android. It’s nice to have only a single interface to learn for cryptography. It’d be nicer if the interface was nicer though.

The comments in the previous post about the Android programming interface for RSA key pair generation apply equally to AES key generation. Here they are, for convenience.

Java Security System
The Android cryptography programming interface is based on the Java Security system. The Java Security system has a broad scope.

A smart card that can respond to challenges can be coded in the Java Security system. So can a derived credentials system. So can a hardware security module in an Android smartphone. So can a certification authority service. The Java Security programming interface has to be quite extensive in order to model all the applications that it supports.

Two significant pieces are the concepts of the key store and the security provider. The Android Keystore counts as both a store and a provider.

Pitfalls for Android
Although the Android RSA key generation code looks quite simple and robust, many pitfalls had to be fallen into, and climbed out of, in the process of writing it. For example:

You have to know that “AndroidKeyStore” is the magic value that selects the on-device hardware-backed storage of an Android device. Specifying the store explicitly is optional. If you don’t specify it, the Java Security system will select a store based on the key specification. It won’t necessarily select the Android Keystore because an Android device can have several stores, including stores that only exist in-memory at run time.

You have to know to use the KeyGenParameterSpec class. The KeyPairGenerator will accept any parameter that implements the AlgorithmParameterSpec interface, of which there are quite a few. The generator can also be initialized directly just by specifying a key length, without a specification object. However, you must use KeyGenParameterSpec or else your key won’t be stored in the Android Keystore.

Even though it’s a dedicated subclass for specifying keys in the Android Keystore, the KeyGenParameterSpec class doesn’t prevent you from making mistakes. For example, it’s quite possible to specify a combination of modes that aren’t supported by any security provider on the device. Sometimes you won’t find out about that until the key generation code runs and throws an exception. Sometimes you won’t find out until later, like cipher preparation time when you try to attach the key. Or even later, after you’ve encrypted some data and then can’t decrypt it.

The Android code is at least object oriented and works well with Kotlin scope functions and other modern concepts.

iOS Keychain Programming Interface

The iOS keychain has different capabilities for AES and RSA keys.

  • RSA keys are generated in the keychain and stored there natively, as keys.
  • AES keys are generated outside the keychain, then encoded and stored as generic passwords.

So, for the storage portion of AES key generation, the keychain programming interface is used. The comments in the previous post about the iOS keychain programming interface for RSA key pair generation apply equally to the keychain as it is used with AES keys. Here they are, for convenience.

iOS Keychain Programming Interface
The RSA key generation programming interface of iOS isn’t modern. Old-time Objective-C coders will be familiar with the kSec constants and the rest of the SecKey family. The programming interface doesn’t seem to have been updated to make use of new Swift syntax.

The shorter dot notation for Swift enum constants can’t be used in a SecKey attributes dictionary, for example. The SecKey interface isn’t really based on objects and classes. Basically, it’s C code and a module map. Where Android has a class the represents the key specification, iOS uses a generic dictionary object that happens to have some particular values in the keys.

However, in the centre of the AES key generation code is usage of a different iOS programming interface, CryptoKit.

CryptoKit

The CryptoKit interface has been used in this sample because the keychain interface doesn’t support explicit symmetric key generation. (Symmetric keys are generated implicitly in some cases, but they aren’t accessible to the app.)

CryptoKit has a proper modern Swift interface, based on classes and in which, for example, the shorter dot notation can be used for enumerated constants.

The CryptoKit interface is good as far as it goes. For example, to generate a symmetric key, you need only instantiate a SymmetricKey object, in a single call with well-named parameters. You don’t need to specify that the key will be used for AES encryption.

One thing it’s missing, compared to the Android equivalent, is namespace.

No Namespace

Java and Kotlin have a namespace and package structure. The coders of the Java Security system and the Android Keystore have made use of them to give their programming interface a proper modular structure. The coders of CryptoKit couldn’t do that because Swift doesn’t have namespaces.

As a stand-in for namespaces, Cryptokit has enum classes with associated data. The interface, and the reference documentation, consequently ends up with a structure that appears bizarre. At the top are a number of enum classes. If you go into them, you’ll find they have further nested enum classes, and under one or two levels, the actual functional struct and class declarations.

Some Assembly Required

You have to extend CrpytoKit yourself if you want to store its keys in the iOS keychain. Apple provides some sample code for that, in a playground that can be downloaded from their developer site here: Storing CryptoKit Keys in the Keychain.

You have to create your own custom exception subclass in Swift, if you want a throw-able that has a message that can be retrieved by a catcher. You can’t instantiate Error, for example, because it’s abstract. If you don’t believe that, and I didn’t, open the playground linked above and look for KeyStoreError. Even developers at Apple had to create their own exception subclass.

Conclusion: Still Difficult

The CryptoKit programming interface makes AES key generation less difficult than RSA key generation, on iOS.

However, both the Android and iOS programming interfaces for AES key generation still seem more difficult to use than they need to be. Same as the RSA interfaces, they either offer too wide an array of options, and don’t trap errors at build time, or they use old-fashioned coding styles, and again don’t trap errors at build time.

Some Cryptographic Terms

You can make better use of the interfaces on either platform if you know a few cryptographic terms. The following definitions are a start but don’t claim to be rigorous IANAC.

(The terms and definitions here are substantially the same as in the previous post.)

Symmetric and Asymmetric Encryption

Symmetric encryption utilizes the same key to encrypt and decrypt data. The key can be referred to as a symmetric key.

Asymmetric encryption utilizes two dependent keys, sometimes referred to as a key pair. The two keys in a key pair can be referred to as the private key and the public key. If data is encrypted by the private key it can be decrypted by the public key, for example.

Cipher

A cipher is a process for a cryptographic operation, such as encrypting data.

Ciphers don’t feature as principals in the AES key generation code snippets, above, but will make an appearance in a future post in this series. However, cipher specifications are mentioned, because there is a need for compatibility with key specifications. A cipher specification can include an algorithm, a block mode, and a padding mode, for example.

Key Store

Any persistent location for cryptographic keys on a mobile device can be referred to as a key store. Persistence here means through termination of the app or power cycling of the mobile device, for example.

Stored keys each have a key identification. Depending on the capabilities of the store, the identification will be used:

  • To retrieve the key from the store.
  • To request cryptographic processing involving the key and taking place inside the store.

Key stores that support processing requests typically also support generation of new keys inside the store. A key generated inside a store that supports processing requests never has to leave the store, which is a desirable security characteristic.

The AES key generation snippets cover the following key stores.

Android Keystore:

  • Supports processing requests.
  • Identifies each key by a simple name, referred to as its alias.

iOS keychain:

  • Doesn’t support processing requests.
  • Identifies each key by a set of attributes.

A future post could also cover the iOS Secure Enclave Processor (SEP), which does support processing requests.

Sample Code Locations

Many individually useful code samples can be found here on Medium, and on Stack Overflow, and in GitHub Gists.

For the sample code that appears above, see the following.

For Android:

For iOS:

Use the sample code with care. It is working code but might require modification in order to represent the best cryptography for your application.

The above applications each use an embedded web view for their user interface (UI). The actions that the user can take in the UI correspond closely to low-level cryptographic tasks. Commands are sent from the web view through a bridging layer to the native Kotlin or Swift code. The native code then utilizes the device’s cryptographic programming interface.

For official samples, see the following.

Next: Short Text Encryption

The next post in this series will cover encryption of a short text with a stored key, either RSA or AES.

Thank you for reading and happy coding

--

--

Jim Hawkins
VMware 360

Software developer working on secure mobilisation of enterprise data to Android and iOS devices.