Create an external representation of a key for transmission.
Framework
- Security
Overview
If you have a key that you want to send somewhere, you package it in a form that’s suitable for transmission.
Export the Key as a Data Instance
Create a data instance representing a key using the Sec
function:
let key = <# a key #>
var error: Unmanaged<CFError>?
guard let data = SecKeyCopyExternalRepresentation(key, &error) as Data else {
throw error!.takeRetainedValue() as Error
}
The data instance returned from this function can then be sent across a network, through the file system, or by any other means, so long as the data’s content is preserved exactly.
This method works for both public and private keys. However, it doesn’t work for all keys. For example, if a key is bound to a smart card or to the Secure Enclave, you can’t export it this way. Also, in macOS, a key that has the k
attribute set to false is ineligible for export. In these cases, or if any other error occurs, the returned data is nil
and the error object indicates the reason for failure.
When an error does occur, you become responsible for the error object’s memory. In Objective-C, you transfer ownership to Automatic Reference Counting (ARC) using a call to CFBridging
. In Swift, you convert the unmanaged CFError
object to a managed Error
using take
and a cast.
The format of the returned data depends on the kind of key you are exporting:
For an RSA key, the function returns data in the PKCS #1 format.
For an elliptic curve public key, the format follows the ANSI X9.63 standard using a byte string of
04 || X || Y
.For an elliptic curve private key, the output is formatted as the public key concatenated with the big endian encoding of the secret scalar, or
04 || X || Y || K
.
All of these representations use constant size integers, including leading zeros as needed.
Avoid insecure distribution of cryptographic keys, and pay special attention to private keys. You can distribute a public key freely, although you do need to ensure trust—can the receiver be sure this is the public key that you sent? Trust is typically established with a signed certificate, but if you are working in a closed system, or enforce trust in some other way, you can export and send public keys as data. Use extra caution, however, when exporting private keys, because they must remain absolutely secret to be of value.
Important
If there’s even a possibility that an untrusted entity has gained access to your private key, it is compromised and you should stop using it.
Restore a Key from a Data Instance
When you’re ready to restore the data instance back into a key, use the Sec
function:
let options: [String: Any] = [kSecAttrKeyType as String: kSecAttrKeyTypeRSA,
kSecAttrKeyClass as String: kSecAttrKeyClassPublic,
kSecAttrKeySizeInBits as String : 2048]
var error: Unmanaged<CFError>?
guard let key = SecKeyCreateWithData(data as CFData,
options as CFDictionary,
&error) else {
throw error!.takeRetainedValue() as Error
}
In this case, you use an options dictionary to inform the function about certain characteristics of the key. At a minimum, you indicate the key’s type, class, and size, which means you need to communicate this information along with the key data or establish it ahead of time. If the function returns an empty key reference, check the error object for indications of failure.
In Objective-C, you release the key object’s memory after you’re done using it. You also transfer ownership of the error object, if it exists, to ARC. In Swift, the key object’s memory is already managed by the system, but you transfer ownership of the unmanaged CFError
to the system with take
and recast it as an Error
before throwing it.