Hi
Using the newer methods in the SDK for iOS 10, I can generate private and public keys, with the private key residing in the Keychain. The public key is exported.
Here's my code thus far:
func createKeyPair(_ keyName: String, authenticationRequired: SecAccessControlCreateFlags? = nil, completion: (_ success: Bool, _ publicKeyData: Data?) -> Void)
{
guard !keyName.isEmpty else
{
NSLog("\tNo keyname provided.")
return completion(false, nil)
}
var error: Unmanaged<CFError>?
// Private key parameters
var privateKeyParams: [String: Any] = [
kSecAttrIsPermanent as String: true,
kSecAttrApplicationTag as String: keyName
]
// If we are using a biometric sensor to access the key, we need to create an SecAccessControl instance.
if authenticationRequired != nil
{
guard let accessControl = SecAccessControlCreateWithFlags(kCFAllocatorDefault, kSecAttrAccessibleWhenUnlockedThisDeviceOnly, authenticationRequired!, &error) else
{
NSLog("\tError: %@", error!.takeRetainedValue().localizedDescription)
completion(false, nil)
return
}
privateKeyParams[kSecAttrAccessControl as String] = accessControl
}
/
let parameters: [String: Any] = [
kSecAttrKeyType as String: kSecAttrKeyTypeRSA,
kSecAttrKeySizeInBits as String: 2048,
kSecPrivateKeyAttrs as String: privateKeyParams
]
// Global parameters for our key generation
guard let privateKey = SecKeyCreateRandomKey(parameters as CFDictionary, &error) else
{
NSLog("\tError generating keypair. %@", "\(error!.takeRetainedValue().localizedDescription)")
return completion(false, nil)
}
// Generate the keys.
guard let publicKey = SecKeyCopyPublicKey(privateKey) else
{
NSLog("\tError obtaining public key.")
return completion(false, nil)
}
// Get the public key.
guard let privateKeyData = SecKeyCopyExternalRepresentation(privateKey, nil) else
{
NSLog("\tError obtaining export of private key.")
return completion(false, nil)
}
print("\nPrivate key: \(String(describing: exportPublicKey(privateKeyData as Data)))")
// Extract the public key for export.
guard let publicKeyData = SecKeyCopyExternalRepresentation(publicKey, nil) else
{
NSLog("\tError obtaining export of public key.")
return completion(false, nil)
}
completion(true, publicKeyData as Data)
}
public func exportPublicKey(_ rawPublicKeyBytes: Data, base64EncodingOptions: Data.Base64EncodingOptions = []) -> String?
{
return rawPublicKeyBytes.base64EncodedString(options: base64EncodingOptions)
}
// Call the function like so.
_ = createKeyPair(keyName)
{
(status, data) in
if status
{
print("exporting public key: \(String(describing: exportPublicKey(data!)))")
}
}
If I've understood the documentation, SecKeyCopyExternalRepresentation says that the method returns data in the PCKS #1 format for an RSA key. From the various forums, I'm lead to beleive that simply base64 encoding to a string the output of SecKeyCopyExternalRepresentation is all that is required to export the public key in PEM format (without BEGIN RSA PUBLIC KEY and END RSA PUBLIC KEY)
When the public key is used to validate some signed data in a Java app, the public key fails to load with invalid key errors... Anyone provide some guidance on this?
Thanks
Simply base64 encoding the SecKey is not enough, it's missing from what I understand is the RSA OID header and the ASN.1 sequence.
Right. I’ve talked about this extensively in other threads (related to public keys, not private keys, but similar logic applies). For example:
In many cases you can convince the ‘foreign’ security toolkit to accept the unwrapped structure by using
BEGIN RSA PUBLIC KEY
markers in the PEM (as opposed to
BEGIN PUBLIC KEY
, which is used for the wrapped version).
Do you know of any functions in the iOS SDK that do this?
No. For simple RSA cases it’s relatively easy to do this wrapping by hand. However, my recommendation is that you look at doing this on the server side; it’s likely that the security toolkit there is more fully-featured, and thus can handle this job.
Share and Enjoy
—
Quinn “The Eskimo!”
Apple Developer Relations, Developer Technical Support, Core OS/Hardware
let myEmail = "eskimo" + "1" + "@apple.com"