Open rolfbjarne opened 3 months ago
Tagging subscribers to this area: @dotnet/area-system-security, @bartonjs, @vcsjones See info in area-owners.md if you want to be subscribed.
/fyi @simonrozsival
That's a known platform limitation. iOS Keychain doesn't allow exporting keys.
while not passing this flag does not prevent exporting the certificate on Mac Catalyst.
That's the tricky part. It's only exportable for the in-memory copy until you store it in the keychain.
That's a known platform limitation. iOS Keychain doesn't allow exporting keys.
while not passing this flag does not prevent exporting the certificate on Mac Catalyst.
That's the tricky part. It's only exportable for the in-memory copy until you store it in the keychain.
Maybe it would make more sense to throw when trying to store an exportable certificate in the keychain (as opposed to when creating the certificate)?
Maybe it would make more sense to throw when trying to store an exportable certificate in the keychain (as opposed to when creating the certificate)?
The design of X509KeyStorageFlags
is not great. Note that the enum itself has Storage
in the name and there's a reason for that. Other flags like X509KeyStorageFlags.PersistKeySet
cause the keys to be imported in the new X509Certificate
constructor (on Windows or whereever the flag is supported).
Various combinations of the flags result in various behaviors that are not consistently supported across platforms. This is especially noticeable when comparing iOS/MacCatalyst and macOS which use entirely different implementation on the OS level. The traditional macOS Keychain has more than one store and keys can be exportable. On the other hand, many APIs don't support ephemeral keys or certificates and so they are emulated to some extent by creating temporary keychains (hence, #102599). The iOS APIs, on the other hand, expose a totally different per-app keychain that doesn't have exportable keys and has single storage container (well, two, if you include the iCloud keychain which is not exposed by .NET API). Unlike the macOS API there is concept of ephemeral certificates and ephemeral keys stored in memory.
The discrepancy you see with the keys being exportable in the in-memory iOS/macOS implementation is a technical detail that should not be depended upon. To import PFX certificates .NET uses a parser written in managed code (since the OS API have too many quirks and limitations, especially around more complex container layouts and empty/no password). This parser creates two separate Cocoa objects, one for the certificate (SecCertificate
) and another for the private keys (SecKey
). They are not merged together until you store them in the keychain since the required API to create (UPD: The description is not entirely accurate. I was reminded that we actually ended up producing a simplified PFX and importing that with SecIdentity
is private. The SecKey
itself is exportable at this point in time. This is, however, specific to the PFX importer and should not be depended on.SecPKCS12Import
. Posting it here for posterity to avoid misleading someone in future.)
Description
Can't create X509Certificate using X509KeyStorageFlags.Exportable on Mac Catalyst or iOS
Note that the code works fine on macOS (
TargetFramework=net8.0-macos
, or just a plain console app).Also note that not passing this flag prevents the certificate from being exported later on macOS (#102599), while not passing this flag does not prevent exporting the certificate on Mac Catalyst.
In other words: in order to export the certificate later, the
X509KeyStorageFlags.Exportable
flag can not be specified on iOS and Mac Catalyst, but must be specified on macOS.This doesn't make much sense...
Reproduction Steps
Expected behavior
Actual behavior
Regression?
Probably not
Known Workarounds
Don't pass
X509KeyStorageFlags.Exportable
to the constructor.Configuration
Other information
No response