Migrate RSA key pair between iOS devices

I am generating a key pair using SecKeyGeneratePair() on iOS. I would like to get those keys (both public and private) onto another iOS device. I see two options for importing a public and private key on iOS.


1) Generate a PKCS12 blob, send it to the second device, and call SecPKCS12Import().

2) Use SecItemCopyMatching() to copy out the raw key data, bundle them up in my own format, then use SecItemAdd() on the second device to create a public and private key in the keychain.


The first option seems far preferable, but I can't find any way to export a PKCS12 blob. On OS X, SecItemExport() appears to support PKCS12 export, but that function does not exist on iOS.


The second option apparently doesn't work. I can't ever get `SecItemAdd()` to actually create a private RSA key object, no matter what parameters I pass in the dictionary. I always get back error -25353 ("errKCNoSuchAttr / errSecNoSuchAttr: / The attribute does not exist"). After fighting that challenge for a while, I eventually ran across this post in the old developer forums, which suggests that adding private keys via SecItemAdd() is not supported.


I'd love a third option, but I'm not seeing one. It seems like generating a PKCS12 blob is the right way to go, but as far as I can tell, the iOS system frameworks don't support that natively, and I can't see how to get enough information out of SecItemCopyMatching() to create a useful PKCS12 blob even if I pull in something like OpenSSL.


So yeah, if anyone has a way to make this work, I'd love to hear about it.

Accepted Reply

The first option seems far preferable, but I can't find any way to export a PKCS12 blob.

That’s correct; iOS has no equivalent to the OS X keychain export APIs )-: Thanks for filing an enhancement request for this.

The fundamental issue you’re going to hit here is exporting the private key. For public keys we support exporting the key so that folks can implement a certificate signing request workflow (that is, generate a key pair, export the public key, give that to a CA, have them generate a certificate, import the certificate). The mechanism is poorly documented and has some un-fun gotchas, but we do support it.

For private keys that’s not the case. If you call

SecItemCopyMatching
to get the data (
kSecReturnData
) of a private key, you actually get back key data but we don’t document the format of that data for third-party use. If we did, you could get that data, write your own code to wrap it into a PKCS#12, and proceed from there.

There’s a good reason for this restriction, and it’s related to the accessibility of private key material. On OS X we ensure that private key material never leaves the security daemon except via the various export APIs. On iOS that’s not the case by default, although it’s easy to imagine a future where it were.

IMPORTANT If you generate your key pair such that the private key is stored on the Secure Enclave, iOS behaves much like OS X in this regard. The future is already here!

Which brings us back to your high-level goal. To start, I have to ask, why are you trying to export the private key? It’s seems a bit strange to generate the key pair on one device and then send both parts to another device. Why not simply generate the key pair on the other device? Or generate two key pairs, one at each end, at which point you would only need to export the public key.

Regardless, if you stick with this design then your best option would be to write (or acquire) your own crypto library. You’ll probably need this library anyway (for the PKCS#12 generation), so you might as well use it to generate your key pair.

Share and Enjoy

Quinn “The Eskimo!”
Apple Developer Relations, Developer Technical Support, Core OS/Hardware

let myEmail = "eskimo" + "1" + "@apple.com"

Replies

I've also filed rdar://25151530 regarding a request for something like SecPKCS12Export().

The first option seems far preferable, but I can't find any way to export a PKCS12 blob.

That’s correct; iOS has no equivalent to the OS X keychain export APIs )-: Thanks for filing an enhancement request for this.

The fundamental issue you’re going to hit here is exporting the private key. For public keys we support exporting the key so that folks can implement a certificate signing request workflow (that is, generate a key pair, export the public key, give that to a CA, have them generate a certificate, import the certificate). The mechanism is poorly documented and has some un-fun gotchas, but we do support it.

For private keys that’s not the case. If you call

SecItemCopyMatching
to get the data (
kSecReturnData
) of a private key, you actually get back key data but we don’t document the format of that data for third-party use. If we did, you could get that data, write your own code to wrap it into a PKCS#12, and proceed from there.

There’s a good reason for this restriction, and it’s related to the accessibility of private key material. On OS X we ensure that private key material never leaves the security daemon except via the various export APIs. On iOS that’s not the case by default, although it’s easy to imagine a future where it were.

IMPORTANT If you generate your key pair such that the private key is stored on the Secure Enclave, iOS behaves much like OS X in this regard. The future is already here!

Which brings us back to your high-level goal. To start, I have to ask, why are you trying to export the private key? It’s seems a bit strange to generate the key pair on one device and then send both parts to another device. Why not simply generate the key pair on the other device? Or generate two key pairs, one at each end, at which point you would only need to export the public key.

Regardless, if you stick with this design then your best option would be to write (or acquire) your own crypto library. You’ll probably need this library anyway (for the PKCS#12 generation), so you might as well use it to generate your key pair.

Share and Enjoy

Quinn “The Eskimo!”
Apple Developer Relations, Developer Technical Support, Core OS/Hardware

let myEmail = "eskimo" + "1" + "@apple.com"

Thanks for the response! Our goal is to create a shared identity representing a "user" between all devices. Our server will have the public key, but only the client devices will have the private key. This lets any user device encrypt data intended for the user's other devices, pass it to our servers, and still guarantee that our server cannot access that data.


I know "identities" usually come with a certificate attached, and I'm not opposed to having one if needed, but right now we don't have a particular use for it, so I figured I'd avoid it. I just need to be able to encrypt and decrypt on any device the user grants access, which means that all of those devices need access to both the public and private keys.


So yeah, it looks like I'm stuck using a third-party library for both keygen and PKCS #12 export. At that point I can still store the keys in the keychain, but I'll have to keep the PKCS #12 data around anyway in order to be able to grant access to other devices in the future, which means that storing it in the keychain isn't actually gaining me much. Too bad; I'd like to keep this data in the keychain, but it seems that won't really work.

Our goal is to create a shared identity representing a "user" between all devices.

Do you have control over the servers? If so, you could change your schema to store multiple public keys for any given user, one for each device. That would work well with the current Security framework capabilities and would improve security (because the private keys would never leave the user’s device).

At that point I can still store the keys in the keychain, but I'll have to keep the PKCS #12 data around anyway in order to be able to grant access to other devices in the future, which means that storing it in the keychain isn't actually gaining me much.

You can still use the keychain to improve security here, by using it to store the symmetric key used to secure the PKCS#12 blob.

Share and Enjoy

Quinn “The Eskimo!”
Apple Developer Relations, Developer Technical Support, Core OS/Hardware

let myEmail = "eskimo" + "1" + "@apple.com"