Keys and secrets stored by the Secure Enclave can be created just like any other secret, using SecKeyCreateRandomKey
. The difference in implementation is to use kSecAttrTokenIDSecureEnclave
attribute. [1],[2]
In general, the private key would normally be briefly copied to memory as plain-text to perform cryptographic operations. With the use of Secure Enclave, all operations are performed inside Secure Enclave and the key would never leave it. (thus having a smaller attack surface)
In short, in order to create a pair of keys inside Secure Enclave:
- Specify access control attributes -
kSecAttrAccessibleWhenUnlockedThisDeviceOnly
allows to use the key only on the device that created it, and only when device is unlocked. The .privateKeyUsage
allows Secure Enclave to use it for signing. This flag can be combined with biometryAny
to require FaceID/TouchID authentication before the key can be used.
let access = SecAccessControlCreateWithFlags(
kCFAllocatorDefault,
kSecAttrAccessibleWhenUnlockedThisDeviceOnly,
.privateKeyUsage,
nil)! // Ignore errors.
- Now, we can create dictionary specifying key type and other attributes. With
kSecAttrKeyTypeECSECPrimeRandom
we use elliptic curve keys with 256 bits size, and kSecAttrTokenIDSecureEnclave
specifies that key is to be stored inside SecureEnclave. We also specify access control attributes that we just created, and a key name for later retrieval.
let tag = "MyNewKeyPair".data(using: .utf8)!
let attributes: NSDictionary = [
kSecAttrKeyType: kSecAttrKeyTypeECSECPrimeRandom,
kSecAttrKeySizeInBits: 256,
kSecAttrTokenID: kSecAttrTokenIDSecureEnclave,
kSecPrivateKeyAttrs: [
kSecAttrIsPermanent: true,
kSecAttrApplicationTag: tag,
kSecAttrAccessControl: access
]
]
- Finally, we can create the key and retrieve reference to it:
var error: Unmanaged<CFError>?
guard let privateKey = SecKeyCreateRandomKey(attributes, &error) else {
throw error!.takeRetainedValue() as Error
}
and use it for i.e. signing with SecKeyCreateSignature
function [3].