Luckily SecKeyCreateWithData is available to inspect via open source releases, and your suspicion is confirmed: keyData is ignored when tokenID != NULL. Source: https://github.com/apple-oss-distributions/Security/blob/e4ea024c9bbd3bfda30ec6df270bfb4c7438d1a9/OSX/sec/Security/SecKey.m#L1352-L1358
With this in mind, the accepted reply is not actually 'exchanging' a SecureEnclave.P256.Signing.PrivateKey for a SecKey, it is creating a SecKey instance backed by a different private key, which I do not believe is intended.
I was able to use SecKeyCreateWithData to reliably recreate a SE-backed private key after suspecting the parameters arg passed to SecKeyCreateCTKKey likely contained the necessary information to reconstruct the key. I inspected the output of SecKeyCopyAttributes and found a value that looked very much like the CryptoKit dataRepresentation associated with key toid. This maps to kSecAttrTokenOID (Not exported in iOS headers for some reason), and replaces the now deprecated kSecAttrSecureEnclaveKeyBlob. The following code should satisfy the ask in the OP:
let pk = try SecureEnclave.P256.Signing.PrivateKey()
let sf = try secCall { SecKeyCreateWithData(Data.init() as NSData, [
kSecAttrKeyType: kSecAttrKeyTypeECSECPrimeRandom,
kSecAttrKeyClass: kSecAttrKeyClassPrivate,
kSecAttrTokenID: kSecAttrTokenIDSecureEnclave,
"toid": pk.dataRepresentation,
] as NSDictionary, $0) }
// verify results
print(pk.publicKey.x963Representation as NSData)
print(SecKeyCopyExternalRepresentation(SecKeyCopyPublicKey(sf)!, nil)! as NSData)
That the method must be called in this way feels like a mistake in the implementation of SecKeyCreateWithData when a tokenID is present. If I were able, I would submit a patch to use actually use the passed keyData:
From 1965be6fcf05ba02f141a20c0501cdeff0c76cd2 Mon Sep 17 00:00:00 2001
From: Judson Stephenson <Jud@users.noreply.github.com>
Date: Wed, 14 Jun 2023 00:32:30 -0500
Subject: [PATCH] Use keyData when SecKeyCreateWithData is called with a
tokenID
---
OSX/sec/Security/SecKey.m | 1 +
1 file changed, 1 insertion(+)
diff --git a/OSX/sec/Security/SecKey.m b/OSX/sec/Security/SecKey.m
index 55bbaa9e..02bdb853 100644
--- a/OSX/sec/Security/SecKey.m
+++ b/OSX/sec/Security/SecKey.m
@@ -1350,6 +1350,7 @@ SecKeyRef SecKeyCreateWithData(CFDataRef keyData, CFDictionaryRef parameters, CF
CFStringRef tokenID = CFDictionaryGetValue(parameters, kSecAttrTokenID);
if (tokenID != NULL) {
+ CFDictionarySetValue(parameters, kSecAttrTokenOID, keyData);
key = SecKeyCreateCTKKey(allocator, parameters, error);
if (key == NULL) {
os_log_debug(SECKEY_LOG, "Failed to create key for tokenID=%{public}@: %{public}@", tokenID, error ? *error : NULL);