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