How to persist SecureEnclave.P256.Signing.PrivateKey

I am slightly confused as to how I am supposed to maintain persistent access to a SecureEnclave.P256.Signing.PrivateKey. Do I have to persist the key myself (using its dataRepresentation property and code along the lines of Storing CryptoKit Keys in the Keychain or is there another persistent reference to the key inside the Secure Enclave that I can use later?

Replies

Do I have to persist the key myself … and code along the lines of Storing CryptoKit Keys in the Keychain

Yes.

I think you’re suffering from a common misconception about Secure Enclave keys [1]. Keys that are protected by the Secure Enclave aren’t actually stored in the Secure Enclave. See this post for an explanation of how this actually works.

Share and Enjoy

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

[1] Which is not surprising because our documentation is misleading on this subject, something I hope to get fixed (r. 83612073).

Thanks Quinn! You know, there are days where I feel I would not get anything done without you helping me out...

Does that mean the dataRepresentation of a SecureEnclave.P256.Signing.PrivateKey is already stored in the keychain by the system, but is inaccessible to client code so the only way for it to get a reference to such a key is to store the dataRepresentation itself and recreate the key from it?

And if the key had instead been created by not using CryptoKit, but the Security framework (passing .privateKeyUsage in the access control list) it could later be retrieved by SecItemCopyMatching, correct?

Does that mean the dataRepresentation of a SecureEnclave.P256.Signing.PrivateKey is already stored in the keychain

No. If you generate a new SE key using Apple CryptoKit, the key is ephemeral; it’s not stored anywhere. The dataRepresentation property returns a copy of the key itself, but wrapped in a way such that it can only be unwrapped by your SE. You can do whatever you want with that data — store it in the keychain, write it to a file, print it to punch cards — but it’s not going to do anyone any good unless they have your SE to unwrap it.

Share and Enjoy

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

Ok, I think I am getting close. So while SE keys are not actually stored inside the Secure Enclave keys generated by the Security framework are (or can be, depending on whether kSecAttrIsPermanentis set or not) stored in the keychain. Keys generated by Apple CryptoKit are not stored at all and there is no way to convert between the two kinds of keys. Correct?

Correct?

Pretty much.

SecKeyCreateRandomKey generates an ephemeral key by default. If you add kSecAttrTokenID, that ephemeral key is protected by the SE. If you specify kSecAttrIsPermanent, that key gets added to the keychain.

In Apple CryptoKit, P256.Signing.PrivateKey.init(…) creates a ephemeral key and SecureEnclave.P256.Signing.PrivateKey.init(…) creates one protected by the SE. There is no equivalent of kSecAttrIsPermanent, so you have to do that manually.

The only bit I’m not sure about is this:

there is no way to convert between the two kinds of keys.

I suspect that you may be able to do this using one of the data representations, but I’ve not investigated that in depth. Still, I don’t think that matters in practice, because you’re going to choose an API up front and stick with it.

Share and Enjoy

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

Thanks again. Do you deem it worthwile to file an enhancement request asking for API parity in this regard (i.e. by providing an initializer on SecureEnclave.P256.Signing.PrivateKeythat takes a parameter akin to kSecAttrIsPermanent)?

For the record:

I suspect that you may be able to do this using one of the data representations, but I’ve not investigated that in depth.

I tried converting a Keychain key to a CryptoKit key by utilising SecKeyCopyExternalRepresentation(_:_:), but as the documentation suggests that does not work:

The operation couldn’t be completed. (OSStatus error -4 - export not implemented for key <SecKeyRef:('com.apple.setoken') (which should be errSecUnimplemented).