Secure Enclave Cryptokit

I am using the CryptoKit SecureEnclave enum to generate Secure Enclave keys. I've got a couple of questions:

  1. What is the lifetime of these keys?

When I don't store them somewhere, how does the Secure Enclave know they are gone? Do backups impact these keys? I.e. can I lose access to the key when I restore a backup?

  1. Do these keys count to the total storage capacity of the Secure Enclave?

If I recall correctly, the Secure Enclave has a limited storage capacity. Do the SecureEnclave key instances count towards this storage capacity?

  1. What is the dataRepresentation and how can I use this?

I'd like to store the Secure Enclave (preferably not in the Keychain due to its limitations). Is it "okay" to store this elsewhere, for instance in a file or in the UserDefaults?

  1. Can the dataRepresentation be used in other apps?

If I had the capability of extracting the dataRepresentation as an attacker, could I then rebuild that key in my malicious app, as the key can be rebuilt with the Secure Enclave on the same device, or are there measures in place to prevent this (sandbox, bundle id, etc.)

Answered by DTS Engineer in 841302022

You’re working under a common misunderstanding [1]. SE keys are not stored in the SE. If you use the SecItem API, they end up stored in the standard keychain. If you use Apple CryptoKit, they end up stored wherever you store the data representation.

What’s special about these keys is that they are wrapped in such a way that only the SE can unwrap and work with them.

1. What is the lifetime of these keys?

As long as you keep around the key reference or its data representation.

Except that there are further limits. For example:

  • If the SE gets reset for some reason, a key it issued will no longer work.

  • When using the SecItem API, you can apply other constraints, lie tying the key to biometrics.

2. Do these keys count to the total storage capacity of the Secure Enclave?

This question doesn’t make sense given the design I described above.

3. What is the dataRepresentation and how can I use this?

It’s an opaque representation of the key. Internally this holds the key bytes, wrapped in a way that only the issuing SE can extract and work with the key.

4. Can the dataRepresentation be used in other apps?

I believe that the key is tied to your app via its App ID entitlement. I’ve never really looked into the details, but the research that I did do confirmed that there’s no supported way to serialise a key in one app and deserialise it another.

See my follow-up post below.

Share and Enjoy

Quinn “The Eskimo!” @ Developer Technical Support @ Apple
let myEmail = "eskimo" + "1" + "@" + "apple.com"

[1] This partially Apple’s fault. Our Protecting keys with the Secure Enclave doc used to be called something like ‘storing keys in the SE’, and that’s probably what got this meme going in the first place.

You’re working under a common misunderstanding [1]. SE keys are not stored in the SE. If you use the SecItem API, they end up stored in the standard keychain. If you use Apple CryptoKit, they end up stored wherever you store the data representation.

What’s special about these keys is that they are wrapped in such a way that only the SE can unwrap and work with them.

1. What is the lifetime of these keys?

As long as you keep around the key reference or its data representation.

Except that there are further limits. For example:

  • If the SE gets reset for some reason, a key it issued will no longer work.

  • When using the SecItem API, you can apply other constraints, lie tying the key to biometrics.

2. Do these keys count to the total storage capacity of the Secure Enclave?

This question doesn’t make sense given the design I described above.

3. What is the dataRepresentation and how can I use this?

It’s an opaque representation of the key. Internally this holds the key bytes, wrapped in a way that only the issuing SE can extract and work with the key.

4. Can the dataRepresentation be used in other apps?

I believe that the key is tied to your app via its App ID entitlement. I’ve never really looked into the details, but the research that I did do confirmed that there’s no supported way to serialise a key in one app and deserialise it another.

See my follow-up post below.

Share and Enjoy

Quinn “The Eskimo!” @ Developer Technical Support @ Apple
let myEmail = "eskimo" + "1" + "@" + "apple.com"

[1] This partially Apple’s fault. Our Protecting keys with the Secure Enclave doc used to be called something like ‘storing keys in the SE’, and that’s probably what got this meme going in the first place.

It’s an opaque representation of the key. Internally this holds the key bytes, wrapped in a way that only the issuing SE can extract and work with the key.

Together with the potential fact that they're also tied to App ID entitlements, does this mean I can safely store this data representation in a file on disk or in the UserDefaults, or in an SQLite database of some sorts? I'm really curious, as the Keychain has several drawbacks to using it.

It depends on what you mean by “safely”:

  • If you’re asking whether that’ll work then, yeah, it’ll work just fine.

  • If you’re asking whether it’s secure then… well… I’m not really in a position to do a security audit of your product. My general advice is that you store secrets in the keychain. And, indeed, this is exactly what we show in Storing CryptoKit Keys in the Keychain

Share and Enjoy

Quinn “The Eskimo!” @ Developer Technical Support @ Apple
let myEmail = "eskimo" + "1" + "@" + "apple.com"

Hi!

I believe that the key is tied to your app via its App ID entitlement. I’ve never really looked into the details, but the research that I did do confirmed that there’s no supported way to serialise a key in one app and deserialise it another.

From my testing, that does not appear to be the case for keys created with:

let privateKey = try SecureEnclave.P256.KeyAgreement.PrivateKey(/*default params*/)

Having "dataRepresentation" of the privateKey another process can use it for sign/decrypt — even when that second process is signed with a different Team ID, or is unsigned entirely. The process that originally creates the key is properly signed with my Team ID and has the keychain-access-groups entitlement. SIP is also enabled. Based on these experiments, it seems that SecureEnclave keys are not intrinsically bound to the originating app or signing identity, and isolation from other apps may be enforced primarily through Keychain access controls.

that does not appear to be the case

Agreed.

Since I posted that my understanding of how this stuff works has shifted, meaning that I no longer believe that the key is tied to your App ID. When you think about this it kinda makes sense. When you store a SE-protected key in the keychain, you have the option to put it in a keychain access group that’s accessible by multiple apps. If the key bytes were tied to your App ID, those other apps wouldn’t be able to use the key.

So, I repeated you tests here in my office, and I see the same results. I’m able to successfully pass an SE-protected key between two apps (running on iOS 26.4.2).

Pasted in below are the code snippets I used for this.

Thanks for setting things straight here!

Share and Enjoy

Quinn “The Eskimo!” @ Developer Technical Support @ Apple
let myEmail = "eskimo" + "1" + "@" + "apple.com"


For the source app:

func generateAndSaveKey() throws {
    let ud = UserDefaults(suiteName: "group.eskimo1.test")!

    let privateKey = try SecureEnclave.P256.Signing.PrivateKey()
    let privateKeyBytes = privateKey.dataRepresentation
    ud.set(privateKeyBytes, forKey: "privateKeyBytes")

    let message = Data("Hello Cruel World! \(Date.now)".utf8)
    ud.set(message, forKey: "message")

    let signature = try privateKey.signature(for: message)
    let signatureBytes = signature.derRepresentation
    ud.set(signatureBytes, forKey: "signatureBytes")
}

For the destination app:

func useSavedKey() throws {
    let ud = UserDefaults(suiteName: "group.eskimo1.test")!

    let privateKeyBytes = try secCall { ud.data(forKey: "privateKeyBytes") }
    let privateKey = try SecureEnclave.P256.Signing.PrivateKey(dataRepresentation: privateKeyBytes)

    let message = try secCall { ud.data(forKey: "message") }

    let signatureBytes = try secCall { ud.data(forKey: "signatureBytes") }
    let signature = try P256.Signing.ECDSASignature(derRepresentation: signatureBytes)
    
    let publicKey = privateKey.publicKey
    let isValid = publicKey.isValidSignature(signature, for: message)
    if isValid {
        print("Success!")
    } else {
        print("Failed to verify signature.")
    }
}

This uses the secCall(…) helpers from this thread.

Secure Enclave Cryptokit
 
 
Q